Home > other >  Construct an object such that one field is the value type corresponding to another key field
Construct an object such that one field is the value type corresponding to another key field

Time:04-13

Basically I need to derive a "Pair" type

e.g.

type Pair<T extends Record<string, string | number>, K extends keyof T> = {
    field: K,
    value: T[K]
}

so that given

type Rabbit = {
    name: string,
    age: number,
    weight: number
}

I'd like to have:

let namePair: Pair<Rabbit, keyof Rabbit> = {
    field: 'name',
    value: 123 //this should fail
}

CodePudding user response:

You can use a generic type T which is a key of Rabbit.

type RabbitPair<T extends keyof Rabbit> = {
    rabbitField: T
    rabbitValue: Rabbit[T]
}

let field1 : RabbitPair<"name"> = { rabbitField: 'name', rabbitValue: 'Johnny'}
let field2 : RabbitPair<"age"> = { rabbitField: 'age', rabbitValue: 14}
let field3 : RabbitPair<"weight"> = { rabbitField: 'weight', rabbitValue: 'This is not a number'}

function test<T extends keyof Rabbit>(t: RabbitPair<T>){}

test({
  rabbitField: "age",
  rabbitValue: 23 // fails if there is string here
})

The solution doesn't work perfectly for arrays though:

const arr: RabbitPair<keyof Rabbit>[] = []

arr.push({
  rabbitField: "name",
  rabbitValue: "value" // numbers work here too, but nothing else
})

function push_rabbit<T extends keyof Rabbit>(t: RabbitPair<T>){
  arr.push(t)
}

push_rabbit({
  rabbitField: "name",
  rabbitValue: "value" // numbers don't work here
})

CodePudding user response:

You can declare pair as a mapped type that maps each key to a corresponding pair object:

type Pair<T extends Record<string, string | number>> =
  {[K in keyof T]: {field: K, value:T[K]}}[keyof T]

If you apply it to Rabbit, the result is a union of pairs:

type Test = Pair<Rabbit>
// {field: "name", value: string} | {field: "age", value: number} | {field: "weight", value: number}

This should give you the desired behavior on single pair declarations:

const namePair: Pair<Rabbit> = { field: 'name', value: "Bob" } // ok

const weightPair: Pair<Rabbit> = { field: 'weight', value: 250 } // ok

const badPair: Pair<Rabbit> = { field: 'name', value: 606} // fails

And it works on arrays as well:

const arr: Pair<Rabbit>[] = []

arr.push({ field: "name", value: "Bob" }) // ok

arr.push({ field: "weight", value: 250 }) // ok

arr.push({ field: "name", value: 606 }) // fails

TypeScript playground

  • Related