这里有两个问题。
第一个与工会参与时的过多财产检查有关。你可以阅读这个答案here类似的问题。其要点是,对联合的多余属性检查允许任何成员的任何键出现在对象上。我们可以通过引入 never 类型的额外成员来解决这个问题,以确保具有多余属性的对象不会错误地与特定成员兼容:
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
第二个问题与以下事实有关:如果您指定变量的类型,TypeScript 不会进行任何额外的推断,因此items[1]
is 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