用作类型断言
identifier! 从 identifier 的类型里去除了 null 和 undefined
function broken(name: string | null): string {
function postfix(epithet: string) {
return name.charAt(0) + '. the ' + epithet; // error, 'name' is possibly null
}
name = name || "Bob";
return postfix("great");
}
function fixed(name: string | null): string {
function postfix(epithet: string) {
return name!.charAt(0) + '. the ' + epithet; // ok
}
name = name || "Bob";
return postfix("great");
}
本例使用了嵌套函数,因为编译器无法去除嵌套函数的null(除非是立即调用的函数表达式)。 因为它无法跟踪所有对嵌套函数的调用,尤其是你将内层函数做为外层函数的返回值。 如果无法知道函数在哪里被调用,就无法知道调用时name的类型。
检查未调用的函数
一个常见且危险的错误是:忘记调用一个函数,特别是当该函数不需要参数,或者它的命名容易被误认为是一个属性而不是函数时。
interface User {
isAdministrator(): boolean;
notify(): void;
doNotDisturb?(): boolean;
}
// 之后…
// 有问题的代码,别用!
function doAdminThing(user: User) {
// 糟了!
if (user.isAdministrator) {
sudo();
editTheConfiguration();
}
else {
throw new AccessDeniedError("User is not an admin");
}
}
在这段代码中,我们忘了调用 isAdministrator,导致该代码错误地允许非管理员用户修改配置!
在 TypeScript 3.7 中,它会被识别成一个潜在的错误:
function doAdminThing(user: User) {
if (user.isAdministrator) {
// ~~~~~~~~~~~~~~~~~~~~
// error! This condition will always return true since the function is always defined.
// Did you mean to call it instead?
这个检查功能是一个破坏性变更,基于这个因素,检查会非常保守。 因此对这类错误的提示仅限于 if 条件语句中。当问题函数是可选属性、或未开启 strictNullChecks 选项、或该函数在 if 的代码块中有被调用,在这些情况下不会被视为错误:
interface User {
isAdministrator(): boolean;
notify(): void;
doNotDisturb?(): boolean;
}
function issueNotification(user: User) {
if (user.doNotDisturb) {
// OK,属性是可选的
}
if (user.notify) {
// OK,调用了该函数
user.notify();
}
}
如果你打算对该函数进行测试但不调用它,你可以修改它的类型定义,让它可能是 undefined/null,或使用 !! 来编写类似 if (!!user.isAdministrator) 的代码,表示代码逻辑确实是这样的。