我有两个班级: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
}
答案 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
}