TypeScript Lookup Types: type-safe properties

Posted on Apr 11, 2017

Wait, a video explaining Lookup Types? Yes! Check it out on Egghead.io!

That's something I was wondering for a while. I read the official docs and Marius Schulz post where quite well explain it, but didn't totally get the use of it. I needed to come across a real world case where I had to use it.

Then I made a PR to Jest on DefinitelyTyped repository for adding the spyOn function introduced in Jest 19. That's when I finally understood it.

What exactly are Lookup types?

Basically a lookup type defines an indexed property type of another type. They are created using the keyof operator, which returns an union of string literals:

// Given a Bike type
interface Bike {
  model: string;
  weight: number;
  ride: Function;
}

// Get all prop names of Bike
type BikePropNames = keyof Bike; // "model" | "weight" | "ride"

// We can get the all prop types of Bike as well
type BikePropTypes = Bike[BikePropNames]; // "string" | "number" | "Function"

Typescript infers the string literals by looking up on the types used, either for the keyof operator and element access.

OK... But when can this be useful?

Let's take the jest.spyOn function, as an example. The function works takes an object as a first parameter, and the method you wanna spy on as a second parameter. I've first wrote it like this:

function spyOn(object: any, method: string);

Yes, this would work. But what if, given the Bike example, I use a non-existent method as a second parameter?

const bike: Bike = {
  model: "Orbea X5",
  weigth: 10,
  ride: () => console.log("riding!!")
};

// No TS error, but it would fail, since 'blabla' is not a method of bike
spyOn(bike, "blabla");

This is not type-safe, ts will not complain at all. How can we do this type-safe?

function spyOn<O, M extends keyof O>(object: O, method: M)

...

spyOn(bike, 'blabla') // now TS throws an error :)
spyOn(bike, 'ride') // This works

If you still don't understand the spyOn declaration, basically is saying:

  • <O, M extends keyof O>: O is any object, and M is a property of O
  • object: O, method: M: we expect O (any object) as a first parameter, and M (a property of O as a second)

Do you see now the power of lookup types? You can dynamically generate string literal union types! That'll make your type definitions much more accurate 😉

Alex Jover

ALEX JOVER

Google Dev Expert, Vue team member, cats and dance lover. Find here all my teachings about web development, web performance and more.

Alex Jover Morales © 2018