Evrim Persembe
Evrim Persembe

Reputation: 547

Typing the values of a static object while leaving its keys type as is

Let's say I have the following interface and object:

interface Person {
  name: string;
  lastName?: string;
}

const obj: { [key: string]: Person } = {
  a: { name: 'John' },
  b: { name: 'Jane', lastName: 'Doe' },
}

This defines the type of the object such that its keys can be any string. But I would like to still be able to infer the keys of the object (as it is static), which, in this case, I can't.

I can do something like this:

type ObjKeys = 'a' | 'b';

const obj: { [key in ObjKeys]: Person } //...

But it's verbose.

Is there a short way of saying "here is the type for all the values of this object but leave the keys as defined statically?"

Upvotes: 3

Views: 669

Answers (1)

jcalz
jcalz

Reputation: 329773

You can't do this purely at the type level; if you annotate the type of obj with {[k: string]: Person} then the compiler will assume that this is the actual type to use and will not narrow it to something else based on the assignment of {a: {...}, b: {...}}. Such control flow analysis only occurs for values whose type is a union, and {[k: string]: Person} is not a union.

What I usually do in situations like this is to write a generic helper identity function from which the compiler can infer the type you're looking for:

const asPersonDict = <K extends PropertyKey>(
  o: { [P in K]: Person }) => o;

And then you call the helper function instead of annotating the type:

const obj = asPersonDict({
  a: { name: 'John' },
  b: { name: 'Jane', lastName: 'Doe' },
});

/* const obj: {
    a: Person;
    b: Person;
} */

Playground link to code

Upvotes: 2

Related Questions