学习 TypeScript | 青训营笔记
这是我参与「第五届青训营」伴学笔记创作活动的第 4 天
0x1 基本语法
-
什么是 TypeScript
TypeScript 是基于 JavaScript 发展而来,是静态类型的弱类型语言
JS-动态类型:编译在执行阶段进行
TS-静态类型:编译在执行之前进行
-
基本语法
-
基础数据类型
const str: string = 'a string'; const num: number = 10; const boo: boolean = true; const nul: null = null; const und: undefined = undefined;
-
对象数据类型
const obj2: obj1 = { id: 001, name: 'xxx', age: 20 } interface obj1 { readonly id: number; // 只读属性 name: string; age: number; other?: string; // 可选属性 [key: string]: any; // 任意属性 } /* 错误1:只读属性值不可更改 */ obj2.id = 002; /* 错误2:除可选和任意属性,其他属性的值不可缺省 */ const obj3: obj1 = { id: 003, age: 30, other: 'yyy' } /* 任意属性实际操作 */ obj3.newString = 'zzz';
-
函数类型
-
类型声明
function add(x: number, y: number): number { return x + y; } const sub: (x: number, y: number) => number = (x, y) => x - y; interface Mul { (x: number, y: number): number; } const mul: Mul = (x, y) => x * y;
-
函数重载
function func(type: 'string', y?: string): string; interface Func { (type: 'string', y?: string): string; (type: 'date', y?: string): Date; (: 'string' | 'date', y?: string): Date | string; }
-
-
数组类型
/* 类型+方括号 */ type Arr1 = number[]; const arr1: Arr1 = [1, 2, 3, 4, 5, 6]; /* 泛型表示 */ type Arr2 = Array<string | number | Record<string, number>>; const arr2: Arr2 = [1, 2, '3', '4', { a: 1 }]; /* 元祖表示 */ type Arr3 = [number, number, string, string]; const arr3: Arr3 = [1, 2, '3', '4']; /* 接口表示 */ interface Arr4 { [key: number]: any; } const arr4: Arr4 = ['string', () => null, {}, []];
-
TypeScript 补充类型
/* 空类型:表示无赋值 */ type EmptyFunction = () => void; /* 任意类型:是所有类型的子类型 */ type AnyType = any; /* 枚举类型 */ enum Alpha { A, B, C }; Alpha['A'] === 0; Alpha[0] === 'A'; enum EnumExample { add = '+', mult = '*' } EnumExample['add'] === '+'; EnumExample['+'] === 'add'; /* 泛型 */ type NumArr = Array<number>;
-
泛型
function getRepeatArr(target) { return new Array(100).fill(target); } type IGetRepeatArr = (target: any) => any[]; /* 不预先指定具体类型,而是在使用的时候再指定类型的一种特性 */ type IGetRepeatArrTemplate = <T>(target: T) => T[];
/* 泛型接口 & 多泛型 */ interface IX<T, U> { key: T; val: U; } /* 泛型类 */ class IMan<T> { instance: T; } /* 泛型别名 */ type ITypeArr<T> = Array<T> /* 泛型约束:限制泛型必须符合字符串 */ type IGetRepeatStringArr = <T extend string>(target: T) => T[]; /* 泛型参数默认类型 */ type IGetRepeatArr<T = number> = (target: T) => T[];
-
类型别名和类型断言
/* 通过 type 关键字定义了 IObjArr 的别名类型 */ type IObjArr = Array<{ key: string; [objKey: string]: any; }> function keyBy<T extends IObjArr>(objArr: Array<T>) { /* 未指定类型时,result 的类型为 {} */ const result = objArr.reduce((res, val, key) => { res[key] = val; return res; }, {}); /* 通过 as 关键字,断言 result 类型为正确类型 */ reutnr result as Record<string, T>; }
-
字符串/数字 字面量
/* 允许指定字符串/数字必须的固定值 */ /* IDomTag 必须为 html、body、div、span 中的其一 */ type IDomTag = 'html' | 'body' | 'div' | 'span'; /* IOddNumber 必须为 1、3、5、7、9 中的其一 */ type IOddNumber = 1 | 3 | 5 | 7 | 9;
-
0x2 高级数据类型
-
联合/交叉类型
举例:对图书进行分类
const bookList = [{ author: 'aaa', type: 'history', range: '1978-2018' }, { author: 'bbb', type: 'story', theme: 'love' }]
- 联合类型:
A | B
,表示一个值可以是几种类型之一 - 交叉类型:
A & B
,多种类型叠加到一起成为一种类型,包含了所需的所有类型的特性
type IBookList = Array<{ author: string; } & ({ type: 'history'; range: string; } | { type: 'story'; theme: string; })>
- 联合类型:
-
类型保护与类型守卫
interface IA { a: 1, aa: 2 }; interface IB { b: 1, bb: 2 }; /* 类型守卫:定义一个函数,它的返回值是一个类型谓词,生效范围为子作用域 */ function getIsIA(arg: IA | IB): arg is IA { return !!(arg as IA).a; } /* 在 JS 中不会报错 在 TS 中属于错误: 类型 IA | IB 上不存在属性 a;类型 IB 上不存在属性 a 上述情况说明:访问联合类型时,处于程序安全的原因,仅能访问联合类型中的交集部分 */ function opt1(arg: IA | IB) { if(arg.a) { console.log(arg.aa); } else { console.log(arg.bb); } } /* 正确写法 */ function opt2(arg: IA | IB) { if(getIsIA(arg)) { console.log(arg.aa); } else { console.log(arg.bb); } }
结合上述的 “ 图书分类 ” 案例,使用类型保护:
function optBook(book: IBookList) { // 联合类型 + 类型保护 = 自动类型推断 if(book.type === 'history') { console.log(book.range); } else { console.log(book.theme); } }
-
merge()
函数类型/* 要求 sourceObj 必须为 targetObj 的子集 */ function merge1(sourceObj, targetObj) { const result = { ...sourceObj }; for(let key in targetObj) { const itemVal = sourceObj[key]; itemVal && ( result[key] = itemVal )l } return result; } function merge2(sourceObj, targetObj) { return { ...sourceObj, ...targetObj }; } /* 使用泛型编写类型 */ interface IMerge { <T extends Record<string, any>>(sourceObj: Partial<T>, targetObj: T): T; } type IPartial<T extends Record<string, any>> = { [P in keyof T]?: T[P]; } type IKeys = keyof { a: string, b: number };
索引类型
- 关键字
keyof
相当于取值对象中的所有 key 组成的字符串字面量 (Ikeys
相当于type IKeys = "a" | "b";
) - 关键字
in
相当于取值字符串字面量中的一种可能,配合泛型 P,即表示每个 key - 关键字
?
通过设定对象可选选项,即可自动推导出子集类型
- 关键字
-
函数返回值类型
举例:实现函数 delayCall 的类型声明,该函数接收一个函数入参,实现延迟 1000ms 运行函数,返回 Promise,结果为入参函数的返回结果
-
函数方法
function delayCall(func) { return new Promise(resolve => { setTimeout(() => { const result = func(); resolve(result); }, 1000); }); }
-
泛型方法
type IDelayCall = <T extends () => any>(func: T) => ReturnType<T>; type IReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R :any;
-
关键字
extends
跟随泛型出现时,表示类型推断,其表达式可类比三元表达式 (如:T === 类型判断 ? 类型A : 类型B
) -
关键字
infer
出现在类型推荐中,表示定义类型变量,可以用于指代类型(如:上述代码将函数的返回值类型作为变量,使用新泛型
R
表示,使用在类型推荐中的结果中)
-
-
0x3 工程应用
-
浏览器 Web
以 webpack 举例:在工程中使用 webpack 执行 TypeScript
- 配置 webpack loader 相关配置(loader 用于将文件转化为 webpack 可以识别的文件)
- awesome-typescript-loader:
npm install awesome-typescript-loader --save-dev
- babel-loader:
npm install -D babel-loader @babel/core @babel/preset-env webpack
- awesome-typescript-loader:
- 配置 tsconfig.js 文件
- 运行 webpack 启动/打包
- loader 处理 TypeScript 文件时,会进行编译和类型检查
- 配置 webpack loader 相关配置(loader 用于将文件转化为 webpack 可以识别的文件)
-
NodeJS
使用 TSC 编译
- 安装 Node 与 npm
- 配置 tsconfig.js 文件
- 使用 npm 安装 TSC
- 使用 tsc 运行编译得到对应的 js 文件