Typescript 小结
概述
1. 是 javascript 语言的超集,可以编译成ECMScript标准指定的版本。
2. javascript 是一门动态的弱类型语言,具有很高的灵活性。没有静态类型检查是它的一大短板,因而无法为需要长期维护的、大型的项目提供友好支持,而 TypeScript 补全了这一短板。
3. 即让 javascript 代码行为可预测
类型
1. 原始类型
string
number
boolean
null
undefined
symbol (target 必须是es2015及以上版本)
/** 当编译器选项strict设置为true,且strictNullChecks选项没有配置,
或者strictNullChecks选项设置为true时,编译器会提示类型错误 */
// Type 'null' is not assignable to type 'string'
const a: string = null;
const b: string = undefined;
const c: number = null;
const d: boolean = null;
// Type 'undefined' is not assignable to type 'null'
const e: null = undefined;
const f: undefined = null;
const g: symbol = null;
2. 其他类型
数组类型:Array<T> 或者 T[]
元组类型:[T, U, K]
const arr: number[] = [1,2];
const arr2: Array<number> = [1,2];
const tuple: [number, string] = [1, 'cat'];
接口类型:interface
interface IPerson {
name: string;
age: number;
}
枚举类型:enum
未指定属性值,则首个属性默认值为 0, 依次递增
/** 枚举编译后会生成并保留一个可以key和value相互映射的对象*/
enum EDirection {
UP,
DOWN,
}
/** 常量枚举,编译后会被移除(配置preserveConstEnums为true时会保留) */
const enum EShape {
CIRCLE,
RECT,
ELLIPSE,
}
未知类型:unknown (类型安全的)
任意类型:any (类型不安全的)
联合类型:T | U | K
type UDirection = 'UP' | 'DOWN';
const direction: UDirection = 'UP';
类型别名:type
type UDirection = 'UP' | 'DOWN';
const direction: UDirection = 'UP';
字面量类型:
// 变量 greet 的类型为 'hello'
const greet = 'hello';
// 变量 catAlias 的类型为 'cat'
let catAlias: 'cat';
catAlias = 'cat';
// 自动生成对象字面量类型
const obj = {
a: 1,
b: 2,
}
// 类型“{ a: number; b: number; }”上不存在属性“c”
obj.c = 3;
函数无返回值类型: void
function fun(): void {}
never 类型(https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking)
type UShape = 'circle' | 'rect' | 'triangle' | 'polygon';
const unReachable = (msg: string = '检测到遗漏的类型未做处理!'): never => {
throw new Error(msg);
}
function handleShape(shape: UShape) {
switch (shape) {
case 'circle':
break;
case 'rect':
break;
case 'triangle':
break;
// case 'polygon':
// break;
default:
unReachable();
break;
}
}
handleShape('polygon');
3. 类型断言
1. as
interface IPerson {
name: string;
age: number;
}
let obj: IPerson;
obj = { name: "jack" } as IPerson;
2. <T>
注意:该方式的类型断言中的尖括号会与 jsx 中的标签冲突,所以建议用 as类型断言
interface IPerson {
name: string;
age: number;
}
let obj: IPerson;
obj = <IPerson>{ name: "jack" };
Interface 接口
属性可选修饰符 ?
interface IA { name?: string }
const obj: IA = {}
属性只读修饰符 readonly
interface IA { readonly name: string }
const obj: IA = { name: 'cat'}
/** Cannot assign to 'name' because it is a read-only property */
obj.name = 'dog';
索引签名
/** 由于对象的键最终都是string类型,所以此处键类型声明为string,相当于 string|number */
interface IA {
[index: string]: boolean;
}
const obj: IA = {};
obj[0] = true;
函数签名
interface IFun {
(args?: string): void;
}
/** 构造函数签名 */
interface ICatConstr {
new (args: string): any;
}
function gen(Cat: ICatConstr) {
return new Cat('maomi');
}
对象或类方法签名
interface IMethod {
go(args: string): void;
}
const obj: IMethod = {
go(args: string): void {}
}
class Cat implements IMethod {
go(args: string): void {}
}
泛型
更加灵活的声明类型,与类型操作符 typeof、keyof 结合使用而产生强大的功能
类型惰性推断
function fun<T>(param: T) {
if (typeof param === "string") {
return `${param} world`;
} else if (typeof param === "number") {
return param * 1;
}
return;
}
// function fun<string>(param: string): string | number | undefined
fun('hello');
// function fun<number>(param: number): string | number | undefined
fun(100);
类型约束
function fun<T extends { length: number}>(param: T) {
return param.length;
}
fun('hello');
// error: Argument of type 'number' is not assignable to parameter of type '{ length: number; }'
fun(100);
// 取对象中的属性值
function getPropVal<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const obj = { name: 'jack', color: 'red' }
getPropVal(obj, 'name');
// Argument of type '"colour"' is not assignable to parameter of type '"name" | "color"'
getPropVal(obj, 'colour');
类型声明
function fun<T>(param: T): T {
return param;
}
// 第一种方式
interface IGFun1 {
<T>(args: T): T;
}
const myFun1: IGFun1 = fun;
// 第二种方式
interface IFun2<T> {
(args: T): T;
}
const myFun2: IFun2<string> = fun;
// 第三种方式
const myFun3: <T>(args: T) => T = fun;
// 第四种方式
const myFun4: { <T>(args: T): T } = fun;
类型操作符
提取类型属性名作为类型或值类型:keyof
/** 提取属性名 */
interface IA {
name: string;
age: number;
isWaiting: boolean;
}
// 等同于 type = 'name' | 'age' | 'isWaiting';
type IAA = keyof IA;
// 等同于 type IAAA = string | number | boolean
type IAAA = IA[keyof IA];
/** 提取属性值类型 */
interface IB {
[key: string]: boolean;
}
// 等同于 type IBB = string | number;
type IBB = keyof IB;
// 等同于 type IAny = string | number | symbol
type IAny = keyof any;
提取值类型:typeof
function fun() {
return { name: 'jack', age: 20 };
}
// 等同于 IF = () => { name: string; age: number }
type IF = typeof fun;
extends操作符操作联合类型时会展开每个类型做匹配
type INullable<T> = T extends null | undefined ? never : T;
// 等同于 type INo = "A" | "B"
type INo = INullable<'A' | 'B' | null | undefined>;
条件类型
condition ? trueExpression : falseExpression
interface IA {
name: string;
}
interface IAA extends IA {
age: number;
}
// 等同于 type IC = string
type IC = IAA extends IA ? string : number;
提取数组元素类型
// 等同于 type IArrItem<T> = T extends Array<infer Item> ? Item : T;
type IArrItem<T> = T extends any[] ? T[number] : T;
// 等同于 type IItem = string
type IItem = IArrItem<string[]>;
// 等同于 type IItem = boolean
type IItem2 = IArrItem<boolean>;
映射&模版字面量类型
interface IA {
name: string;
age: string;
}
type IMap<T> = {
[k in keyof T]: boolean;
}
// 等同于 type IMA = { name: boolean; age: boolean; }
type IMA = IMap<IA>;
// 移除可选修饰符
type IMap<T> {
[k in keyof T]-?: boolean;
}
// 移除只读修饰符
type IMap<T> {
-readonly [k in keyof T]: boolean;
}
// 模版字面量 使用关键字 as 修改属性名
interface IA {
name: string;
age: number;
}
type IMap<T> = {
[k in keyof T as `get${Capitalize<k & string>}`]: () => void;
}
const getProps: IMap<IA> = {
getName: () => {},
getAge: () => {},
}
内置操作类型
操作字符串类型
// 等同于 type IName = 'NAME';
type IName = Uppercase<'name'>
// 等同于 type IName = 'name';
type IName = Lowercase<'NAME'>;
// 等同于 type IName = 'Name';
type IName = Capitalize<'name'>;
// 等同于 type IName = 'name';
type IName = Uncapitalize<'Name'>;
所有属性可选:Partial
interface IA {
name: string;
age: number;
}
/** 等同于 type IAP = {
name?: string | undefined;
age?: number | undefined;
}
*/
type IAP = Partial<IA>;
// Partial 类似于
type IParcial<T> = {
[k in keyof T]?: T[k];
}
所有属性必选:Required
interface IA {
name?: string;
age?: number;
}
/** 等同于 type IAR = {
name: string;
age: number;
}
*/
type IAR = Required<IA>;
// Required 类似于
type IRequired<T> = {
[k in keyof T]-?: T[k];
}
所有属性只读:Readonly
interface IA {
name: string;
age: number;
}
/** 等同于 type IAR = {
readonly name: string;
readonly age: number;
}
*/
type IAR = Readonly<IA>;
// Readonly 类似于
type IReadonly<T> = {
readonly [k in keyof T]: T[k];
}
构造一个所有属性类型一样的类型:Record<keys, T>
type IShape = 'Circle' | 'Rect' | 'Ellipse'
interface IProps {
label: string;
radius: number;
}
/** 等同于 type IM = {
Circle: IProps;
Rect: IProps;
Ellipse: IProps;
}
*/
type IEntity = Record<IShape, IProps>;
// Record 类似于
type IRecord<T extends keyof any,U> = {
[k in T]: U;
}
摘取属性组成新的类型:Pick<T, keys>
interface IA {
name: string;
age: number;
isWaiting: boolean;
}
/** 等同于 type IPk = {
name: string;
age: number;
}
*/
type IPk = Pick<IA, 'name' | 'age'>
// Pick 类似于
type IPick<T, U extends keyof T> = {
[k in U]: T[k];
};
type IPick2<T, U> = Omit<T, Exclude<keyof T, U>>;
type IPick3<T, U extends keyof T> = {
[p in {
[k in keyof T]: k extends U ? k : never;
}[keyof T]]: T[p];
};
剔除指定属性后剩下的组成新的类型(与Pick相反):Omit<T, keys>
interface IA {
name: string;
age: number;
isWaiting: boolean;
}
/** 等同于 type IO = {
name: string;
age: number;
}
*/
type IO = Omit<IA, 'isWaiting'>
// Omit 类似于
type IOmit<T, U extends keyof T> = Pick<T, Exclude<keyof T, U>>
type IOmit2<T, U extends keyof T> = {
[k in Exclude<keyof T, U>]: T[k]
}
type IOmit3<T, U extends keyof T> = {
[p in ({
[k in keyof T]: k extends U ? never: k;
}[keyof T])] : T[p];
}
剔除联合类型指定的类型后构造新的类型:Exclude<U, T>
type IShape = "Circle" | "Rect" | "Ellipse";
// 等同于 IS = "Circle" | "Rect"
type IS = Exclude<IShape, "Ellipse">;
// Exclude 类似于
type IExclude<T, U> = T extends U ? never : T;
摘取联合类型中指定类型组成新的类型:Extract<U, T>
// 这个内置类型感觉比较鸡😓
type IShape = "Circle" | "Rect" | "Ellipse";
// 等同于 IS = "Circle" | "Rect"
type IS = Extract<IShape, "Circle" | "Rect">;
// Extract 类似于
type IInclude<T, U> = T extends U ? T : never;
剔除联合类型中的null、undefined空类型后组成新的类型:NonNullable<U, T>
type IShape = "Circle" | "Rect" | "Ellipse" | undefined | null;
// 等同于 type INon = "Circle" | "Rect" | "Ellipse";
type INo = NonNullable<IShape>;
// 类似于
type INonNullable<T> = T & {};
type INonNullabl2e<T> = T extends null | undefined ? never : T;
type INonNullable3<T> = Exclude<T, null | undefined>;
ReturnType 用函数类型 T 的返回值类型构造新的类型
Parameters 从函数类型 T 中,提取参数类型来构造元组类型
ConstructorParameters 从构造函数类型 T 中,提取参数类型来构造元组或数组类型
InstanceType 用构造函数类型 T 的实例类型构造一个新的类型
ThisParameterType 提取函数类型 T 的this参数类型,构造新的类型
OmitThisParameter 删除函数类型 T 的this参数类型,构造新的类型
class 类
TypeScript 3.8 版本开始支持 ECMAScript标准目前处于stage3阶段的提案:以 # 开头作为私有属性
属性&方法修饰符
public
protected
private
readonly
属性访问器
setter/getter 必须具有相同的属性修饰符(4.3版本后可以不同)
只存在get,则该属性自动被设置为readonly
若不指定set参数类型,则从get返回值推断
一个子类只允许继承一个父类,但可以实现多个接口
赋值断言运算符 !
class Cat {
// 避免 strictPropertyInitialization 配置项规定的类属性必须在构造函数中初始化的问题
name!: string;
}
// 非空断言
function toLowerCaseXX(str: string | null | undefined) {
return str!.toLowerCase();
}
声明合并
// 若接口声明在不同的文件,则文件均为非模块文件且通过三斜杆方式引入
interface IA {
name: string;
}
interface IA {
age: number;
}
// Property 'age' is missing in type '{ name: string; }' but required in type 'IA'
const obj: IA = { name: 'jack' };
// 接口合并,合并后顺序:后合并的排在先合并的前面(参数是字符串的顺序会自动向上冒泡)
interface IShape {
paint(params: Rect): Rect;
}
interface IShape {
paint(params: Circle): Circle;
}
// 合并后相当于
interface IShape {
paint(params: Circle): Circle;
paint(params: Rect): Rect;
}
// 命名空间 namespace 合并和 interface 差不多
// 声明文件没有 export import 关键字,则 typescript视为非模块文件
标准库声明
TypeScript 默认包含一个名为 lib.d.ts 的文件,它提供了像 DOM 这种 Javascript 内置库的接口声明, 用来提高 TypeScript 对第三方库和像 Node.js、浏览器等这种运行时环境的兼容
操作符
对象属性链式可选操作符
// 等同于 const name = record && record.city && record.city.name
const name = record?.city?.name;
非空操作符
// 等同于 const addr = record?.addr || 'shanghai'
const addr = record?.addr ?? 'shanghai'
配置文件
tsc --init 自动生成tsconfig.json配置文件,且每个编译项都有注释说明
配置文件顶级属性
- --files
注解:指定需要被编译的文件,且文件必须存在
类型:string[] 样例:
{
"files": ['utils.ts', 'request.ts'],
"compilerOptions": {}
}
- --extends 注解:继承指定的tsconfig.json文件配置(reference配置项除外),两个配置文件不允许相互依赖 类型:string 样例:
{
"extends": "**/*/base",
"compilerOptions": {}
}
- --include 注解:指定需要被编译的文件路径(路径可以使用模式通配符) 类型:string[] 样例:
{
// 默认匹配 .ts、.tsx、.d.ts
// allowJs 配置为 true,则也包含 .js、.jsx
"include": ["src/**/*", "test/**/*"],
"compilerOptions": {}
}
- --exlcude 注解:指定不需要被编译的文件路径(仅相对于 include 配置的路径) 类型:string[] 样例:
{
// 默认匹配 .ts、.tsx、.d.ts
// allowJs 配置为 true,则也包含 .js、.jsx
"exclude": ["src/test/**/*"],
"compilerOptions": {}
}
- --references 注解:一种将ts项目构建成更小片段的方式,有助于提升编译效率、提升编译更新交互时间 类型:object[] 样例:
{
"compilerOptions": {},
"references": [
{ "path": "../src" }
]
}
编译器选项
- --allowUnreachableCode 注解:允许永远执行不到的代码存在 类型:boolean 样例:
function valid(num: number) {
if (num > 1) {
return true;
} else {
return false;
}
// Unreachable code detected
return false;
}
- --alwaysStrict 注解:确保每个文件启用严格模式 类型:boolean
- --noFallthroughCasesInSwitch 注解:switch 语句可能不会命中的case错误提示 类型:boolean 样例:
{
switch (Math.ceil(Math.random() * 10) % 9) {
// Fallthrough case in switch
case 1:
console.log('#### 1111 #####');
case 2:
console.log('#### 2222 #####');
break;
}
}
- --noImplicitAny 注解:是否开启【类型隐式推断为any,则提示错误】 类型:boolean 样例:
// Parameter 's' implicitly has an 'any' type, but a better type may be inferred from usage
function copy(s) {
console.log(s.slice());
}
copy(42);
- --noImplicitReturns 注解:检查函数是否所有条件下都有返回值 类型:boolean 样例:
// Not all code paths return a value
function fun(num: number) {
if (num > 0) {
return 1;
} else if (num == 0) {
return 0;
}
}
fun(42)
- --strict 注解:开启严格模式,相当于开启(alwaysStrict、strictNullChecks、strictBindCallApply、strictFunctionTypes、strictPropertyInitialization、noImplicitAny、noImplicitThis、useUnknownInCatchVariables) 类型:boolean
- --strictNullChecks 注解:开启空值严格检查(null、undefined有独立的类型,不可赋值给其他类型) 类型:boolean 样例:
// type 'undefined' is not assignable to type 'string'
const a: string = undefined;
// Type 'null' is not assignable to type 'number'
const b: number = null;
- --strictPropertyInitialization 注解:类属性必须在构造函数中被初始化(strictNullChecks也设置为true) 类型:boolean 样例:
class Person {
name: string;
// Property 'age' has no initializer and is not definitely assigned in the constructor
age: string;
constructor(name: string) {
this.name = name;
}
}
- --allowUmdGlobalAccess 注解:允许umd模块代码全局可访问 类型:boolean 样例:jquery、lodash
- --baseUrl 注解:模块查找的基础路径 类型:string 样例:
src
|-- utils
| |-- get.ts
|-- tsconfig.json
// baseUrl: './'
import { get } from 'utils/get'
- --module 注解:告诉编译器需要生成何种模块类型的代码 类型:"commonJs" | "AMD" | "UMD" | "ES2015" | "ESNext" | "NodeNext" | "System" 等等(有代码提示)
- --moduleResolution 注解:告诉编译器模块的处理方式 类型:"classic" | "node" | "node12" | "nodenext"
- --noResolve 注解:告诉编译器打包文件时跳过三斜杠引入的文件 类型:boolean
- --paths 注解:路径映射(baseUrl必须被声明) 类型:object 样例:
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"env/*": ["env/*"],
"config/*": ["config/*"],
"utils/*": ["utils/*"],
},
}
- --resolveJsonModule 注解:项目中允许引入json文件 类型:boolean 样例:
import { version } from 'package.json';
- --rootDir 注解:项目资源文件根目录 类型:string 样例:
{
"compilerOptions": {
rootDir: "src"
}
}
- --rootDirs 注解:虚拟根目录 类型:string[] 样例:
// 资源文件和类型声明文件归类存放
{
"compilerOptions": {
rootDirs: ["src", 'typings']
}
}
- --typeRoots 注解:类型文件查找根目录(编译器仅会在该目录下查找) 类型:string[] 样例:
{
"compilerOptions": {
typeRoots: ['typings']
}
}
- --types 注解:类型文件查找路径(仅在指定的目录查找) 类型:string[] 样例:
{
"compilerOptions": {
// 编译器仅会在 node_modules/@types/node、node_modules/@types/express 下查找
types: ['node', 'express']
}
}
- --declaration 注解:生成 .d.ts 类型声明文件 类型:boolean
- --declarationDir 注解:指定生成的类型声明文件存放目录 类型:string
- --declarationMap 注解:生成sourceMap类型声明文件 类型:boolean
- --downlevelIteration 注解:for-of循环降级处理 类型:boolean
- --noEmitOnError 注解:当编译器报错,则不生成文件 类型:boolean
- --outDir 注解:编译器生成的文件存放目录 类型:string 样例:
{
"compilerOptions": {
"outDir": "dist"
}
}
- --outFile 注解:生成的文件合并到指定的文件中(仅当module被设置为none、system、amd,否则不可用) 类型:string
- --preserveConstEnums 注解:枚举常量(const enum)被编译后作为普通对象保留(不会像interface等接口类型编译后被移除) 类型:boolean
- --removeComments 注解:编译后移除注释 类型:boolean
- --sourceMap 注解:生成sourceMap映射文件 类型:boolean
- --allowJs 注解:允许在ts文件中导入js文件 类型:boolean
- --checkJs 注解:开启js文件类型校验(allowJs也启用)(相当于在文件顶部声明了@ts-check) 类型:boolean
- --disableSizeLimit 注解:禁用项目内存大小限制 类型:boolean
- --allowSyntheticDefaultImports 注解:允许直接导入没有声明default导出的文件 类型:boolean 样例:
// 开启时,不用 import * as A from 'ab';
import A from 'ab';
- --esModuleInterop 注解:生成新的代码以支持处理“commonJs/amd/umd”模块引入相关问题(详情查阅官方文档): 引入整个模块:import * as X from 'x' 引入默认导出:import y from 'y'; 类型:boolean
- --forceConsistentCasingInFileNames 注解:导入模块时开启模块名称严格大小写检查 类型:boolean 样例:
// 引入文件goodName.ts,开启forceConsistentCasingInFileNames会提示引入错误
import name from './goodname';
-
--isolatedModules 注解:开启每个文件模块可以不依赖其他模块独立打包 类型:boolean
-
--experimentalDecorators 注解:开启装饰器能力支持(experimentalDecorators) 类型:boolean
-
--jsx 详情 注解:.tsx 文件编译后生成的文件 类型:'preserve' | 'react' | 'react-native' | 'react-jsx' | 'react-jsxdev'
-
--lib 注解:声明项目包含哪些包类型声明文件 类型:"ES5" | "DOM" | ... 样例:
// tsconfig.json
{
"compilerOptions": {
// 项目运行环境仅包含es5包类型定义
"lib": ["ES5"],
}
}
// test.ts
// Cannot find name 'console'. Do you need to change your target library? Try changing the `lib` compiler option to include 'dom'.
console.log(version);
- --target 注解:编译后生成的兼容js代码的版本(ECMAScript) 类型:"ES2015" | "es2016" | ...
- --explainFiles 注解:打包时终端上显示文件模块打包细节(一般用于debug场景) 类型:boolean
- --extendedDiagnostics 注解:打包时终端上显示打包耗时(查看打包性能问题) 类型:boolean
- --listFiles 注解:打包时终端上显示被打包的文件 类型:boolean 样例:
- --traceResolution 注解:打包时终端上显示模块打包过程 类型:boolean
- --incremental 注解:增量编译,将上一次编译后的状态信息缓存到磁盘上(.tsbuildinfo文件) 类型:boolean
- --tsBuildInfoFile 注解:指定增量编译缓存的编译信息文件(.tsbuildinfo)存放目录 类型:string
- --watchFile 注解:侦测文件更新策略 类型:'fixedPollingInterval' | 'useFsEvents' | ... 样例:
{
"compilerOptions": {},
"watchOptions": {
"watchFile": "useFsEvents"
}
}
- --watchDirectory 注解:侦测文件夹更新策略 类型:'fixedPollingInterval' | 'dynamicPriorityPolling' | 'useFsEvents' 样例:
{
"compilerOptions": {},
"watchOptions": {
"watchDirectory": "useFsEvents"
}
}
-
--fallbackPolling 注解:watch文件的轮训策略 类型:'fixedPollingInterval' | 'priorityPollingInterval' | 'dynamicPriorityPolling' | 'synchronousWatchDirectory'