在打字稿中定义参数的类型(解构分配)

时间:2018-08-10 07:03:40

标签: typescript

我有两个班级:RewardArticleBase和RewardArticle

export class RewardArticleBase extends Reward {
}

export class RewardArticle extends Reward {
    public title: string = '';
    public images: string[] = [];
}

和一个可以同时处理这两种类型的配置函数。它们之间的区别是RewardArticleBase没有标题字段。添加title?似乎是不正确的,但是现在它说RewardArticleBase和RewardArticle没有标题属性和索引签名。

function getRewardConfig({ content, reward_amount, title }: RewardArticle | RewardArticleBase) {
    if (title) {
        // todo
    }
    // other stuff
}

1 个答案:

答案 0 :(得分:2)

联盟仅允许访问公共字段。这是有道理的,因为没有任何额外的检查就无法知道是否存在其他字段。

虽然这种行为是设计使然,并且通常是一件好事,但我们可以以稍微不同的方式看待并集。我们可以将其视为具有所有公共字段的类型,但也将非公共字段仅标记为可选:

type A =  { common : string, fieldA: string }
type B =  { common : string, fieldB: string }
// We would like to create a type equivalent to 
type AB = { common : string, fieldA?: string, fieldB?: string }

鉴于这种类型,我们可以以类型安全的方式执行参数解构(对于非公用字段,如果我们使用strictNullChecks,则需要进行额外的null检查)

要创建所需的类型而不显式重新定义它,我们首先需要一种将联合转换为相交的方法(以便访问所有字段)。幸运的是,this答案中的类型UnionToIntersection<U>为我们提供了一种方法(不要忘了投票@jcalz的答案,这是一个真正的不起眼的答案:))。

使用UnionToIntersection来创建一个类型,该类型包含与非公共字段相交的公共键(实际上只是原始并集),使用Pick仅接受非公共字段从交集和Exclude中获取不常见字段的键,最后应用Partial将所有非公共字段标记为可选:

type UnionToIntersection<U> = 
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never

type DeconstructUnionHelper<T> = T & Partial<Pick<UnionToIntersection<T>, Exclude<keyof UnionToIntersection<T>, keyof T>>> 

用法:

export class Reward {
    content: string = "";
    reward_amount = "";
}
export class RewardArticleBase extends Reward {
}

export class RewardArticle extends Reward {
    public title: string = '';
    public images: string[] = [];
}
function getRewardConfig({ content, reward_amount, title }: DeconstructUnionHelper<RewardArticle | RewardArticleBase>) {
    if (title) { // title is string | undefined since it's optional
        // todo
    }
    content // is string
    // other stuff
}