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
jsdata/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
tsTryexport 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".
tsTryimport {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 
fetchRecordsfunction that fetches an array of records (of a single type) - ensure that when records of type 
bookare 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.
tsTryimport {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!