详解 TypeScript 严格模式有多严格

作者:青柠

时间:2026-10-26

浏览:438

本文深入解析 TypeScript 严格模式(strict)的开启方式及其包含的七个核心配置项。通过 tsconfig.json 启用 strict 后,编译器将强制执行 noImplicitAny、noImplicitThis、strictNullChecks 等规则。文章详细阐述了各项规则对变量类型推断、this 上下文、空值处理、函数参数方差及属性初始化的具体限制,并提供相应的代码修复方案,帮助开发者编写更安全、高可读性的 TypeScript 代码。

文章目录

正文

TypeScript 由微软在 2012 年发布,作为 JavaScript 的超集,它引入了更完善的类型系统。与 JavaScript 类似,TypeScript 也提供了严格模式的支持。本文将详细介绍如何启用 TypeScript 的严格模式,并深入剖析该模式的具体限制与优势。

TypeScript 的所有配置均定义在项目根目录下的 tsconfig.json 文件中。开发者可以通过修改该文件来启用严格模式。

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

TypeScript 编译器提供了超过 90 种配置选项,其中包含 7 个专门针对严格模式的配置项。

  • noImplicitAny
  • noImplicitThis
  • alwaysStrict
  • strictBindCallApply
  • strictNullChecks
  • strictPropertyInitialization
  • strictFunctionTypes

在 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 被隐式推断为 any 类型。参考以下示例:

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" 指令,以告知浏览器当前代码处于严格模式。

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 赋值给非这两种类型的值,同时也禁止将其他类型的值赋值给 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 可能为 undefined,代码仍可编译通过。要修复此问题,需为编译代码添加 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 | undefined
  • B:null 检查条件调用
// A
const logBookName = (book: Book | undefined) => {
    if (book) {
        console.log(book.name);
    }
    else {
        console.log('not book');
    }
}
// B
if (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 默认情况下参数是双向协变的(bivariant),即既可以是协变的,也可以是逆变的。方差是理解子类型关系的一种方法。当参数为协方差时,可以将特定类型分配给更广泛的类型(例如将子类型分配给超类型)。逆变则相反:可以将更广泛的类型分配给特定类型(例如将超类型分配给子类型)。

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

上述代码在运行时并未提示错误,因为默认情况下参数采用双向协变比较。超类型 getAnimalName 和子类型 getDogName 的方法可以相互分配。启用严格模式后,TypeScript 将对参数进行逆变比较。

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

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

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

在此例中,getAnimalName 是比 getDogName 更广泛的函数。因此,在这种情况下,无法将超类型分配给子类型。但是,可以将子类型分配给超类型。通常情况下,函数参数应为逆变而非双向协变。启用严格模式后,TypeScript 将不再将函数参数视为双向协变。

以上就是详解 TypeScript 严格模式有多严格的详细内容,更多关于 TypeScript 严格模式的资料请关注本站其它相关文章!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章