简体   繁体   English

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

[英]Typescript: mapped types, strip field from a union

I'm trying to create a type mapper NewRecord<T> which will strip the id type out of <T> . 我正在尝试创建一个类型映射器NewRecord<T> ,它将把id类型从<T>剥离出来。 That's how I do it: 我就是这样的:

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

but, unfortunately, it doesn't play nice with union types. 但是,不幸的是,它不适用于联合类型。 Let me illustrate: 让我说明一下:

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

It happens because keyof iterates over intersection of types, not unions and that's intended behaviour: https://github.com/Microsoft/TypeScript/issues/12948 发生是因为keyof在类型的交集而不是并keyof迭代,并且这是预期的行为: https : //github.com/Microsoft/TypeScript/issues/12948

What is the correct way to strip a field from the union? 从联合中删除字段的正确方法是什么?

You want to apply the mapped type for each member of the union. 您要为联合的每个成员应用映射的类型。 Fortunately conditional types have this exact behavior, they distribute over naked type parameters. 幸运的是,条件类型具有这种确切的行为,它们分布在裸类型参数上。 This means that the mapped type is applied independently to each member of the union and all results are unioned into the final type. 这意味着映射类型将独立应用于联合的每个成员,并且所有结果都将合并为最终类型。 See here and here for more explanations. 有关更多说明,请参见此处此处

In this case the condition in the conditional type can just be extends any we don't care about the conditional part we care just about the distribution behavior of the conditional type: 在这种情况下,只要我们不关心条件部分,只要我们关心条件类型的分布行为,就可以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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM