从类型中选择一个键值对

时间:2019-07-08 10:35:18

标签: typescript typescript-generics

我有以下类型:

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

,我需要基于Example类型创建类型,以成为key: value对之一。当然,我正在寻找通用解决方案。这是此类型应返回的内容:

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>;

但是它返回所有属性和示例,其中const f无法正常工作。

2 个答案:

答案 0 :(得分:1)

我们将生成所有可能性的组合:

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

我们这样做的方法是,首先创建一个新类型,对于每个键,我们都有一个仅带有该键的object属性(暂时忽略& 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>

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

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

答案 1 :(得分:0)

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

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