In TypeScript Fundamentals v4: Type queries we studied a use case around building a data layer. We’ve covered a couple of concepts in Intermediate TypeScript v2 that will come in handy! Let’s pick up where we left off and finish the job.
Let’s set the stage again.
Imagine we’re building a data library for a web applications. Part of this task involves building a function that fetches different types of records from a user’s API. We want to be able to retrieve a record by the name of the kind of record and its ID, but as the builders of the library, we don’t know the specific types that any given user will need.
ts
// Assumption -- our user has set up resources like Book and Magazine//// returns a BookfetchRecord("book", "bk_123")// returns a MagazinefetchRecord("magazine", "mz_456")// maybe should refuse to compilefetchRecord("blah", "")
Our project (in our course notes) has a file structure like
js
data/book.ts // A model for Book recordsmagazine.ts // A model for Magazine recordslib/registry.ts // Our type registry, and a `fetchRecord` functionindex.ts // Entry point
You can find it in packages/intermediate-ts-v2/src/10-type-registry-revisited
.
The lib/registry.ts
file looks like
tsTry
export interfaceDataTypeRegistry {// empty by design}// the "& string" is just a trick to get// a nicer tooltip to show you in the next stepexport functionfetchRecord (arg : keyofDataTypeRegistry & string,id : string,) {}
You may recall that our book.ts
and magazine.ts
files look like this
tsTry
// @filename: data/book.tsexport classBook {deweyDecimalNumber (): number {return 42}}declare module "../lib/registry" {export interfaceDataTypeRegistry {book :Book }}// @filename: data/magazine.tsexport classMagazine {issueNumber (): number {return 42}}declare module "../lib/registry" {export interfaceDataTypeRegistry {magazine :Magazine }}
and as a result, the keyof DataTypeRegistry
type started to show "book" | "magazine"
.
tsTry
import {fetchRecord ,DataTypeRegistry } from './lib/registry'typeFoo = keyofDataTypeRegistry & string
Now our task is to use a mapped type, to make fetchRecord
return a Promise
that resolves to the correct type. While we’re at it, let’s
- make a
fetchRecords
function that fetches an array of records (of a single type) - ensure that when records of type
book
are fetched, their IDs begin with"book_"
(and"magazine_"
for records of typemagazine
)
The desired outcome is to get this piece of code to type-check.
tsTry
import {fetchRecord ,fetchRecords } from './lib/registry'async functionmain () {constmyBook = awaitfetchRecord ("book", "book_123")constmyMagazine = awaitfetchRecord ("magazine", "magazine_123")constmyBookList = awaitfetchRecords ("book", ["book_123"])constmyMagazineList = awaitfetchRecords ("magazine", ["magazine_123"])//@ts-expect-errorfetchRecord ("book", "booooook_123")//@ts-expect-errorfetchRecord ("magazine", "mag_123")//@ts-expect-errorfetchRecords ("book", ["booooook_123"])//@ts-expect-errorfetchRecords ("magazine", ["mag_123"])}
A starting point, containing this code (which will fail to compile once we uncomment it) is in notes/intermediate-ts-v2/src/10-type-registry-revisited/index.ts
. Everything we need to change (aside from the un-commenting) in order to make this work is in lib/registry.ts
.
Let’s begin!