我想创建一种方法来指定特定对象的属性(可为空)是否为null。我想这样做,以便可以筛选出属性确实为null的所有对象,以便可以安全地使用该对象并访问其属性。
在此示例中,Container
是对象,nullableContent
是属性。我想通过在Container
的通用参数中使用条件类型来指定该属性不为null。如果HasThing
为true,则nullableContent
不为null。如果为false,则为null。
即使在创建类型谓词hasContent
时,这似乎也可以工作。在hasContent(container) === true
的情况下,可以算出container
的类型为Container<true>
。但是,如果hasContent(container) === false
的TypeScript认为container
的类型永远不会,那么我将无法再访问其任何属性。
为什么会这样?为什么TS无法确定container
现在只是类型Container<false>
?
interface Container<HasThing extends boolean = boolean> {
id: string;
nullableContent: HasThing extends true ? string : null;
}
const hasContent = (container: Container): container is Container<true> =>
!!container.nullableContent;
function doThing(container: Container) {
if (hasContent(container)) {
return container;
} else {
// Below line gives error "Property 'id' does not exist on type 'never'.ts(2339)"
throw new Error(`Container with ID ${container.id} is empty`);
}
}
答案 0 :(得分:1)
在工会上,缩小效果最好。在false
分支上具有并集之后,编译器可以从该联合中取出在true
分支上处理过的组成部分。
如果没有要缩小的并集,则编译器将使原始类型与保护类型相交,该保护类型将在Container<true>
分支上对true
起作用,但是在false分支上它将尝试做类似Exclude<Container, Container<true>>
的操作,结果在这里never
(仍然试图绕开为什么……)
最简单的解决方案是将接口转换为联合:
type Container = {
id: string;
} & ({ nullableContent: string } | { nullableContent: null })
const hasContent = (container: Container): container is Exclude<Container, { nullableContent: null }> =>
!!container.nullableContent;
function doThing(container: Container) {
if (hasContent(container)) {
return container;
} else {
// Below line gives error "Property 'id' does not exist on type 'never'.ts(2339)"
throw new Error(`Container with ID ${container.id} is empty`);
}
}
或者如果您想保留type参数:
type Container<HasThing extends boolean = boolean> = {
id: string;
} & (HasThing extends true ? { nullableContent: string } : { nullableContent: null })
const hasContent = (container: Container): container is Container<true> =>
!!container.nullableContent;
function doThing(container: Container) {
if (hasContent(container)) {
return container;
} else {
// Below line gives error "Property 'id' does not exist on type 'never'.ts(2339)"
throw new Error(`Container with ID ${container.id} is empty`);
}
}
答案 1 :(得分:0)
看起来TS不能缩小类型。天哪,我不知道为什么,但是我可以提出一种解决方案,该解决方案至少会更改/影响您的代码。将类型定义更改为条件联合,例如:
type Container<T extends boolean = boolean> = T extends true ? {
id: string;
nullableContent: string;
} : {
id: string;
nullableContent: null;
}
现在您的其余代码都可以使用适当的类型范围缩小和推断。