在typescript中定义参数类型(解构赋值)

在typescript中定义参数类型(解构赋值),typescript,Typescript,我有两个类:RewardArticleBase和RewardArticle export class RewardArticleBase extends Reward { } export class RewardArticle extends Reward { public title: string = ''; public images: string[] = []; } 以及一个可以处理这两种类型的配置函数。它们之间的区别是RewardArticleBase没有标题字

我有两个类:RewardArticleBase和RewardArticle

export class RewardArticleBase extends Reward {
}

export class RewardArticle extends Reward {
    public title: string = '';
    public images: string[] = [];
}
以及一个可以处理这两种类型的配置函数。它们之间的区别是RewardArticleBase没有标题字段。添加
标题?
似乎不正确,但现在它说RewardArticleBase和RewardArticle没有标题属性和索引签名

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

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

虽然这种行为是出于设计,通常是好事,但我们可以用稍微不同的方式来看待工会。我们可以将is视为一种类型,它具有所有公共字段,但也具有仅标记为可选的非公共字段:

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 }
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>>> 
给定这样的类型,我们可以以类型安全的方式执行参数解构(如果使用
strictNullChecks
,则非公共字段需要额外的null检查)

要创建所需类型而不显式重新定义它,我们首先需要一种将并集转换为交集的方法(以便访问所有字段)。幸运的是,答案中的类型
UnionToIntersection
为我们提供了一种实现这一点的方法(别忘了更新@jcalz的答案,这是一个真正伟大的未署名答案:)

使用
UnionToIntersection
我们可以创建一个类型,该类型在与非公共字段的交叉点中包含公共键(实际上只是原始的联合),使用
Pick
仅从交叉点获取非公共字段,并使用
Exclude
获取非公共字段的键,最后应用
Partial
将所有非公共字段标记为可选字段:

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 }
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>>> 
类型UnionToIntersection=
(U扩展任何?(k:U)=>void:never)扩展((k:inferi)=>void)?I:从来没有
类型DeconstructUnionHelper=T&Partial
用法:

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
}
出口类奖励{
内容:string=“”;
奖励金额=”;
}
导出类RewardArticleBase扩展奖励{
}
出口类奖品扩展奖励{
公共标题:字符串=“”;
公共图像:字符串[]=[];
}
函数getRewardConfig({内容,奖励金额,标题}:解构UnionHelper){
如果(title){//title是字符串|未定义,因为它是可选的
//待办事项
}
内容//是字符串
//其他东西
}