打字稿版本3.0.3
我正在为侧边栏导航菜单创建模型,item可以是以下所述的两种类型之一:
type SidebarItems = Array<SimpleSidebarItem | ComplexSidebarItem>;
abstract class SidebarItem {
title: string;
}
class SimpleSidebarItem extends SidebarItem {
url : string;
}
class ComplexSidebarItem extends SidebarItem {
subItems: Array<{
title: string;
url : string;
}>
}
我无法正常工作- 这应该不是有效的输入,但显示正常:
const items: SidebarItems = [{title: '', url: '', subItems: [{title: '', url: ''}]}];
推理无法按预期进行:
const items: SidebarItems = [{title: '', url: ''}, {title: '', subItems: [{title: '', url: ''}]}];
const shouldBeComplexSidebarItem = items[1];
ShouldBeComplexSidebarItem的类型是SimpleSidebarItem | ComplexSidebarItem。
我在这里想念什么?
答案 0 :(得分:2)
这里有两个问题。
第一个涉及与工会有关的多余财产检查。您可以阅读关于类似问题的答案here。其要点是,多余的联合检查属性允许对象上存在任何成员的任何键。我们可以通过引入额外的类型的成员来避免这种情况,即永远不要确保具有过多属性的对象与特定成员不会错误兼容:
type SidebarItems = Array<StrictUnion<SimpleSidebarItem | ComplexSidebarItem>>;
type UnionKeys<T> = T extends any ? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends any ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
const items2: SidebarItems = [{title: '', url: '', subItems: [{title: '', url: ''}]}]; //error
const items3: SidebarItems = [{title: '', subItems: [{title: '', url: ''}]}]; //ok
第二个问题与以下事实有关:如果您指定变量的类型,打字稿将不会做任何额外的推断,因此items[1]
是ComplexSidebarItem
的信息将会丢失,所有打字稿都会知道一个项目可以是SimpleSidebarItem | ComplexSidebarItem
。
我们可以使用类型保护来检查类型:
const items: SidebarItems = [{title: '', url: ''}, {title: '', subItems: [{title: '', url: ''}]}];
const shouldBeComplexSidebarItem = items[1];
if(!('url' in shouldBeComplexSidebarItem)){ //type guard
shouldBeComplexSidebarItem.subItems // is ComplexSidebarItem here
}
或者我们可以使用一个函数来创建一个数组,该数组将推断一个元组类型,对于该类型,特定索引处的类型是已知的:
function createItems<T extends SidebarItems>(...a:T){
return a;
}
const items = createItems({title: '', url: ''}, {title: '', subItems: [{title: '', url: ''}]});
const shouldBeComplexSidebarItem = items[1];
shouldBeComplexSidebarItem.subItems // is an object literal compatible with ComplexSidebarItem
您还可以手动指定元组类型,在这种情况下,不再需要StrictUnion
:
const items: [SimpleSidebarItem, ComplexSidebarItem] = [{title: '', url: ''}, {title: '', subItems: [{title: '', url: ''}]}];
const shouldBeComplexSidebarItem = items[1];
shouldBeComplexSidebarItem.subItems // is ComplexSidebarItem