In Part 1 of this series we developed an API for searching and sorting HTML tables. We introduced some code reuse in Part 2 but ended up with a lot of boilerplate and fell short in our efforts to address the problem. In this post we discover a feature of TypeScript that offers hope.


Some days later Patrick says:

“Hey, Gordon, I found something. Check this out.”

What’s this? What does K extends keyof T mean1?

As in JavaScript, TypeScript allows objects to be treated as an associative array where a field value can be accessed like this

But in TypeScript, while firstName is a string, it is also a type. Because Person has a field called firstName, the string firstName matches a type called keyof Person. If you inspect keyof Person you get a union type consisting of all field names in Person.

Now we can write code that feels just like JavaScript, but the compiler will actually detect spelling errors:

Yields a compile error:

How great is that? Sold! Now we can update the TableSort API

Recalling the search API, while we’re at it we can lose the FieldExtractor and write a new predicate leveraging thekeyof T type

Then users can write code like this to set up the table

And an IDE can offer code completion

Whoops. Code completion just ruined our day.

Holey API!

The astute reader will recognize that age is a number field, yet code completion offers it as an option where a string field is expected. We want this for textPredicate; all fields values will be converted to strings for the text search. But tableSort.addString() should clearly reject the addition of age to its list.

To illustrate the problem, consider how these types resolve

And this code

This definition permits calling addString('age') but it *should* be an error because Person['age'] is a number. We need to distinguish between string fields and number fields with something like

Two types for which we can write two type-safe functions. One could imagine these additions to the TableSort API:

Surely that’s too much to ask of the type system. On the other hand, the utility of keyof seems starkly limited if these additional constraints can’t be applied. What does TypeScript have to say about all this? Find out in the final post of this series.