TypeScript 知识点小结
本文首发于公众号【龙猫研习社】mp.weixin.qq.com/s/4ndWSn9iX…
最近给团队系统性地培训了 TypeScipt,本文简单地对工作中常见知识点进行了总结,以便大家随时翻阅巩固。
类型系统
TypeScipt 的类型系统主要包含以下几个类别:
- string、number、boolean、null、undefined、symbol、Array、Object;
- unknown、any;
- never(永不返回);
- interface(接口类型)、type(类型别名);
- unions(联合类型)、intersection(交叉类型);
- enum(枚举类型)。
其中 string、number、boolean、null、undefined、symbol、Array、Object 与 JavaScript 中的用法相同,不做过多阐述,下面对其它几个类别进行简单介绍。
未知类型
有些情况下某个变量在编译期无法确定其类型,此时可以使用 unknown
或 any
对该变量进行类型声明,关于这两个类型的差异,先看下面的例子:
let value1: unknown;
value1 = 12;
value1.toFixed();
let value2: any;
value2 = 12;
value2.toFixed();
我们定义了 value1
和 value2
两个变量,其中 value1
为 unknown
类型、value2
为 any
类型,然后分别对其进行赋值,接着调用 toFixed
方法,如果编译上述代码,编译器会针对 value1.toFixed()
语句抛出 Property 'toFixed' does not exist on type 'unknown'
的异常,这是由于相对于 any
,TypeScript 编译器会对 unknown
类型的变量进行更为严格的类型检查,我们只需要手动检查 value1
的实际类型,即可修复该问题:
let value1: unknown;
value1 = 12;
if (typeof value1 === 'number') {
value1.toFixed();
}
上述代码中,在执行 value1.toFixed()
语句之前,先对 value1
是否为 number
类型进行了判断,毕竟只有 number
对象才拥有 toFixed
方法,这相对于 any
类型来说,更好地保证了代码的安全性及行为的可预期性。
根据上面的讨论,建议大家在遇到未知类型时优先考虑使用 unknown
,不到万不得已不要使用 any
。
永不返回
当某个函数或方法是个死循环或直接抛出异常时,可将返回值设置为 never
类型来表示永不返回,该类型主要用在泛型编程中,比如下面的例子:
function eventLoop(): never {
while (true) {
//do something...
}
}
function raiseError(message: string): never {
throw new Error(message);
}
接口与类型别名
通过 interface
或 type
可以定义各种复杂的类型,其用法参见 www.typescriptlang.org/docs/handbo… 和 www.typescriptlang.org/docs/handbo… ,此处不做过多说明,唯一要注意的这两者之间的区别:
- 重复定义的
interface
,它的属性可叠加,该特性使得我们可以极其方便地对全局变量、第三方库的类型做扩展; type
可为string
、number
等基本类型定义别名;type
可用来声明联合、元组(即元素类型不相同的数组)类型。
对于第一条,我们看下面的例子:
interface Person {
name: string;
}
interface Person {
age: number;
}
上述代码中的类型声明等同于:
interface Person {
name: string;
age: number;
}
联合与交叉
联合类型表示变量、参数的类型不是单一的原子类型,而可能多种不同类型的组合;比如下面的例子:
type UnionType = string | number; // 联合类型声明
let value: UnionType;
上例中,我们定义了联合类型 UnionType
以及类型为 UnionType
的变量 value
,此时能够将 string
、number
或 UnionType
类型的值赋予变量 value
。
交叉类型表示把多个类型合并成一个类型,合并后的类型将拥有所有成员类型的特性;比如下面的例子:
interface Name {
name: string;
}
interface Age {
age: number;
}
type Person = Name & Age; // 交叉类型声明
上例中,我们定义了交叉类型 Person
,该类型将拥有类型 Name
和 Age
的所有属性。
枚举
有些情况下我们只需要知道变量是否为某一类型(比如页面中的各种事件类型)而不关心它实际的值,在 JavaScript 中通常定义一堆常量来达到该 目的,在 TypeScript 中我们可以使用枚举来完成相关需求。比如下面的例子:
enum EventType {
Click, // 0
MouseUp, // 1
MouseLeave, // 2
}
enum EventType {
Click = 1, // 1
MouseUp, // 2
MouseLeave, // 3
}
enum EventType {
Click, // 0
MouseUp = 2, // 2
MouseLeave, // 3
}
枚举中每一项的赋值规则如下:
- 如果第一项没被指定具体的值,那么第一项的值为
0
,否则为指定的值; - 如果后续项没被指定具体的值,那么该值在前一项的值的基础上加
1
,否则为指定的值。
枚举中每一项值的类型为 string
或 number
,因此下面的例子都是合法的:
enum EventType {
Click = 1,
MouseUp,
MouseLeave = 'MouseLeave',
}
不过需要注意的是,如果某一项值为 string
类型,必须手动为该项的下一项赋值,比如下面的例子:
// 不合法
enum EventType {
Click = 'Click',
MouseUp,
MouseLeave,
}
// 合法
enum EventType {
Click = 'Click',
MouseUp = 1,
MouseLeave,
}
之所以有这个规定是因为编译器无法或者不知道如何为 string
做递增操作。
泛型编程
所谓泛型编程,其本质就是将类型参数化,最终目的是进行类型抽象,即将多个类型中具有某一共同行为的逻辑进行抽象,以达到代码重用的目的。在 TypeScript 中,我们可以定义泛型方法
、泛型类
和 泛型类型
,比如下面的例子:
// 泛型方法
function doSomething<A>(arg: A): A {
return arg;
}
// 泛型类
class Memory<S> {
store: S;
constructor(store: S) {
this.store = store;
}
set(store: S) {
this.store = store;
}
get() {
return this.store;
}
}
// 泛型类型
type ReflectFuncton = <P>(param: P) => P;
interface ReflectFuncton {
<P>(param: P): P
}
interface ReflectFuncton<P> {
(param: P): P
}
类型约束
默认情况下,所指定的类型参数 P
是 any
的子类型(关于父子类型的讨论,参见笔者的另一篇文章 TypeScript 类型兼容性——父子类型),我们可通过 extend
关键字来修改泛型参数 P
的父类型,以便对泛型参数的行为进行约束,比如下面的例子:
function doSomething<P extends number>(arg: P) {
//do something...
}
上例中,我们指定了 doSomething
的类型参数 P
的类型只能是 number
的子类型,如果给该函数传入字符串等其它类型,那么编译器将抛出异常。
其它
在 TypeScript 泛型编程中,我们还会遇到 分配条件类型
、infer
、变型
,这些概念在笔者的其它文章中均已讨论过,故此处不再重述,具体详情参看下面链接:
类型守卫
在 TypeScript 中,如果遇到 typeof
、instanceof
、in
、==
、===
、!=
、!==
关键字时,它会在语句的块级作用域内将相关变量的类型缩小
为具体的类型,以便减少不必要的类型断言及提高程序的安全性.
typeof
该操作符主要用于获得变量类型:
const convert = (c: number | string) => {
if (typeof c === 'number') {
return c.toFixed();
}
return c.toLowerCase();
}
instanceof
该操作符主要用于判断变量是否是某个类的实例:
class Dog {
wang = 'wangwang';
}
class Cat {
miao = 'miaomiao';
}
const getName = (animal: Dog | Cat) => {
if (animal instanceof Dog) {
return animal.wang;
}
return animal.miao;
}
in
该操作符主要用于判断变量是否为某个对象的属性:
interface Dog {
wang: string;
}
interface Cat {
miao: string;
}
const getName = (animal: Dog | Cat) => {
if ('wang' in animal) {
return animal.wang;
}
return animal.miao;
}
等值判断
主要包含 ==
、===
、!=
和 !==
操作符,主要用于判断变量是否为某个字面值:
const getName = (animal: 'Dog' | 'Cat') => {
if (animal === 'Dog') {
return 'wangwang';
}
return 'miaomiao';
}
is
除了上述操作符外,我们也可以使用 is
关键字自定义类型守卫:
function isDog(animal: Dog | Cat): animal is Dog {
return 'wang' in animal;
}
const getName = (animal: Dog | Cat) => {
if (isDog(animal)) {
return animal.wang;
}
return animal.miao;
}
上例中我们定义了一个类型守卫 isDog
,它其实就是一个函数,只不过返回值是 {参数名} is {类型}
的格式,通过该方式,我们可以实现一套符合自己业务属性的类型守卫系统,以达到提高效率及程序安全性的目的。
类型兼容
相信大家都遇到过不同类型变量之间相互赋值而引起的各种问题,不同的类型之间,为什么有的可以相互赋值,有的则不行,这主要是由 TypeScript 的类型兼容系统决定的,掌握了 TypeScript 的类型兼容系统,我们才能从容应对上述问题,关于 TypeScript 的类型兼容系统,请参见笔者的另一篇文章 TypeScript 类型兼容性,此处不再阐述。
类型元编程
元编程能力的强弱在笔者心里始终是衡量一个编程语言好不好玩的第一标准,TypeScript 为我们提供了强大的类型元编程能力,通过它我们可以构建各种自定义类型来应对各种复杂的场景,关于 TypeScript 的类型元编程,请参见笔者的另一篇文章 TypeScript 类型编程,此处不再阐述。
类型声明
由于历史原因,很多第三方库并无类型声明文件(即 .d.ts
文件,作用类似于 C/C++ 中的头文件),如在使用过程中想要对这些第三方库进行类型检查,可手动为这些库添加类型声明,本节便对其进行简单介绍。
变量
变量类型声明只需在变量声明的前面加上 declare
关键字即可:
declare let value1: number;
declare const value2: boolean;
上例的意义是:
- 只能将
number
类型的值赋予value1
; value2
的类型是boolean
,且value2
是只读的(即无法给该变量赋予任何新的值)。
函数
函数类型声明与函数声明一样,只不过需要在 function
前加上 declare
关键字,且无实现体:
declare function doSomething(x: number);
类
类的类型声明与类声明一样,只不过需要在 class
前加上 declare
关键字,且类中的方法(包含静态方法、构造方法、实例方法)无实现体:
declare class Person {
public name: string;
private age: number;
constructor(name: string);
getAge(): number;
}
枚举
枚举类型声明只需在枚举声明的前面加上 declare
关键字即可:
declare enum Direction {
Up,
Down,
Left,
Right,
}
模块
模块声明常用来对具有很多变量、函数、类、枚举等子属性的模块(一个模块即对应一个 JS
文件)或文件(比如图片)进行类型声明:
// tom.d.ts
declare module 'tom' {
export function doSomething(money: number);
}
// jpg.d.ts
declare module '*.jpg' {
const src: string;
export default src;
}
上例中对模块 tom
和后缀为 jpg
的文件进行了类型声明,且在各自内部通过 export
导出了可供外部访问的成员,完成声明后下面的代码便能正常编译:
// index.ts
import { doSomething } from 'tom';
import bg from './bg.jpg';
命名空间
命名空间与模块类似,都是用来对具有很多变量、函数、类、枚举等子属性的对象进行类型声明,只不过命名空间主要用于对全局对象进行类型声明,比如下面的例子:
declare namespace $ {
const version: number;
function ajax(settings?: any): void;
}
命名空间与模块还有一点差异是在命名空间中声明的子属性都能被外部访问到,而无需 export
(也不支持)。
三斜线指令
在 .d.ts
文件中,会经常看到类似下面的代码:
// /root/src/index.d.ts
/// <reference no-default-lib="true"/>
/// <reference path="./types.d.ts" />
/// <reference types="lodash" />
/// <reference lib="es5" />
上例中由 ///
开头的语句便是所谓的三斜线指令
,其中各指令的用途如下所述:
-
no-default-lib
:用于将类型声明文件标记为默认库(常见于lib.d.ts
),用于指示编译器在编译时不包含默认库(即lib.d.ts
); -
path
:常用于引入自定义的类型声明文件,根据指定的路径加载类型声明文件; -
types
:常用于引入第三方库的类型声明文件,其加载顺序如下(此处假设types
的值为lodash
):/root/src/node_modules/@types/lodash/index.d.ts
/root/src/node_modules/@types/lodash/package.json
(加载package.json
中types
属性指定的文件)/root/src/node_modules/lodash/index.d.ts
/root/src/node_modules/lodash/package.json
(加载package.json
中types
属性指定的文件)/root/node_modules/@types/lodash/index.d.ts
/root/node_modules/@types/lodash/package.json
(加载package.json
中types
属性指定的文件)/root/node_modules/lodash/index.d.ts
/root/node_modules/lodash/package.json
(加载package.json
中types
属性指定的文件)/node_modules/@types/lodash/index.d.ts
/node_modules/@types/lodash/package.json
(加载package.json
中types
属性指定的文件)/node_modules/lodash/index.d.ts
/node_modules/lodash/package.json
(加载package.json
中types
属性指定的文件)
-
lib
:常用于引入 TypeScript 内置库的类型声明文件。
在使用三斜线指令
时需要注意以下两点:
- 指令必须位于文件的最顶部;
- 指令前如有注释,只能是单行注释或多行注释。
除了上述指令外,还有 /// <amd-module />
和 /// <amd-dependency />
指令,由于不常用,故此不再阐述,相关详情可参见官网 www.typescriptlang.org/docs/handbo…。
编译配置
本文仅对 tsconfig.json
中的常用配置进行说明,完整信息可查看 www.typescriptlang.org/tsconfig。
target
- 用途:指定编译目标。
- 可用值:ES3、ES5、ES6、ES7、ES2016、ES2017、ES2018、ES2019、ES2020、ES2021、ESNext。
- 默认值:ES3。
module
- 用途:指定所使用的模块系统。
- 可用值:ES6、ES2015、ES2020、ESNext、CommonJS、UMD、AMD、System、None。
- 默认值:target 配置值为 ES3、ES5 时为 CommonJS,ES6 或更高版本时为 ES6。
moduleResolution
- 用途:指定模块解析策略。
- 可用值:Classic、Node。
- 默认值:module 配置值为 AMD、UMD、System、ES6 时为 Classic,其它情况为 Node(模拟 Node.js 的加载策略)。
这里模块解析主要作用是告诉 TypeScript 编译器在引入其它模块时如何加载该模块,下面我们便对不同的加载策略进行简单介绍。
相对路径引用
假设在 /root/src/folder/moduleA.ts
中以相对路径的形式引入 moduleB
(即 import { b } from "./moduleB"
),那不同策略下的加载逻辑如下:
Classic
该模式下,TypeScript 将根据以下顺序进行加载:
/root/src/folder/moduleB.ts
/root/src/folder/moduleB.d.ts
Node
该模式下,TypeScript 将根据以下顺序进行加载:
/root/src/moduleB.ts
/root/src/moduleB.tsx
/root/src/moduleB.d.ts
/root/src/moduleB/package.json
(加载package.json
中types
属性指定的文件)/root/src/moduleB/index.ts
/root/src/moduleB/index.tsx
/root/src/moduleB/index.d.ts
直接引用
假设在 /root/src/folder/moduleA.ts
中引入的模块 moduleB
不包含路径信息(即 import { b } from "moduleB"
),那不同策略下的加载逻辑如下:
Classic
该模式下,TypeScript 将根据以下顺序进行加载:
/root/src/folder/moduleB.ts
/root/src/folder/moduleB.d.ts
/root/src/moduleB.ts
/root/src/moduleB.d.ts
/root/moduleB.ts
/root/moduleB.d.ts
/moduleB.ts
/moduleB.d.ts
Node
该模式下,TypeScript 将根据以下顺序进行加载:
/root/src/node_modules/moduleB.ts
/root/src/node_modules/moduleB.tsx
/root/src/node_modules/moduleB.d.ts
/root/src/node_modules/moduleB/package.json
(加载package.json
中types
属性指定的文件)/root/src/node_modules/@types/moduleB.d.ts
/root/src/node_modules/moduleB/index.ts
/root/src/node_modules/moduleB/index.tsx
/root/src/node_modules/moduleB/index.d.ts
/root/node_modules/moduleB.ts
/root/node_modules/moduleB.tsx
/root/node_modules/moduleB.d.ts
/root/node_modules/moduleB/package.json
(加载package.json
中types
属性指定的文件)/root/node_modules/@types/moduleB.d.ts
/root/node_modules/moduleB/index.ts
/root/node_modules/moduleB/index.tsx
/root/node_modules/moduleB/index.d.ts
/node_modules/moduleB.ts
/node_modules/moduleB.tsx
/node_modules/moduleB.d.ts
/node_modules/moduleB/package.json
(加载package.json
中types
属性指定的文件)/node_modules/@types/moduleB.d.ts
/node_modules/moduleB/index.ts
/node_modules/moduleB/index.tsx
/node_modules/moduleB/index.d.ts
esModuleInterop
- 用途:默认情况下,TypeScript 像 ESModule 模块一样对待 CommonJS / AMD / UMD,但此时的 TypeScript 代码转移会导致不符合 ESModule 模块规范。开启该属性后,这些问题都将得到修复。
- 可用值:true、false(设置为
true
时,需同步开启allowSyntheticDefaultImports
)。 - 默认值:false。
为了更加直观的理解该属性,看下面的例子:
// tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "commonjs"
},
"include": [
"./src",
],
"exclude": [
"node_modules"
]
}
// src/index.js
import fs from "fs";
fs.readFileSync("file.txt", "utf8");
执行 npx tsc
命令,TypeScript 编译器将抛出 Module '"fs"' has no default export
异常;之所以抛出这个异常,是因为 CommonJS
中没有 export default
这个东西,为了能够正确的引入 CommonJS
模块,需要将 esModuleInterop
设置为 true
,再次执行 npx tsc
命令后查看生成的 JS 文件,会发现 TypeScript 会通过辅助函数 __importDefault
动态为 CommonJS
模块设置了 default
属性。
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
fs_1.default.readFileSync("file.txt", "utf8");
需要注意的时,该属性仅在 module
属性值为 CommonJS
、UMD
或 AMD
才有意义。
allowSyntheticDefaultImports
- 用途:是否允许合成默认导出。
- 可用值:true、false。
- 默认值:module 值为 System,或 esModuleInterop 为 true 及 module 的值不为 ES6、ESNext 时为 true,其它情况为 false。
通过前面的讨论可知,如果因导入了 CommonJS 模块而导致编译抛出了 Module '"xxx"' has no default export
异常,此刻需要将 esModuleInterop
设置为 true
以便编译器能够通过辅助函数 __importDefault
动态为 CommonJS 模块设置 default 属性。然而有些时候我们可能希望编译器忽略此类错误且不做除类型检查外的任何处理,比如下面的例子:
const allFunctions = {};
module.exports = allFunctions;
module.exports.default = allFunctions;
上例中通过设置 module.exports.default
为 CommonJS 模块设置 default
导出,如果我们引入这样的 CommonJS 模块,便可将 allowSyntheticDefaultImports
设置为 true
以让编译器忽略此类错误且不做除类型检查外的任何处理。
incremental
- 用途:是否启动增量编译,开启后会将上次编译的工程图信息保存到磁盘上的文件中。
- 可用值:true、false。
- 默认值:当 composite 为 true 时为 true,否则为 false。
composite
- 用途:用于项目之间的引用。
- 可用值:true、false。
- 默认值:false。
假设有如下的项目结构:
├── src/
│ └── index.ts
│ └── tsconfig.json
├── test/
│ ├── index.spec.ts
│ └── tsconfig.json
└── tsconfig.json
上例需要满足以下两个需求:
- 测试环境下
test
依赖于src
; - 生产环境下仅对
src
目录下的代码进行编译。
可通过以下方式进行编译配置:
-
在根目录下创建
tsconfig.json
并设置公共的编译选项; -
在
src
目录下创建tsconfig.json
,继承根目录下的配置,并将composite
属性设置为true
:{ "extends": "../tsconfig.json", "compilerOptions": { "composite": true } }
-
在
test
目录下创建tsconfig.json
,继承根目录下的配置,并设置references
属性的值:{ "extends": "../tsconfig.json", "compilerOptions": { "references": [ { "path": "../src" } ] } }
通过上面的配置,我们以还算优雅的形式处理了多个相互依赖项目间的编译配置组织问题,不过需要注意的是,在运行测试之前,需要通过 tsc --build src
先行构建 test
的依赖项(此处为 src
)。
declaration
- 用途:是否为项目中的 TypeScript 或 JavaScript 文件生成 .d.ts 文件。
- 可用值:true、false。
- 默认值:false。
sourceMap
- 用途:是否生成 source map,这些文件允许调试器和其他工具在使用实际生成的 JavaScript 文件时,显示原始的 TypeScript 代码;生成的文件以 .js.map(或 .jsx.map)文件的形式被生成到与 .js 文件相对应的同一个目录下。
- 可用值:true、false。
- 默认值:false。
sourceRoot
- 用途:指定调试器需要定位的 TypeScript 文件根路径。
- 可用值:字符串。
- 默认值:无。
mapRoot
- 用途:用来指定调试器需要定位的 source map 文件根路径。
- 可用值:字符串。
- 默认值:无。
inlineSourceMap
- 用途:开启该选项时,将不会生成 .js.map 文件,而是将 source map 文件内容生成内联字符串写入对应的 .js 文件中;开启该选项会生成较大的 js 文件,但是在不支持 .map 调试的环境下将会很方便。
- 可用值:true、false。
- 默认值:false。
inlineSources
- 用途:开启该选项时,将会把源文件的所有内容生成内联字符串并写入 source map 中(作用等同于 inlineSourceMap)。
- 可用值:true、false。
- 默认值:false。
lib
- 用途:更细粒度地控制代码运行时的库定义文件,如果属性 noLib(默认为 false) 设置为 true,将忽略该属性。
- 可用值:详情参见 www.typescriptlang.org/tsconfig#li…。
- 默认值:依赖于 target 属性的值,详情参见 www.typescriptlang.org/tsconfig#li…。
baseUrl
- 用途:解析非绝对路径模块时的基准目录。
- 可用值:字符串。
- 默认值:
tsconfig.json
所在目录。
paths
- 用途:将模块路径重新映射到相对于 baseUrl 定位的其他路径配置;相当于 webpack 中的 alias 设置;该配置仅在类型检测时有效。
- 可用值:键值对对象。
- 默认值:无。
outDir
- 用途:输出目录,即指定编译后的 JavaScript 文件的存放目录。
- 可用值:字符串。
- 默认值:无。
rootDir
- 用途:指定输出目录的根目录。
- 可用值:字符串。
- 默认值:
tsconfig.json
所在目录。
比如下面的例子:
├── src
│ ├── index.ts
在配置 outDir
为 lib
的情况下进行编译,生成的目录结构如下:
└── lib
├── src
| ├── index.js
查看编译结果会发现 lib
下多了一层 src
,这或许不符合我们的预期,为此可将 rootDir
设置为 src
后再次进行编译:
└── lib
├── index.js
此时,生成的目录结构符合了我们的预期(即不包含 src
这一层)。
rootDirs
- 用途:指定多个目录作为根目录。这将允许编译器在这些
虚拟
目录中解析相对应的模块导入,就像它们被合并到同一目录中一样。 - 可用值:字符串数组。
- 默认值:无。
比如下面的例子:
src
└── utils.ts
└── view.ts
views
└── render.ts
{
"compilerOptions": {
"rootDirs": ["src", "views"]
}
}
上例中,我们可以:
- 在
src/view.ts
中以import Render from "./render"
的形式加载views/render.ts
中的内容; - 在
views/render.ts
中以import Utils from "./utils"
的形式加载views/utils.ts
中的内容。
需要注意的是,该配置仅在类型检测时有效,build 时还需要将相关文件放在同一个目录。
typeRoots
- 用途:指定类型文件的根目录。
- 可用值:字符串数组。
- 默认值:默认会在 node_modules/@types 下查找类型文件;如果设置了该属性,则仅会从指定的目录里查找类型文件。
types
- 用途:指定包含在编译过程中的类型包。
- 可用值:字符串数组。
- 默认值:在默认情况下,所有的 typeRoots 包都将被包含在编译过程中;如果设置了该属性,只有列出的包才会被包含在全局范围内。
forceConsistentCasingInFileNames
- 用途:TypeScript 对文件的大小写是敏感的,如果有一部分的开发人员在大小写敏感的系统开发,而另一部分的开发人员在大小写不敏感的系统开发,则可能会出现问题;开启此选项后,如果开发人员正在使用和系统不一致的大小写规则,则会抛出错误。
- 可用值:true、false。
- 默认值:false。
include
- 用途:指定需要包括在 TypeScript 项目中的文件或文件匹配路径。
- 可用值:glob 模式字符串数组。
- 默认值:如果指定了 files 配置项,默认值为 [],否则默认值为 ["**/*"]。
exclude
- 用途:用来指定解析 include 配置中需要跳过的文件或者文件匹配路径;只会改变 include 配置项中的结果。
- 可用值:glob 模式字符串数组。
- 默认值: ["node_modules", "bower_components", "jspm_packages"]。
files
- 用途:用来指定 TypeScript 项目中需要包含的文件列表;如果项目非常小,可使用 files 指定项目的文件,否则更适合使用 include 指定项目文件。
- 可用值:字符串数组。
- 默认值: []。
extends
- 用途:声明当前配置需继承的另外一个配置的路径;TypeScript 首先会加载 extends 中指定的配置文件,然后使用当前的 tsconfig.json 文件里的配置覆盖继承的文件里的配置。
- 可用值:字符串。
- 默认值: 无。
strict
- 用途:是否开启严格模式,开启严格模式后,TypeScript 将进行更为严格的类型检测。
- 可用值:true、false。
- 默认值:false。
除了该属性外,也可通过下列选项更加细粒度地进行严格检测:
alwaysStrict
:保证编译出的文件是 ECMAScript 的严格模式,并且每个文件的头部会添加 'use strict'。strictNullChecks
:更严格地检查 call、bind、apply 函数的调用,比如会检查参数的类型与函数类型是否一致。strictFunctionTypes
:更严格地检查函数参数类型和类型兼容性。strictPropertyInitialization
:更严格地检查类属性初始化,如果类的属性没有初始化,则会提示错误。noImplicitAny
:禁止隐式 any 类型,需要显式指定类型;TypeScript 在不能根据上下文推断出类型时,会回退到 any 类型。noImplicitThis
:禁止隐式 this 类型,需要显示指定 this 的类型。noImplicitReturns
:禁止隐式返回。如果代码的逻辑分支中有返回,则所有的逻辑分支都应该有返回。noUnusedLocals
:禁止未使用的本地变量。noUnusedParameters
:禁止未使用的函数参数。noFallthroughCasesInSwitch
:禁止 switch 语句中的穿透的情况;开启后,如果 switch 语句的流程分支中没有 break 或 return,则会抛出错误,从而避免了意外的 swtich 判断穿透导致的问题。
以上选项的默认值为 strict
的值。
总结
本文对 TypeScript 的类型系统、泛型编程、类型守卫、类型兼容、类型元编程、类型声明、tsconfig.json 配置等主要知识点进行了简单地总结,如有疏漏之处还望诸位海涵,最后祝大家快乐编码每一天。
转载自:https://juejin.cn/post/7110949595957428260