0

Say I had an interface that described a library that items that look like this:

interface MyItem {
  category: string,
  title: string
}

Now I have a config file full of those MyItems:

const myLibrary: MyItem[] = [
  {
    category: "dogs",
    title: "Fuzzy quadrupeds" 
  },
  { 
    category: "snakes",
    title: "Slithery reptiles"
  },
  ...
]

Now, I'd like to create a type that consists of all the category in MyItem[]

If I do this: type Category = typeof MyItem[number]["category"] I get string.

If I remove the typing from myLibrary (i.e. const myLibrary = [ {...} ]) and get what I want:

That means that type Category = typeof MyItem[number]["category"] gives me the union type I want of dogs | snakes but of course I lose the typing when creating new items in my config file.

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102
Caleb Larsen
  • 739
  • 2
  • 8
  • 17

3 Answers3

1

We want to restrict the items in myLibrary such that they must implement MyItem, but we want to do it in a way that preserves specific types of specific items and doesn't broaden the type to just MyItem.

It is hard to do that just with assigning a type to the constant. A commonly-used pattern is to create the constant through an identity function. With a function we can use extends syntax to ensure that T extends MyItem[] while keeping T specific.

I had to use as const to get the literal category names, so I also had to allow readonly in the function arguments.

interface MyItem {
  category: string,
  title: string
}

const buildLibrary = <T extends readonly MyItem[]>(library: T): T => library;

const myLibrary = buildLibrary([
  {
    category: "dogs",
    title: "Fuzzy quadrupeds" 
  },
  { 
    category: "snakes",
    title: "Slithery reptiles"
  }
] as const);

type Categories = (typeof myLibrary)[number]['category'] // "dogs" | "snakes"

Typescript Playground Link

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102
0

If I understand you right, you want this: How to create enum like type in TypeScript? and then specify MyItem as

interface MyItem: {
    category: MyDogSnakeType,
    title: string
}
  • Thanks! I thought of the enum route and it might be road I end up going down. I was hoping to avoid having to define the `category` separately when defining all of the different items that might be in library. – Caleb Larsen Mar 13 '21 at 23:03
  • If someone answers with a nicer option, I will be interested ;) –  Mar 13 '21 at 23:33
0

Do not complicate


type Categorías = 'Food' | 'categoy1' | 'cstegory2'

interface MyItem {
  category: Categories;
  title: string
}

const myLibrary: MyItem[] = [
  {
    category: "dogs",
    title: "Fuzzy quadrupeds" 
  },
  { 
    category: "snakes",
    title: "Slithery reptiles"
  },
  ...
Ernesto
  • 3,944
  • 1
  • 14
  • 29