详解Typescript 严格模式有多严格
简介
Typescript 自2.3版本开始引入了“严格模式”(Strict mode),它通过加强类型检查、禁用一些不安全的语法和行为等手段来让代码更规范、更健壮,从而减少意外的运行时错误。
在这篇文章中,我们将详细讲解 Typescript 严格模式的多个方面,并给出一些示例代码来进一步说明各个模式之间的区别。
严格模式开启方式
要开启 Typescript 严格模式,只需要在 tsconfig.json
文件中将 strict
选项设置为 true
即可。
{
"compilerOptions": {
"strict": true
}
}
这个选项会同时开启以下严格模式子选项:
noImplicitAny
strictNullChecks
strictFunctionTypes
strictBindCallApply
noImplicitThis
alwaysStrict
noImplicitAny
如果一个变量的类型没有被显式定义,并且 Typescript 也无法从上下文中推断出其类型,那么它的类型就会被默认设置为 any
,这样就没有类型检查的效果了。
举个例子:
function add(a, b) {
return a + b;
}
add(1, 2); // 3
add('hello', 1); // 'hello1'
这个例子中,add
函数中的参数和返回值都没有明确指定类型,因此它们的类型都被默认设置为 any
,这样就可以传入任意类型的参数了,显然这是不规范的。
在开启 noImplicitAny
选项后,编译器会对未显式定义类型的变量、函数参数和返回值进行报错,提示你应该明确它们的类型。
strictNullChecks
在 Javascript 中,变量默认是可以为 null
或 undefined
的,这常常成为一些难以定义变量类型的问题。
let foo;
console.log(foo.length); // TypeError: Cannot read property 'length' of undefined
在这个例子中,由于 foo
变量没有被初始化,它的值为 undefined
,那么当我们尝试对它进行字符串长度的访问时,就会报错。
开启 strictNullChecks
选项后,编译器会在类型检查时对这种行为进行报错,从而让代码更加健壮。
let foo: string | undefined;
console.log(foo.length); // Error: Object is possibly 'undefined'.
在这个例子中,我们将 foo
变量的类型显式设置为 string | undefined
,这意味着它可能是字符串类型,也可能是 undefined
,这样可以有效地避免了前面的报错。
strictFunctionTypes
函数是 Javascript 中的重要组成部分,Typescript 严格模式中对函数的类型匹配进行了强制验证,防止常见的隐式的类型转换,在开发过程中更容易发现错误。
type AddFn = (a: number, b: number) => number;
const add1: AddFn = (a, b) => a + b;
const add2: AddFn = (a: number, b: number) => `${a}${b}`;
add1(1, 2); // 3
add2(1, 2); // Error: Type '(a: number, b: number) => string' is not assignable to type 'AddFn'.
在这个例子中,我们定义了一个函数类型 AddFn
,它接收两个数字类型的参数,并返回一个数字类型的值。但如果我们将一个返回字符串类型的函数赋给 AddFn
类型的变量,编译器就会报错,从而让我们更早地发现这个错误。
strictBindCallApply
在 Javascript 中,bind
、call
、apply
方法能够改变函数的 this
值,有时候也会造成不必要的麻烦。
function foo(this: number) {
console.log(this);
}
foo(); // TypeError: this is undefined
foo.call('hello'); // TypeError: '"hello"' is not a number.
在这个例子中,我们定义了一个只能在数字上下文中调用的函数 foo
,如果它被单独调用,或者尝试在字符串上下文中调用,都会产生不规范的结果。
在开启 strictBindCallApply
选项后,编译器会对这种行为进行报错,从而让代码更加健壮。
function foo(this: number) {
console.log(this);
}
foo(); // Error: The 'this' context of type 'void' is not assignable to method's 'this' of type 'number'.
foo.call('hello'); // Error: Argument of type 'string' is not assignable to parameter of type 'number'.
在这个例子中,我们在函数定义时声明了它的 this
值类型为 number
,这样可以避免它被误用在非数字上下文中。另外,当我们尝试使用 call
方法时,编译器会报错提示参数类型不匹配的问题。
noImplicitThis
在一些 Javascript 中,函数内的 this
值可能是不确定的,这通常发生在事件回调函数、定时器函数等场景中,这个时候编译器可能无法判断函数内部 this
值的类型,使用 this
值时可能会因为类型问题产生错误。
class Button {
private onButtonClick() {
console.log(this.text);
}
constructor(public text: string) {
document
.querySelector('button')
.addEventListener('click', this.onButtonClick);
}
}
new Button('Hello'); // undefined
在这个例子中,我们想要将按钮点击事件的回调函数 onButtonClick
绑定到 Button
类的实例上,但实际上这个回调函数并没有被正确地绑定到当前类的实例上,这样就无法访问当前实例的 text
属性,从而导致出错。
在开启 noImplicitThis
选项后,编译器会在这种情况下发出警告,从而提示我们应该先针对 this
值类进行显式声明。
class Button {
private onButtonClick = () => {
console.log(this.text); // 这里的 this 就是 Button 类的实例
};
constructor(public text: string) {
document
.querySelector('button')
.addEventListener('click', this.onButtonClick);
}
}
new Button('Hello'); // 'Hello'
在这个例子中,我们将回调函数 onButtonClick
定义为一个箭头函数,并且在箭头函数中访问 this.text
属性,这样就避免了上述的问题。
alwaysStrict
开启 alwaysStrict
选项后,编译器会在生成的 Javascript 代码中插入 "use strict"
指令,这会强制执行 Javascript 严格模式,进一步减少代码中出现的一些不规范行为和错误。
示例代码:
// tsconfig.json
{
"compilerOptions": {
"alwaysStrict": true
}
}
// test.ts
function add(a: number, b: number) {
return a + b;
}
console.log(add(1, 2));
编译后的 Javascript 代码:
"use strict";
function add(a, b) {
return a + b;
}
console.log(add(1, 2));
结论
通过对上述几个严格模式进行逐一解释和对比,我们可以看到 Typescript 严格模式在加强类型检查、去除不规范语法和行为、提高静态分析等方面都做了各自的工作,使得开发工作更加规范、高效。在使用 Typescript 进行项目开发时,这些严格模式可以有效地降低错误率,提高代码质量。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Typescript 严格模式有多严格 - Python技术站