首页>国内 > 正文

TypeScript 严格模式有多严格?

2023-01-29 13:20:09来源:前端充电宝

大家好,我是 CUGGZ。

TypeScript 是微软于 2012 年推出的一门语言,它是 JavaScript 的超集,具有更强的可选类型系统。TypeScript 和 JavaScript 一样是有严格模式的,今天就来看看 TypeScript 中的严格模式如何开启,以及它到底有多严格!


(资料图)

TypeScript 的配置项都写在项目根目录名为tsconfig.json的配置文件中,可以通过以下方式来开启严格模式:

{  ...  "compilerOptions": {    "strict": true,    ...  },  ...}

TypeScript 编译器有超过 90 个不同的配置项。其中 7 个是关于严格模式的:

noImplicitAnynoImplicitThisalwaysStrictstrictBindCallApplystrictNullChecksstrictPropertyInitializationstrictFunctionTypes

当在tsconfig.json中开启严格模式之后,就相当于开启了这些配置:

{  ...  "compilerOptions": {    "noImplicitAny": true,    "noImplicitThis": true,    "alwaysStrict": true,    "strictBindCallApply": true,    "strictNullChecks": true,    "strictFunctionTypes": true,    "strictPropertyInitialization": true,    ...  }  ...}

下面就来分别看一下这些选项都有什么含义。

noImplicitAny

此规则不允许变量或函数参数具有隐式any类型。来看下面的例子:

const add10 = number number + 10;

当启用了严格模式时,函数参数 number 就报错了:

参数“number”隐式具有“any”类型。ts(7006)

要想修复这个报错,只需给参数或变量显式指定类型:

const add10 = (number: number) => number + 10;

因此 noImplicitAny 规则可以确保代码更具可读性。否则,add10函数的调用者需要推断参数是一个数字,那使用 TypeScript 还有什么意义呢?

noImplicitThis

此规则不允许this隐式定义上下文。来看下面的例子:

class Person {  weight: number;  height: number;   constructor(weight: number, height: number) {    this.weight = weight;    this.height = height;  }   getBodyMassIndex() {    return function () {      return this.weight / (this.height * this.height);    };  }}

当启用了严格模式时,getBodyMassIndex中的this就报错了:

"this" 隐式具有类型 "any",因为它没有类型注释。ts(2683)

解决这个问题的方法就是使用箭头函数,因为箭头函数使用其父级的执行上下文:

class Person {  weight: number;  height: number;   constructor(weight: number, height: number) {    this.weight = weight;    this.height = height;  }   getBodyMassIndex() {    return () {      return this.weight / (this.height * this.height);    };  }}

alwaysStrict

此规则指定始终以严格模式检查每个模块,并且在编译之后的 JavaScript 文件中加入"use strict",用来告诉浏览器该 JavaScript 为严格模式。

ECMAScript 严格模式是在 ES5 中引入的,它只是向编译器提示代码应该以严格模式执行,使用严格模式可以使代码更以更安全、高效的方式运行。

strictBindCallApply

此规则可以确保使用具有正确参数的call()、bind()和apply()函数。来看下面的例子:

const logNumber = (x: number) => {  console.log(`number:${x}`)}logNumber.call(undefined, "10"); //

当启用了严格模式时,getBodyMassIndex中的this就报错了:

类型“string”的参数不能赋给类型“number”的参数。ts(2345)

当遇到这种报错时,只需检查函数调用的参数,并使用正常的方式调用:

logNumber.call(undefined, 10); // number:10

strictNullChecks

此规则使得 null和 undefined 值不能赋值给非这两种类型的值,别的类型的值也不能赋给它们。除了 any 类型,还有个例外就是 undefined 可以赋值给 void 类型。这个选项可以帮助 Uncaught TypeError 错误。来看下面的例子:

interface Book {    name: string;    author: string;  }  const books: Book[] = [ {name: "Test1", author: "Max"} ];const getBookByAuthor = (author: string) => books.find((book) => book.author = author);const book = getBookByAuthor("John");console.log(book.name);

当启用了严格模式时,打印book.name时就报错了:

对象可能为“未定义”。ts(2532)

如果未开启严格模式,即使book.name可能未定义,此代码也会编译。想要修复这个问题,就需要为要编译的代码添加 null 检查:

interface Book {  name: string;  author: string;}  const books: Book[] = [ {name: "Test1", author: "Max"} ];const getBookByAuthor = (author: string) => books.find((book) => book.author = author);const book = getBookByAuthor("John");if (book) {  console.log(book.name);} else {  console.log("Book not found");}

函数中也是一样的,来看下面的例子:

interface Book {    name: string;    author: string;  }  const books: Book[] = [ {name: "Test1", author: "Max"} ];const getBookByAuthor = (author: string) => books.find((book) => book.author = author);const book = getBookByAuthor("John");const logBookName = (book: Book) => {    console.log(book.name);}logBookName(book);

如果启用了严格模式时,调用logBookName(book);时就会报错:

类型“Book | undefined”的参数不能赋给类型“Book”的参数。  不能将类型“undefined”分配给类型“Book”。ts(2345)

这里提供两种解决方案:

A:将logBookName函数参数类型设置为Book | undefinedB:null检查条件调用

// Aconst logBookName = (book: Book | undefined) => {    if (book) {        console.log(book.name);    }    else {        console.log("not book");    }}// Bif (book) {    logBookName(book);}

使用该规则时,可以强制开发人员编写具有更好类型描述的代码。

strictPropertyInitialization

此规则将强制在构造函数中初始化所有属性值。来看下面的例子:

class User {    name: string;    age: number;    occupation: string | undefined;       constructor(name: string) {        this.name = name;    }}

在上面的代码块中有一个User类,constructor()方法是初始化其实例属性的地方。当实例化一个类对象时,JavaScript 会自动调用constructor()方法。Typescript 要求要么初始化定义的属性,要么指定一个undefined类型。因此,当编译上面的代码时,将会提示以下错误:

属性“age”没有初始化表达式,且未在构造函数中明确赋值。ts(2564)

对于上面的代码,可以这样改:

// A:指定 undefined 类型class User {    name: string;    age: number | undefined;    occupation: string | undefined;    constructor(name: string) {        this.name = name;    }}// B:初始化定义的属性class User {    name: string;    age: number;    occupation: string | undefined;    constructor(name: string, age: number) {        this.name = name;        this.age = age;    }}

strictFunctionTypes

此规则会更彻底地检查函数参数。Typescript 参数默认是双向协变的,这意味着它们既可以是协变的,也可以是逆变的。方差是一种深入了解子类型关系的方法。当参数是协方差时,我们可以将特定类型分配给更广泛的类型(例如将子类型分配给超类型)。逆变是相反的:可以将更广泛的类型分配给特定类型(例如将超类型分配给子类型)。

// 非严格模式interface Animal {  name: string;}interface Dog extends Animal {  breeds: Array;}let getDogName = (dog: Dog) => dog.name;let getAnimalName = (animal: Animal) => animal.name;getDogName = getAnimalName;  // OkgetAnimalName = getDogName;  // Ok

上面的代码运行时并没有提示错误,默认情况下参数是双向协变比较的。超类型getAnimalName和子类型getDogName的方法可以相互分配。当开启严格模式之后,则 TypeScript 的参数进行逆变比较。

// 严格模式interface Animal {  name: string;}interface Dog extends Animal {  breeds: Array;}let getDogName = (dog: Dog) => dog.name;let getAnimalName = (animal: Animal) => animal.name;getDogName = getAnimalName; // OkgetAnimalName = getDogName; // Error

当开启严格模式时,最后一行将报以下错误:

不能将类型“(dog: Dog) => string”分配给类型“(animal: Animal) => string”。  参数“dog”和“animal” 的类型不兼容。    类型 "Animal" 中缺少属性 "breeds",但类型 "Dog" 中需要该属性。ts(2322)

这里,getAnimalName是比getDogName更广泛的函数。因此,在这种情况下,无法将超类型分配给子类型。但是,可以将子类型分配给超类型。大多数时候,函数参数应该是逆变的,而不是双向协变的。如果开启严格模式,Typescript 将不会将函数参数视为双向协变。

关键词: 构造函数 可以确保 所有属性

相关新闻

Copyright 2015-2020   三好网  版权所有 联系邮箱:435 22 640@qq.com  备案号: 京ICP备2022022245号-21