export interface MyDoc{
field: string;
}
function insert(doc: MyDoc) { /* db insert code here */ }
let x; // simulate undefined value
let doc = { field: x };
console.log(JSON.stringify(doc));
if (x === undefined) { // also does not prevent with doc.field === undefined
return; // return if x is undefined
}
insert(doc); // causes TS2345: Argument of type error
就我而言,这是不可能的,但感觉像代码气味。我如何通知TS类型检查器doc
不能包含值为undefined
的field
,即使它可能用于其他目的(如日志记录)
1条答案
按热度按时间3qpi33ja1#
TypeScript表现出的几乎所有narrowing行为都只作用于被检查的值。也就是说,对表达式
exp1
的某些操作可能会导致exp1
的表观类型更改,但它几乎永远不会更改其他表达式exp2
的表观类型。有几个明显的例外:检查被鉴别的联合对象的鉴别属性将缩小对象本身的类型;有时候你可以将类型保护检查的结果保存为一个
boolean
值,然后稍后对该值的检查将缩小原始表达式的范围;并且您可以将有区别的联合分解为单独的变量,因此对一个变量的检查将缩小另一个变量的范围。但是你所做的不是这些例外之一。语言中没有任何东西会导致对
x
的检查影响doc
的表观类型。即使他们实现了microsoft/TypeScript#42384的建议,将属性类型保护扩展到非区分联合体的对象,它仍然不会给你这种行为。为了在总体上支持这类事情,编译器必须执行microsoft/TypeScript#46915中提到的"完全反事实分析",跟踪每个表达式的每一个可能的收缩,这对编译器来说是非常昂贵的,所以不太可能按照你想要的方式工作。那么你能做些什么呢?
如果你不想重构你的代码,你可以使用一个类型Assert来告诉编译器它应该把
doc
当作MyDoc
:现在没有错误了,但是现在你要负责验证类型安全,因为编译器不能,如果你执行了错误的检查,仍然不会有错误:
所以要小心。
如果你对让编译器遵循你的逻辑更感兴趣,你将不得不重构成一个受支持的收缩方法。最灵活的方法是实现一个用户定义的类型保护函数,它可以准确地解释你对编译器所做的收缩。它仍然只对你所检查的值起作用,但至少你可以表达更复杂的类型。
例如,您可以编写一个检查,专门查看某个值是否为有效的
MyDoc
,如下所示:编译器不会验证该函数的实现,但现在您可以在其他地方随意使用它:
或者,您可以编写一个更通用的保护,检查对象在给定键处是否具有定义的属性:
这种输入有点复杂,但让我们看看它的实际操作:
这是因为
doc
已经从{ field: string | undefined }
缩小到了可以赋值给MyDoc
的{ field: string | undefined } & { field: string}
。hasDefinedProp()
类型保护函数比isMyDoc()
有更多的潜在用例,所以根据在代码库中进行这种检查的频率,它可能更有用。但是,如果你对干净/清晰的代码感兴趣,最好的方法是重构,这样你就可以在开始变量别名之前做检查:
这种类型的重构并不总是对每个用例都可行,但是编译器和读者都将更容易理解这种明显的类型保护流。
Playground代码链接