如何从记录类型创建区分联合类型?

时间:2019-11-20 11:24:26

标签: typescript

是否可以创建映射条件类型,使得诸如

的任何类型
interface Car {
    color: string
    max_speed: number
    doors: number
}

type CarPorperties = SomeMappedAndConditiomalType<Car>

将导致

type CarProperties =
    {color: string}
    |{max_speed: number}
    |{doors: number}

2 个答案:

答案 0 :(得分:3)

事实证明这是可能的:将属性类型映射到{ __tag: k, [k]: T[k] }之类,然后采用这些类型的并集。这样就给出了标记的联合类型,其中__tag属性是判别式。

用法示例:

interface Car {
    color: string
    max_speed: number
    doors: number
}

type Test = ObjectToTaggedUnion<Car>

/*
 * Test = { __tag: 'color', color: string }
 *      | { __tag: 'max_speed', max_speed: number }
 *      | { __tag: 'doors', doors: number }
 */ 

下面的实现。详细信息有些棘手,因为Typescript很难使对象类型变得简单,其中__tag属性是直接命名的,而k属性却不是这样。

type ObjectToTaggedUnion<T> = {
    [K in keyof T]: K extends string
        ? { [kk in K | '__tag']: kk extends '__tag' ? K : T[K] }
        : never
}[keyof T];

Playground Link

答案 1 :(得分:2)

您想要做的是将产品类型拆分为总和类型。我认为这是不可能的,您可以分别使用键和值,但是看起来无法实现配对。

但是我们可以采取另一种方法。因此,我们可以从代表每个属性的类型开始,并通过&|

// single props types
type ColorProp = { color: string }
type MaxSpeedProp = { max_speed: number }
type DoorsProp = { doors: number }

type Car = ColorProp & MaxSpeedProp & DoorsProp // product
type CarProps = ColorProp | MaxSpeedProp | DoorsProp // sum

// we can build Car directly as it would be orginal interface
const car: Car = {
  color: 'color',
  max_speed: 1,
  doors: 2
}

// or create it by merging props types instances
const doors: DoorsProp = { doors: 2 };
const max_speed: MaxSpeedProp = { max_speed: 2 };
const color: ColorProp = { color: 'red' };

const car2: Car = {
  ...doors,
  ...color,
  ...max_speed
}

通过这种方式,您可以组合较小的类型-将类型道具化为较大的复合物-总和和乘积。