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