网站搜索

typescript - 从对象类型创建键/值类型


假设我们有一种类型

type Foo = {
  a: string;
  b: number;
  c: boolean;
}

我现在想要定义一个对象类型,它具有一个给定类型 T 的 key 和 value,这样 value 的类型可以直接从 key 推断出来,这样我就可以这样做:

const kv1: KeyValue<Foo> = {key: 'a', value: 'STRING'};
const kv2: KeyValue<Foo> = {key: 'b', value: 42};
const kv3: KeyValue<Foo> = {key: 'c', value: true};

如果我只是这样做:

type KeyValue<T> = {
  key: keyof T;
  value: T[keyof T]
}

... 那么显然这些值将是 Foo 中所有属性值的并集:

而如果我这样做:

type KeyValue<T, K extends keyof T> = {
  key: K;
  value: T[K]
}

...那么当然,如果明确地写成KeyValue<Foo, 'a'>,我可以创建与Foo属性类型匹配的对象字面量,但如果我没有具体提供每个字面量的类型和键,而只是写KeyValue<Foo, keyof Foo>,每个值将被允许为Foo中任意值的类型,也就是说,所有值都会是,而不是根据输入的key来推断。

最终我希望能够做像这样的事情:

const kvs: Array<KeyValue<Foo>> = [
  {key: 'a', value: 'STRING'}, // should not compile if value is a number or boolean
  {key: 'b', value: 42}, // should not compile if value is a string or boolean
  {key: 'c', value: true}, // should not compile if value is a string or number
];

是否有可能在这些约束下推导出 KeyValue 类型,使其在上面的第二个示例中实际上变成 KeyValue<Foo, 'a'> | KeyValue<Foo, 'b'> | KeyValue<Foo, 'c'>,而不必手动编写这个联合类型?基本上,我想要的是能够从当前字面量对象中 key 的值推断出 value 的类型。

答案#1

你不能使用单个 KeyValue 对象来表示每个键值对,因为这将要求要么对键进行部分推断,要么以某种方式在同一对象的 value 属性中引用 key 属性。

然而,你可以做的是使用输入对象创建一个区分联合类型。

type Foo = {
  a: string;
  b: number;
  c: boolean;
}

type KeyValue<T> = {
  [P in keyof T]: {
    key: P;
    value: T[P];
  }
}[keyof T];

// with KeyValue<Foo>, this generates the following type
// 
// {
//     key: "a";
//     value: string;
// } | {
//     key: "b";
//     value: number;
// } | {
//     key: "c";
//     value: boolean;
// }

const kvs: Array<KeyValue<Foo>> = [
  {key: 'a', value: 40}, // ERROR
  {key: 'b', value: true}, // ERROR
  {key: 'c', value: "foo"}, // ERROR
];