You have the option to define a pair as a mapped type that assigns 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]
When applied to Rabbit
, it generates a union of pairs:
type Test = Pair<Rabbit>
// {field: "name", value: string} | {field: "age", value: number} | {field: "weight", value: number}
This approach ensures the desired outcome with single pair declarations:
const namePair: Pair<Rabbit> = { field: 'name', value: "Bob" } // works fine
const weightPair: Pair<Rabbit> = { field: 'weight', value: 250 } // valid
const badPair: Pair<Rabbit> = { field: 'name', value: 606} // will not work
Moreover, this method is applicable to arrays as well:
const arr: Pair<Rabbit>[] = []
arr.push({ field: "name", value: "Bob" }) // acceptable
arr.push({ field: "weight", value: 250 }) // satisfactory
arr.push({ field: "name", value: 606 }) // fails to meet the criteria
TypeScript playground