打字稿:映射的类型,联合的带状字段

时间:2018-10-15 10:23:43

标签: typescript typescript3.0

我正在尝试创建类型映射器NewRecord<T>,该类型映射器将从id中剥离<T>类型。 我就是这样的:

type NewRecord<T> = {
  [P in Exclude<keyof T, 'id'>]: T[P]
}

但是,不幸的是,它不适用于联合类型。让我举例说明:

interface IRecord {
  id: number
}

interface IBotRecord extends IRecord {
  isBot: true
  cpuCores: 4
}

interface IHumanRecord extends IRecord {
  isBot: false
  isHungry: true
}

type ICreature = IHumanRecord | IBotRecord

type INewBotRecord = NewRecord<IBotRecord>
type INewHumanRecord = NewRecord<IHumanRecord>
type INewCreature = NewRecord<ICreature>

const newHuman:INewHumanRecord = {
  isBot: false,
  isHungry: true // works!
}

const newCreature:INewCreature = {
  isBot: false,
  isHungry: true // does not exist in type NewRecord<ICreature>
}

之所以会这样,是因为keyof在类型的交集而不是并集上进行迭代,并且该行为是预期的:https://github.com/Microsoft/TypeScript/issues/12948

从联合中剥离字段的正确方法是什么?

1 个答案:

答案 0 :(得分:4)

您要为联合的每个成员应用映射类型。幸运的是,条件类型具有这种确切的行为,它们分布在裸类型参数上。这意味着映射类型将独立应用于联合的每个成员,并且所有结果都将合并为最终类型。有关更多说明,请参见herehere

在这种情况下,条件类型中的条件可以只是extends any,我们不在乎条件部分,我们只在乎条件类型的分布行为:

type NewRecord<T> = T extends any ? {
    [P in Exclude<keyof T, 'id'>]: T[P]
} : never

type INewCreature = NewRecord<ICreature>
// The above type is equivalent to
type INewCreature = {
    isBot: false;
    isHungry: true;
} | {
    isBot: true;
    cpuCores: 4;
}
const newCreature:INewCreature = {
  isBot: false,
  isHungry: true // works fine
}