这篇笔记记录一些TypeScript的基本语法和知识点。
一、本堂课重点内容
- TypeScript 基本语法
- TypeScript 高级数据类型
二、详细知识点介绍
1、TypeScript特性
- TypeScript是一门弱类型、静态的语言
- 弱类型语言:允许隐式类型转换
- 静态:编译时就确定变量的数据类型。动态语言是运行时确定
- 静态语言特性
- 可读性强
- 可维护性强:编译时发现错误
- TS是JS的超集
2、基本语法
-
基础数据类型:
string
、number
、boolean
、null
、undefined
-
对象类型
readonly
:只读属性?.
:可选属性[key: string]: any
:表示这一项可有可无,且必须key
为字符串类型,value
为任意类型
interface IByteDance { readonly jobId: number; name: string; sex: 'max' | 'woman' | 'other'; age: number; hobby?: string; [key: string]: any; }
-
函数类型
interface IMult { (x: number, y: number): number; } const mult: IMult = (x, y) => x * y; /* ---或者--- */ const mult: (x: number, y: number) => number = (x, y) => x * y;
-
函数重载
function getDate(type: string, timestamp?: string): string; interface IGetDate { (type: string, timestamp?: string): string; (type: Date, timestamp?: string): Date; (type: string | Date, timestamp?: string): Date | string; const getDate2: IGetDate = (type: string, timestamp: string) => { const date = new Date(timestamp); return type === string ? date | date.toLocaleString(); } }
-
数组类型
- 类型 + 方括号:
const arr1: number[] = [1, 2, 3];
- 泛型
type IArr2 = Array<string | number | Record<string, number>> const arr2: IArr2 = [1, '2', {a: 1}]
- 元组
type IArr3 = [number, string, string]; const arr3: IArr3 = [1, '2', '3'];
- 接口
interface IArr4 { [key: number]: any; } const arr4: IArr4 = [...];
- 类型 + 方括号:
-
补充类型
void
any
- 枚举
// 定义枚举类 enum Gender { Male = 1, Female = 2, } let msg:{name:string,gender:Gender} // 第二步,类型的时候填枚举类 msg = { name:'zhangsan', gender:Gender.Female // 使用的时候用点语法 }
-
泛型
- 不预先指定具体的类型,而在使用的时候再指定类型的一种特性
type IType = <T>(target: T) => T[];
- 泛型接口 & 多泛型
interface IX<T, U> { key: T; val: U; }
- 泛型类
class IMan<T> { instance: T; }
- 泛型别名
type IArr<T> = Array<T>;
- 泛型约束:extends。补充extends作用:
- ①接口继承
interface Animal { name: string } interface Dog extends Animal { bark: () => void }
- ②普通条件判断:用来判断一个类型是否可以分配给另外一个类型。
A extends B
,是表示A类型能够分配给B类型,而不是表示A类型是B类型的子集。换句话来说,如果条件判断的时候A extends B
为true,则说明约束A类型的一切约束条件,B类型都具有。
type bool = Animal extends Dog ? 'yes': 'no' // bool = 'no' type bool1 = Dog extends Animal ? 'yes': 'no' // bool1 = 'yes'
- ③泛型条件判断:对于使用
extends
关键字的条件类型,如果extends
前面的参数是一个泛型类型,当传入该参数的是联合类型,则使用分配律计算最终的结果。- 分配律是指,将联合类型的联合项拆成单项,分别代入条件类型,然后将每个单项代入得到的结果再联合起来,得到最终的判断结果。
- 满足两个要点即可适用分配律:
- 参数是泛型类型,
- 代入参数的是联合类型
type AB<T> = T extends 'x' ? 'a' : 'b'; type All = AB<'x' | 'y'>; // 非确定条件,可能是 'x' 或 'y' // 得到 type All = 'a' | 'b';
- ④泛型约束
function getInfo<T, key extends keyof T>(obj: T, key: key): T[key]{ return obj[key] } const obj = { name: 'tom', age: 12 } getInfo(obj, 'age') // 这里第二个参数只能传age或者name,传其他的字段都会报错。
-
类型断言:
as
interface Vup { name: string, roomId: number, } // 以下代码报错,类型“{}”上不存在属性xxx // let azi = {} // azi.name = "azusa"; // azi.age = 18; // 添加类型断言编译通过 let azi = {} as Vup; azi.name = "azusa"; azi.age = 18;
-
字符串/数字 字面量
- 允许指定字符串/数字必须的固定值
type IChar = 'A' | 'B' | 'C'; type INum = 1 | 3 | 5;
3、高级类型
- 联合/交叉类型
- 联合类型:IA|IB,表示一个值可以是几种类型之一
- 交叉类型:IA&IB,多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性
const vupList = [{ name: 'azusa', type: 'human', vrlink: 1 }, { name: 'nanami', type: 'shark', vrp: 3, }] type IVupList = Array<{ name: string; } & ({ type: 'human'; vrlink: number; } | { type: 'shark'; vrp: number; })>
- 类型保护与类型守卫
- 类型守卫:定义一个函数,它的返回值是一个类型谓词,生效范围为子作用域
interface IA { a: 1, a1: 2} interface IB { b: 1, b1: 2} // 类型守卫:定义一个函数,它的返回值是一个类型谓词,生效范围为子作用域 function getIsIA(arg: IA|IB): arg is IA { return !!(arg as IA).a; } function log2(arg: IA|IB) { if(getIsIA(arg)) { console.log(arg.a1); } else { console.log(arg.b1); } }
- 实现logVup方法,函数接收vup类型,并打印出相关特征
- 联合类型 + 类型保护 = 自动类型推断
function logVup(vup: IVup) { if (vup.type === 'human') { console.log(vup.vrlink); } else { console.log(vup.vrp); } }
- 高级类型
- 假如现在要把两个对象 sourceObj 和 targetObj 合并,其类型实现繁琐。若 obj 类型复杂,则生命 source 和 target 便需要大量重复2遍。若 target 增加/减少 key, 则需要 source 联动去除
interface ISourceObj { x?: string; y?: string; } interface ITargetObj { x: string; y: string; } type IMerge = (sourceOBj: ISourceObj, targetObj: ITargetObj) => ITargetObj;
- 使用高级类型
Record
和Partial
interface IMerge { <T extends Record<string, any>>(sourceObj: Partial<T>, targetObj: T): T; }
Partial
的底层实现keyof
:取值对象中的所有 key 组成的字符串字面量。- e.g.:
type IKeys = keyof {a: string, b: number};
等价于type IKeys = 'a'|'b';
- e.g.:
in
:遍历?
:将该属性变为可选属性
type Partial<T> = { [P in keyof T]?: T[P]; }
- 函数返回值类型
- 实现一个delayCall函数,接收一个函数作为入参,返回一个promise,结果为入参函数的返回结果
function delayCall(func) { return new Promise(resolve => { setTimeout(()=>{ const result = func(); resolve(result); }, 1000); }); }
- 定义类型
extends
跟随泛型出现时,表示类型推断,其表达可类比三元表达式,如T === 判断类型 ? 类型A: 类型B
infer
的理解:如果T
继承了extends (...args: any[]) => any
类型,则返回类型R
,否则返回any
。其中R
被定义在extends (...args: any[]) => infer R
中,即 R 是从传入参数类型中推导出来的。
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
的作用、TypeScript的高级类型都很难懂,也不容易想明白,需要时常回顾复习。