在这篇文章中,我们将探讨控制TypeScript中类型检查的严格性的编译器选项。
了解TypeScript严格模式
有一个TypeScript编译器选项叫做strict 。它开启了一系列的类型检查规则,被称为严格模式。这与JavaScript的严格模式是分开的。
当创建一个新的TypeScript项目时,建议打开严格模式,这样代码从一开始就能从最严格的类型检查中受益。然而,在将JavaScript代码库迁移到TypeScript时,严格模式可能并不可行,因为会产生大量的类型错误。
除了strict 选项,每个类型检查规则都可以通过特定的编译器选项来控制。这给了类型检查器的严格性以细粒度的控制。
理解noImplicitAny
noImplicitAny 编译器选项将在具有隐含类型的表达式和声明中引发错误any 。当strict 选项为true 时,该选项默认为true 。
下面的greet 方法目前违反了这个规则,因为prefix 参数被推断为具有any 类型:
public greet(prefix) {
...
}
我们可以将tsconfig.json 中的noImplicitAny 编译器选项设置为false 来消除类型错误:
{
"compilerOptions": {
...
"noImplicitAny": false
},
....
}
为了正确解决这个错误,我们将给prefix 参数一个类型注释:
public greet(prefix: string) {
...
}
理解noImplicitThis
noImplicitThis 编译器选项在引用this 的表达式上引发了一个错误,该表达式隐含了any 类型。当strict 选项为true 时,该选项默认为true 。
下面的goodbyeFunction 方法在访问this 中的name 属性时违反了这个规则:
class Person {
constructor(private name: string) {}
goodbyeFunction() {
return function() {
console.log(`Goodbye ${this.name}`);
};
}
}
TypeScript将this 推断为any 类型,因此引发了一个类型错误。
我们可以将tsconfig.json 中的noImplicitThis 编译器选项设置为false 来消除类型错误:
{
"compilerOptions": {
...
"noImplicitThis": false
},
....
}
有两种方法可以正确解决这个错误。
解决类型错误的第一个方法是让TypeScript知道函数签名中this 的类型:
goodbyeFunction() {
return function (this: Person) {
...
};
}
解决类型错误的第二种方法是使用一个箭头函数:
goodbyeFunction() {
return () => {
...
};
}
很好!
理解strictBindCallApply
strictBindCallApply 编译器选项可以对函数上的bind,call, 和apply 方法进行更严格的检查。当strict 选项为true 时,该选项默认为true 。
下面对person.calculatePrice 的调用违反了这个规则,因为calculatePrice 需要传入两个参数。函数类型是(discountCode: string, price: number) => number :
const discountedPrice = person.calculatePrice.apply(
undefined,
["FREE"]
);
我们可以将tsconfig.json 中的strictBindCallApply 编译器选项设置为false 来消除类型错误:
{
"compilerOptions": {
...
"strictBindCallApply": false
},
....
}
为了正确解决这个错误,我们应该在函数调用中传递第二个参数:
const discountedPrice = person.calculatePrice.apply(
undefined,
["FREE", 100]
);
了解strictNullChecks
strictNullChecks 编译器选项将null 和undefined 的值排除在每个类型的域之外。当strict 选项为true 时,该选项默认为true 。
在下面的代码中,将person 赋值给null 违反了这个规则,因为null 不在Person 类型之内:
let person = new Person();
...
person = null;
我们可以将tsconfig.json 中的strictNullChecks 编译器选项设置为false 来消除类型错误:
{
"compilerOptions": {
...
"strictNullChecks": false
},
....
}
为了更好地解决这个错误,我们可以在一个联合类型中把null 添加到person 的类型中:
let person: Person | null = new Person();
...
person = null;
了解strictFunctionTypes
strictFunctionTypes 禁用函数类型的双变量参数检查。当strict 选项为true 时,该选项默认为true 。
下面的getDiscountCode 函数包含一个类型错误,因为info 参数包含一个不属于APIRequestHandler 的token 属性:
type APIRequestHandler<T> = (info: { path: string }) => Promise<T>;
type APIRequestInfo = { path: string };
const getDiscountCode: APIRequestHandler<string> = async (
info: APIRequestInfo & { token: string }
) => { ... }
我们可以将tsconfig.json 中的strictFunctionTypes 编译器选项设置为false 来消除这个类型错误:
{
"compilerOptions": {
...
"strictFunctionTypes": false
},
....
}
解决这个类型错误并不直接。getDiscountCode 明确要求使用token属性。我们可以改变APIRequestHandler 的类型,但这可能会影响其他代码。一个解决方案是删除getDiscountCode 中的类型注释,让TypeScript推断其类型。
了解strictPropertyInitialization
strictPropertyInitialization 编译器选项确保非未定义的类属性在构造函数中被初始化。这个选项需要启用strictNullChecks 才能生效。当strict 选项为true 时,strictPropertyInitialization 默认为true 。
下面Person 类型内的name 属性目前违反了这一规则:
class Person {
public name: string;
}
我们可以将tsconfig.json 中的strictPropertyInitialization 编译器选项设置为false 来消除类型错误:
{
"compilerOptions": {
...
"strictPropertyInitialization": false
},
....
}
我们可以给name 赋予一个默认值来正确解决这个错误:
class Person {
public name: string = "";
}
了解alwaysStrict
alwaysStrict 编译器选项确保JavaScript严格模式被用于类型检查过程。它也决定了"use strict" 是否在JavaScript中被排放出来。当strict 选项为true ,该选项默认为true 。
当alwaysStrict 时,"use strict" 将在转置的JavaScript文件的顶部:
"use strict";
class Person ...
在JavaScript严格模式下,你不能像下面这样给一个变量命名为 "arguments":
let arguments;
我们可以将tsconfig.json 中的alwaysStrict 编译器选项设置为false 来消除类型错误:
{
"compilerOptions": {
...
"alwaysStrict": false
},
....
}
然而,正确解决JavaScript严格模式的错误是明智的。在这种情况下,我们会把这个变量重命名为其他的东西:
let args;
总结
TypeScript有一系列的选项来控制类型检查的严格程度:
strict- 设置最大的严格性,将下面所有的标志设置为 。truenoImplicitAny- 对于推断类型为 的表达式和声明,会引发错误。anynoImplicitThis- 在推断类型为 的 表达式上引发错误。anythisstrictBindCallApply- 启用对函数的 , , 和 方法的严格检查。bindcallapplystrictNullChecks- 意味着 和 的值在类型中是无效的值。nullundefinedstrictFunctionTypes- 禁用对函数类型的双变量参数检查。strictPropertyInitialization- 确保类属性被分配一个默认值或在构造函数中被初始化。alwaysStrict- 在JavaScript严格模式下解析代码,并在转译后的代码中发出 "use strict"。
对于新项目,建议将strict 设置为true 。
true 对于JavaScript迁移项目,如果相应的规则引发了大量的类型错误,可以将strict ,其他标志设置为false 。一个目标可以是解决类型错误,并随着时间的推移将所有的strict 标志设为true 。