Skip to content

TypeScript Note #2

@MongoRolls

Description

@MongoRolls

TypeScript 具有类型系统,且是JavaScript超集,其可以编译成普通的JavaScript代码,也就是说,其是带有类型检查的JavaScript

本篇只介绍部分概念和用法,不涉及 babel 转译等等

类型

元祖和数组

特性 数组类型 元组类型
有 length 属性
length 类型 number(可变) 具体数字(3、4 等)
长度 可变 固定
元素类型 相同 各异
类型位置 不固定 固定

as const 的作用:将 length 属性的类型从 number 变成具体数字字面量,同时保持元素的字面量类型,使类型系统能够精确追踪长度和元素值。

联合类型(Union Types)

定义:使用 | 符号表示多种类型的组合,如 T | U 表示值可以是类型 T 或 U 中的任意一个。
用途:描述函数参数、返回值或变量的多种可能类型。

分发特性:当条件类型应用于联合类型时,TypeScript 自动分发条件。即 (A | B) extends U ? X : Y 等价于 (A extends U ? X : Y) | (B extends U ? X : Y)

交叉类型(Intersection Types)

定义:使用 & 符号表示多种类型的合并,如 T & U 表示同时满足类型 T 和 U 的值。
用途:合并多个类型的属性。

模板字面量类型(Template Literal Types)

type MyType<T> = `${T}Value1`;

定义:使用模板字符串语法构造字面量类型,可基于类型 T 生成新的字符串字面量类型。
用途:动态生成键名、事件名等字符串类型。

any 与 never

特性 any never
定义 任意类型,接受所有值 永不存在的类型,无法达到
赋值规则 可接收任何值,可赋值给任何类型 可赋值给任何类型,但无类型可赋值给它
使用场景 禁用类型检查(需避免) 表示不可能的状态、穷举检查

泛型(Generics)

todo

操作符

typeof

定义:类型查询操作符,用于获取变量或函数的类型(非 JavaScript 的基本数据类型检查)。
用途:类型推断、类型获取、类型判断。

const num = 42;
type NumType = typeof num; // number

function getName() { return 'Alice'; }
type ReturnType = typeof getName; // () => string

extends

定义:条件类型操作符,用于类型约束或条件判断,形式为 T extends U ? X : Y
用途:类型约束、条件类型、分布式条件处理。

type IsString<T> = T extends string ? true : false;
type A = IsString<'hello'>; // true
type B = IsString<number>; // false

keyof

定义:索引类型查询操作符,返回类型所有键的联合类型。
用途:提取对象键名、约束泛型参数。

type Person = { name: string; age: number };
type Keys = keyof Person; // 'name' | 'age'

in

定义:映射类型操作符,用于遍历键并创建新类型,形式为 [P in K]: T[P]
用途:基于现有类型生成新的对象类型。

type ReadonlyPerson = {
  readonly [K in keyof Person]: Person[K];
};

infer

定义:条件类型推断关键字,用于在条件类型中捕获并提取类型变量。
用途:提取函数返回值类型、数组元素类型等。

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type Arr<T> = T extends (infer U)[] ? U : never;
type StrOrNum = Arr<(string | number)[]>; // string | number

as

  1. 类型断言:强制指定值的类型。
  2. const 断言:创建不可变的字面量类型。
  3. 映射类型键名重映射:条件性改变对象键名。
type Person = {
  name: string;
  age: number;
  email: string;
};

// 键名重映射示例
type PersonWithPrefix = {
  [K in keyof Person as `user_${K}`]: Person[K];
};
// 结果:{ user_name: string; user_age: number; user_email: string; }

声明文件

tsconfig.json

{
  "compilerOptions": {
    /* Basic Options */
    "target": "es5" /* target用于指定编译之后的版本目标: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
    "module": "commonjs" /* 用来指定要使用的模块标准: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
    "lib": ["es6", "dom"] /* lib用于指定要包含在编译中的库文件 */,
    "allowJs": true,                       /* allowJs设置的值为true或false,用来指定是否允许编译js文件,默认是false,即不编译js文件 */
    "checkJs": true,                       /* checkJs的值为true或false,用来指定是否检查和报告js文件中的错误,默认是false */
    "jsx": "preserve",                     /* 指定jsx代码用于的开发环境: 'preserve', 'react-native', or 'react'. */
    "declaration": true,                   /* declaration的值为true或false,用来指定是否在编译的时候生成相应的".d.ts"声明文件。如果设为true,编译每个ts文件之后会生成一个js文件和一个声明文件。但是declaration和allowJs不能同时设为true */
    "declarationMap": true,                /* 值为true或false,指定是否为声明文件.d.ts生成map文件 */
    "sourceMap": true,                     /* sourceMap的值为true或false,用来指定编译时是否生成.map文件 */
    "outFile": "./",                       /* outFile用于指定将输出文件合并为一个文件,它的值为一个文件路径名。比如设置为"./dist/main.js",则输出的文件为一个main.js文件。但是要注意,只有设置module的值为amd和system模块时才支持这个配置 */
    "outDir": "./",                        /* outDir用来指定输出文件夹,值为一个文件夹路径字符串,输出的文件都将放置在这个文件夹 */
    "rootDir": "./",                       /* 用来指定编译文件的根目录,编译器会在根目录查找入口文件,如果编译器发现以rootDir的值作为根目录查找入口文件并不会把所有文件加载进去的话会报错,但是不会停止编译 */
    "composite": true,                     /* 是否编译构建引用项目  */
    "incremental": true,                   /* 是否启用增量编译*/
    "tsBuildInfoFile": "./",               /* 指定文件用来存储增量编译信息 */
    "removeComments": true,                /* removeComments的值为true或false,用于指定是否将编译后的文件中的注释删掉,设为true的话即删掉注释,默认为false */
    "noEmit": true,                        /* 不生成编译文件,这个一般比较少用 */
    "importHelpers": true,                 /* importHelpers的值为true或false,指定是否引入tslib里的辅助工具函数,默认为false */
    "downlevelIteration": true,            /* 当target为'ES5' or 'ES3'时,为'for-of', spread, and destructuring'中的迭代器提供完全支持 */
    "isolatedModules": true,               /* isolatedModules的值为true或false,指定是否将每个文件作为单独的模块,默认为true,它不可以和declaration同时设定 */
    "newLine": "lf",                       /* 指定换行符。可选`crlf`和`LF`两种 */

    /* Strict Type-Checking Options */
    "strict": true /* strict的值为true或false,用于指定是否启动所有类型检查,如果设为true则会同时开启下面这几个严格类型检查,默认为false */,
    "noImplicitAny": true,                 /* noImplicitAny的值为true或false,如果我们没有为一些值设置明确的类型,编译器会默认认为这个值为any,如果noImplicitAny的值为true的话。则没有明确的类型会报错。默认值为false */
    "strictNullChecks": true,              /* strictNullChecks为true时,null和undefined值不能赋给非这两种类型的值,别的类型也不能赋给他们,除了any类型。还有个例外就是undefined可以赋值给void类型 */
    "strictFunctionTypes": true,           /* strictFunctionTypes的值为true或false,用于指定是否使用函数参数双向协变检查 */
    "strictBindCallApply": true,           /* 设为true后会对bind、call和apply绑定的方法的参数的检测是严格检测的 */
    "strictPropertyInitialization": true,  /* 设为true后会检查类的非undefined属性是否已经在构造函数里初始化,如果要开启这项,需要同时开启strictNullChecks,默认为false */
   "noImplicitThis": true,                /* 当this表达式的值为any类型的时候,生成一个错误 */
    "alwaysStrict": true,                  /* alwaysStrict的值为true或false,指定始终以严格模式检查每个模块,并且在编译之后的js文件中加入"use strict"字符串,用来告诉浏览器该js为严格模式 */

    /* Additional Checks */
    "noUnusedLocals": true,                /* 用于检查是否有定义了但是没有使用的变量,对于这一点的检测,使用eslint可以在你书写代码的时候做提示,你可以配合使用。它的默认值为false */
    "noUnusedParameters": true,            /* 用于检查是否有在函数体中没有使用的参数,这个也可以配合eslint来做检查,默认为false */
    "noImplicitReturns": true,             /* 用于检查函数是否有返回值,设为true后,如果函数没有返回值则会提示,默认为false */
    "noFallthroughCasesInSwitch": true,    /* 用于检查switch中是否有case没有使用break跳出switch,默认为false */

    /* Module Resolution Options */
    "moduleResolution": "node",            /* 用于选择模块解析策略,有'node'和'classic'两种类型' */
    "baseUrl": "./",                       /* baseUrl用于设置解析非相对模块名称的基本目录,相对模块不会受baseUrl的影响 */
    "paths": {},                           /* 用于设置模块名称到基于baseUrl的路径映射 */
    "rootDirs": [],                        /* rootDirs可以指定一个路径列表,在构建时编译器会将这个路径列表中的路径的内容都放到一个文件夹中 */
    "typeRoots": [],                       /* typeRoots用来指定声明文件或文件夹的路径列表,如果指定了此项,则只有在这里列出的声明文件才会被加载 */
    "types": [],                           /* types用来指定需要包含的模块,只有在这里列出的模块的声明文件才会被加载进来 */
    "allowSyntheticDefaultImports": true,  /* 用来指定允许从没有默认导出的模块中默认导入 */
    "esModuleInterop": true /* 通过为导入内容创建命名空间,实现CommonJS和ES模块之间的互操作性 */,
    "preserveSymlinks": true,              /* 不把符号链接解析为其真实路径,具体可以了解下webpack和nodejs的symlink相关知识 */

    /* Source Map Options */
    "sourceRoot": "",                      /* sourceRoot用于指定调试器应该找到TypeScript文件而不是源文件位置,这个值会被写进.map文件里 */
    "mapRoot": "",                         /* mapRoot用于指定调试器找到映射文件而非生成文件的位置,指定map文件的根路径,该选项会影响.map文件中的sources属性 */
    "inlineSourceMap": true,               /* 指定是否将map文件的内容和js文件编译在同一个js文件中,如果设为true,则map的内容会以//# sourceMappingURL=然后拼接base64字符串的形式插入在js文件底部 */
    "inlineSources": true,                 /* 用于指定是否进一步将.ts文件的内容也包含到输入文件中 */

    /* Experimental Options */
    "experimentalDecorators": true /* 用于指定是否启用实验性的装饰器特性 */
    "emitDecoratorMetadata": true,         /* 用于指定是否为装饰器提供元数据支持,关于元数据,也是ES6的新标准,可以通过Reflect提供的静态方法获取元数据,如果需要使用Reflect的一些方法,需要引入ES2015.Reflect这个库 */
  }
  "files": [], // files可以配置一个数组列表,里面包含指定文件的相对或绝对路径,编译器在编译的时候只会编译包含在files中列出的文件,如果不指定,则取决于有没有设置include选项,如果没有include选项,则默认会编译根目录以及所有子目录中的文件。这里列出的路径必须是指定文件,而不是某个文件夹,而且不能使用* ? **/ 等通配符
  "include": [],  // include也可以指定要编译的路径列表,但是和files的区别在于,这里的路径可以是文件夹,也可以是文件,可以使用相对和绝对路径,而且可以使用通配符,比如"./src"即表示要编译src文件夹下的所有文件以及子文件夹的文件
  "exclude": [],  // exclude表示要排除的、不编译的文件,它也可以指定一个列表,规则和include一样,可以是文件或文件夹,可以是相对路径或绝对路径,可以使用通配符
  "extends": "",   // extends可以通过指定一个其他的tsconfig.json文件路径,来继承这个配置文件里的配置,继承来的文件的配置会覆盖当前文件定义的配置。TS在3.2版本开始,支持继承一个来自Node.js包的tsconfig.json配置文件
  "compileOnSave": true,  // compileOnSave的值是true或false,如果设为true,在我们编辑了项目中的文件保存的时候,编辑器会根据tsconfig.json中的配置重新生成文件,不过这个要编辑器支持
  "references": [],  // 一个对象数组,指定要引用的项目
}

内置类型

类型 作用
Partial 将类型 T 的所有属性设为可选
Required 将类型 T 的所有属性设为必需
Readonly 将类型 T 的所有属性设为只读
Record<K, T> 创建键类型为 K,值类型为 T 的对象类型
Pick<T, K> 从类型 T 中选择指定的属性 K
Omit<T, K> 从类型 T 中排除指定的属性 K
Exclude<T, U> 从联合类型 T 中排除 U 中的类型
Extract<T, U> 从联合类型 T 中提取 U 中的类型

技巧

数组索引

T[number]

从数组或元组类型 T 中提取元素类型。T[number] 获取所有索引位置的元素类型联合。

type Arr = [string, number, boolean];
type ElementType = Arr[number]; // string | number | boolean

type StringArr = string[];
type Element = StringArr[number]; // string

keyof any

keyof any // string | number | symbol

keyof any 快速获取对象所有可能的键类型联合。同时 keyof never 结果为 never

type AllKeys = keyof any; // string | number | symbol

P extends any 触发分布式条件类型

当类型参数为未知联合类型时,使用 P extends any 可以触发分布式条件类型:联合类型作为类型参数出现在条件类型左边时,会将每个类型单独传入计算,最后合并结果成联合类型。

type Union = 'a' | 'b' | 'c';

type UppercaseUnion<Item extends string> = 
  Item extends 'a' ? Uppercase<Item> : Item;

type Result = UppercaseUnion<Union>; // 'A' | 'b' | 'c'

Copy

type Copy<Obj extends Record<string,any>> = {
  [Key in keyof Obj] : Obj[Key]
}

Copy 的高级类型是通过映射类型的语法构造了一个新的索引类型,它的功能是原封不动的复制一个索引类型。
类型参数 Obj 约束为索引类型,也就是 Record<string, any>。key 保持不变,也就是 Key in keyof Obj,value 也保持不变,也就是 Obj[Key]。

ts 类型只有计算的时候才会求值,如果是索引类型,可以用映射类型的语法创建个一摸一样的索引类型,因为用到了,就会做计算,从而就可以显示出最终的类型。

Q&A

同态,非同态

interface & type

参考资源

小技巧
// ^?可以用来看类型信息,自动一直显示,省去hover显示

type Instance = Record<string | symbol, number>;
type InstanceKey = keyof Instance;
//  ^? type InstanceKey = string | symbol
type InstanceValue = Instance[keyof Instance];
//  ^? type InstanceValue = number

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions