1

I want to create a generic solution for a type safe array of key value pairs

suppose we have a type like this:

type Person = {
  name: string,
  age: number,
}

I want to define KeyValArr type to have a type safe solution for usage below:

const myPersonKV: KeyValArr<Person> = [
  { key: 'name', value: 'john' },
  { key: 'age', value: 22 }
]

The solution I had on my mind was to define KeyValArr as below:

type KeyValArr<T> = {
  key: keyof T,
  value: T[keyof T]
}[]

but the problem with this approach is that the code below does not result in any error:

const tmp: KeyValArr<Person> = [
  { key: age, value: 'wrong type!' }
]

how can I define KeyValArr in a way to have type safety on value based on the defined key?

3
  • Like this Person[]? Commented Jul 18, 2021 at 12:02
  • 1
    Your examples have many typos and produce numerous TS errors. How can we help you if you don't take the time to give us accurate info? Commented Jul 18, 2021 at 12:23
  • @Inigo I'm sorry I had missed a [] on the KeyValArr type and edited it. Is there any other typo? Commented Jul 18, 2021 at 20:35

2 Answers 2

2

All you need to do is to define union type of allowed values upfront:

type Person = {
  name: string,
  age: number,
}

type Util<T> = {
  [Prop in keyof T]: {
    key: Prop,
    value: T[Prop]
  }
}

type Values<T> = T[keyof T];

type Result = Values<Util<Person>>

const tmp: Array<Result> = [
  { key: ' age', value: 'wrong type!' }, // error,
  { key: 'name', value: 'wer' } // ok
]

Playground

Here, in my blog, you can find how to create similar data structures but with callbacks

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

1 Comment

This is a really smart solution. Thank you very much. I spent more than a day reading documentations and couldn't find a desirable solution. I believe the MappedTypes part of the typescript docs need to include smart examples like this. Thanks again. Will definitely check out your blog <3
2

I'd use something like this:

type Person = {
  name: string,
  age: number,
}

type KeyValArr<T,K=keyof T>= (K extends keyof T ? {key: K, value:T[K]} : never)[]
const myPersonKV: KeyValArr<Person> = [
  { key: 'name', value: 'john' },
  { key: 'age', value: 22 },
  { key: 'age', value: '22 }//error
]

Playground

4 Comments

Thanks for the answer but this solution results in explicitly writing every single key as type argument for each item which is not desirable and repetitive (since we have the key in code). I was looking for an implicit general solution.
No, it doesn't require writing anything, K has a default value, you don't need to override it, the usage will be the same as you have now - KeyValArr<Person>[]
Oh now I understand. I didn't know about type defaults. This a concise solution and I really like it. Thank you. I think this is the best solution for my scenario. Would you polish it and match it with question (notice the KeyValArr is an array type. also the link is not working) so I set your answer as the accepted one?
@Amin_mmz you're welcome! I've updated the answer, have a look

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.