identifier! 从 identifier 的类型里去除了 null 和 undefined
function broken(name: string | null): string { function postfix(epithet: string) { return name.charAt(0) + '. the ' + epithet; // 错误, 'name' 可能是 null } name = name || "Bob"; return postfix("great"); } function fixed(name: string | null): string { function postfix(epithet: string) { return name!.charAt(0) + '. the ' + epithet; // 正确 } 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) 的代码,表示代码逻辑确实是这样的。