2

Example

const food = {
  fruit: ['apples', 'oranges'],
  meat: ['chicken', 'pig']
}

function makeFood(ingredient, category) {
 switch(true) {
   case category === 'fruit' && ingredient === 'apples' {
    // do something
   }
   case category === 'fruit' && ingredient === 'oranges' {
    // do something
   }
   case category === 'meat' && ingredient === 'chicken' {
    // do something
   }
   case category === 'meat' && ingredient === 'pig' {
    // do something
   }
 }
}

what's the best way to type category is a key of food and ingredient is a value?

Currently I'm doing

function(ingredient, keyof typeof category) {

would love to keep the relation between the two. So TS will know the ingredient type based in the category value.

6
  • 2
    Can you please provide a more complete example? Commented Nov 24, 2021 at 17:36
  • What's category? How do ingredient and category relate to food? Commented Nov 24, 2021 at 17:37
  • I'll update the question right away Commented Nov 24, 2021 at 17:39
  • Not sure what exactly you want, but something like function f<T extends keyof typeof food>(ingredient: (typeof food)[T][number], category: T) {? Note, that unless you add as const to the arrays in food, they are just string[]. Also note, that [number] may be a bit quick&dirty, there are alternatives. Commented Nov 24, 2021 at 17:46
  • @Norfeldt wasn't my down vote Commented Nov 24, 2021 at 17:51

1 Answer 1

4

You use typescript Generics. In this you wanted category to be generic parameter, which ingredient can then use.

You'll need to use as const (see here) on your source object to make sure it's specific string are part of its type.

const food = {
  fruit: ['apples', 'oranges'],
  meat: ['chicken', 'pig']
} as const // important to add "as const" here

function makeFood<
  K extends keyof typeof food // Generic parameter
>(
  ingredient: typeof food[K][number], // use K to lookup types for ingredient
  category: K // provide the key to to use here as K
) {
 switch(true) {
   case category === 'fruit' && ingredient === 'apples': {
    // do something
   }
   case category === 'fruit' && ingredient === 'oranges': {
    // do something
   }
   case category === 'meat' && ingredient === 'chicken': {
    // do something
   }
   case category === 'meat' && ingredient === 'pig': {
    // do something
   }
 }
}

makeFood('apples', 'fruit')
makeFood('pig', 'fruit') // type error

Playground

Sign up to request clarification or add additional context in comments.

7 Comments

Also cool to note that it will prevent you from using typos in the switch.
Thank you very much for such a quick answer
An alternative without generics is function makeFood(ingredient: typeof food[typeof category][number], category: keyof typeof food)
And I keep thinking there must be a way to use infer so the compiler can infer that in the first case clause, ingredient could only be 'apples'|'oranges'
@JuanMendes your suggestion without generics is event better since it keeps the relation between the types when autocompleting in vscode. If you post it as an answer I'll give it a vote.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.