You may now notice an type error with getPokemon
:
Type 'BuildPokeApiUrl | PokemonCollection' is not assignable to type 'never'.
That's because we manually defined the PokeApiImpl
interface, which doesn't conform anymore with the new implementation (no dependencies expected):
Every time you see a type issue in the form
Type 'Service' is not assignable to type 'never'
it probably has to do with missing/outdated dependencies (in this exampleBuildPokeApiUrl
andPokemonCollection
)
interface PokeApiImpl {
/// `getPokemon` has `never` as dependencies
/// (`never` is the default type when not defined)
///
/// But our implementation uses `BuildPokeApiUrl` and `PokemonCollection`
/// ⛔️ Not assignable to `never`!
readonly getPokemon: Effect.Effect<
Pokemon,
FetchError | JsonError | ParseResult.ParseError | ConfigError
>;
}
export class PokeApi extends Context.Tag("PokeApi")<PokeApi, PokeApiImpl>() {
Having to keep implementation and types in sync is a pain, we do not want to do this. It's faster to just derive the types from the actual implementation.
Good news! We can do this with typescript using typeof
.
We first define a make
value that contains the implementation of the service, and we provide its type definition when creating the Context
service:
const make = {
getPokemon: Effect.gen(function* () {
const pokemonCollection = yield* PokemonCollection;
const buildPokeApiUrl = yield* BuildPokeApiUrl;
const requestUrl = buildPokeApiUrl({ name: pokemonCollection[0] });
const response = yield* Effect.tryPromise({
try: () => fetch(requestUrl),
catch: () => new FetchError(),
});
if (!response.ok) {
return yield* new FetchError();
}
const json = yield* Effect.tryPromise({
try: () => response.json(),
catch: () => new JsonError(),
});
return yield* Schema.decodeUnknown(Pokemon)(json);
}),
};
export class PokeApi extends Context.Tag("PokeApi")<PokeApi, typeof make>() {
static readonly Live = PokeApi.of(make);
}
Now the service type is implementation-driven: we can go ahead and focus on the implementation (and never have to type things manually when the language supports type inference).
This
make
+typeof
pattern is common and recommended in effect.
Since we changed the service type we also need to update the
Test
implementation.We are going to fix the tests in an upcoming lesson 🔜