从类型中选择一个键值对

Pick one key-value pair from type

我有以下类型:

type Example = {
  key1: number,
  key2: string
}

我需要创建基于 Example 类型的类型,使其成为 key: value 对之一。当然,我正在寻找通用解决方案。这是这种类型应该 return:

type Example2 = { ... };
const a: Example2 = { key3: 'a' } // incorrect
const b: Example2 = { key1: 'a' } // incorrect
const c: Example2 = { key2: 1 } // incorrect
const d: Example2 = { key1: 1 } // correct
const e: Example2 = { key2: 'a' } // correct
const f: Example2 = { key1: 1, key2: 'a' } // incorrect

我正在尝试使用这个:

type GetOne<T> = { [P in keyof T]: T[P] };
type Example2 = GetOne<Example>;

但是 return 所有属性和示例 const f 都没有按预期工作。

我们将生成所有可能性的并集:

type Example = {
   key1: number,
   key2: string
}

type PickOne<T> = { [P in keyof T]: Record<P, T[P]> & Partial<Record<Exclude<keyof T, P>, undefined>> }[keyof T]
type Example2 = PickOne<Example>;
const a: Example2 = { key3: 'a' } // incorrect
const b: Example2 = { key1: 'a' } // incorrect
const c: Example2 = { key2: 1 } // incorrect
const d: Example2 = { key1: 1 } // correct
const e: Example2 = { key2: 'a' } // correct
const f: Example2 = { key1: 1, key2: 'a' } // incorrect

我们这样做的方式是,我们首先为每个键创建一个新类型,我们有一个对象 属性,只有那个键(暂时忽略 & Partial<Record<Exclude<keyof T, P>, undefined>>)。所以 { [P in keyof T]: Record<P, T[P]> } 例如将是 :

type Example2 = {
    key1: Record<"key1", number>;
    key2: Record<"key2", string>;
}

然后使用索引运算 [keyof T] 得到这个新类型中所有值的并集,所以我们得到 Record<"key1", number> | Record<"key2", string>

此类型适用于除最后一个测试之外的所有类型,在该测试中您不希望原始类型具有多个属性。由于 excess 属性 检查与联合类型 () 一起工作的方式,如果它存在于任何联合成分中,它将允许一个键。

为了解决这个问题,我们将 Record<P, T[P]> 与一个可选地包含其余属性 (Exclude<keyof T, P>) 的类型相交,但强制所有属性都为 undefined.

也可以通过下面的代码实现:

export type PickOne<T> = {
    [P in keyof T]?: Record<P, T[P]>
}[keyof T]

type Example = {
    key1: number;
    key2: string;
}

type Example2 = PickOne<Example>;

const a: Example2 = { key1: 1 } // correct
const b: Example2 = { key1: "1" } // incorrect - string cannot be assigned to number
const c: Example2 = { key2: 'a' } // correct
const d: Example2 = { key3: 'a' } // incorrect - unknown property