Ian Storm Taylor
Ian Storm Taylor

Reputation: 8690

How to type nested objects with a value that matches its key in TypeScript?

I have an object of objects like:

const CATEGORIES = {
  diet: {
    id: 'diet',
    title: 'Diet',
    ...
  },
  ...
}

How can I write a type for it so that TypeScript will ensure that the nested id property will match the object's key in the parent?

I've tried...

const CATEGORIES: {
  [T in string]: {
    id: T
    title: string
  }
} = { ... }

...but this seems to still accept any strings for id.

I'm assuming it uses mapped types and generics somehow, but I can't quite figure out what the syntax is, if it's even possible.

Upvotes: 1

Views: 125

Answers (1)

jcalz
jcalz

Reputation: 327754

You can't map over all of string and have it act the way you want here; see microsoft/TypeScript#22509. You'll need a set of string literal keys to map over. One way to achieve this is to use a generic helper function which infers those keys from the passed-in value, and verifies that the id subproperty of each property matches its key:

const asCategories = <T extends { [K in keyof T]: { id: K, title: string } }>(t: T) => t;

You can test to see that it's happy with good values:

const CATEGORIES = asCategories({
  diet: {
    id: 'diet',
    title: 'Diet',
    //...
  },
  //...
}); // okay

and angry with bad ones:

const BADCATEGORIES = asCategories({
  diet: { id: "diet", title: "Diet" },
  exercise: { id: "exersice", title: "Exercise" } // error!
  // -------> ~~
  // Type '"exersice"' is not assignable to type '"exercise"'.
})

Okay, hope that helps; good luck!

Playground link to code

Upvotes: 1

Related Questions