TS是JS+静态类型系统,完全兼容JS,TS通过类型注解、接口、泛型等特性在编写时就能检测类型错误,提前规避bug(JS变量类型动态,可随时变更,运行时才能发现报错)
1、类型
1)基础:number、string、boolean
2)null、undefined
3)any、unknown:可以是所有类型,unknown只能赋值给unknown或者any
any放弃类型校验、unknown相对安全,不能进行运算、调用属性、当作函数等任何操作,使用时类型要具体化,缩小使用范围
function any (info: any): any { return info; }4)void:函数无返回值用 / void 类型的变量只能赋值为 undefined 和 null
function void(): void { console.log('void'); }5)array:数组
6)tuple:元组(允许表示一个已知元素数量和类型的数组,各元素的类型不必相同
eg:let tuple:[string, number, boolean] = ['str', 3, true];
越界元素:访问超出元祖长度的元素时,其类型会被限制为元祖中每个类型的联合类型
7)enum:枚举(取值被限定在一定范围内
enum Direction { Up = 1, Down, Left, Right } //数字枚举会自动增长,如果第一个不初始化赋值会从0开始。枚举可以通过名字和下标访问,枚举值和枚举名字互相映射。 console.log(Direction.Up);//0 console.log(Direction[0]);//Up const a = Direction.Up; declare let a: Direction // a声明为Direction,相当于声明联合类型,即a是Direction.Up | Direction.Down | Direction.Left | Direction.Right8)object:除number,string,boolean,symbol,null或undefined之外的类型
9)never:永不存在的值的类型(出现never其实就是报错,一般不会出现)
function error(message: string): never { throw new Error(message); }10)symbol:通过Symbol构造函数创建,不可改变且唯一
eg:let sym2 = Symbol("key"); let sym3 = Symbol("key"); sym2 === sym3; // false
TS原始类型包括:boolean、number、string、void、undefined、null、symbol、bigint
注解:
1)函数没有返回值时,返回类型就是
void。只有null和undefined可以赋给void。2)默认情况下
null和undefined是所有类型的子类型。开启--strictNullChecks后,null和undefined只能赋值给any和它们各自以及void。3)
number和bigint类型的值不能互相赋值。4)
number是类型,而Number是构造函数
2、类型处理
(1)类型注解:eg:let username: string = "zhangsan"; // 字符串类型
type MyString = string; let str: MyString = "hello";
数组类型写法:
// 写法1:类型[] let numArr: number[] = [1, 2, 3]; numArr.push("4"); // 报错:类型不匹配 // 写法2:Array<类型>(泛型语法) let strArr: Array<string> = ["a", "b", "c"]; // 联合类型数组(允许多种类型) let mixedArr: (number | string)[] = [1, "a", 2];(2)类型声明了以后,变量用类型:
// 类型声明 interface User { name: string; id: number; // 必选属性 readonly role: string; // 只读属性(初始化后不可修改) age?: number; //?表示可选 [propName: string]: any; //添加未定义的属性,并可以指定属性值的类型 } // 变量定义 const user: User = { name: "zhangsan", id: 1, //如果缺少必选属性就会报错 role: "admin" //只读属性,在这里初始化后不可再修改 // 如果这里放一个User没有声明的属性就会报错 };(3)面向对象的写法:
interface User { name: string; id: number; } class UserAccount { name: string; id: number; constructor(name: string, id: number) { this.name = name; this.id = id; } } const user: User = new UserAccount("zhangsan", 1);(4)泛型:声明的泛型类型固定了,传其他类型的值就会报错
interface Backpack<Type> { add: (obj: Type) => void; get: () => Type; } // 这一行简写,告诉 TypeScript 有一个常量,叫做`backpack` declare const backpack: Backpack<string>; // 对象是一个字符串,因为我们在上面声明了它作为 Backpack 的变量部分。 const object = backpack.get(); // 因为 backpack 变量是一个字符串,不能将数字传递给 add 函数。 backpack.add(23);也可使其自动推导:
// 泛型函数:用<T>表示类型占位符,使用时指定具体类型 function identity<T>(value: T): T { return value; // 输入类型与返回类型一致 } // 使用时指定类型(或让TS自动推导) const str = identity<string>("hello"); // 类型为string const num = identity(123); // 自动推导为number类型泛型类:
class Stack<T> { private arr: T[] = [] public push(item: T) { this.arr.push(item) } public pop() { this.arr.pop() } } // 泛型约束(约束为接收number和string类型 type Params = number | string; class Stack<T extends Params> { …… }(5)结构类型:结构匹配只需要匹配对象字段的子集
interface Point { x: number; y: number; } function logPoint(p: Point) { console.log(`${p.x}, ${p.y}`); } const point = { x: 12, y: 26, z: 89 }; logPoint(point); // 打印 "12, 26"(6)接口继承
// 接口 interface User { id: number; name: string; } // 继承User,新增属性 interface AdminUser extends User { role: "admin"; permissions: string[]; } // 定义AdminUser接口的对象 const admin: AdminUser = { id: 2, name: "wangwu", role: "admin", permissions: ["create", "delete"] };(7)联合类型
type Status = "pending" | "success" | "failed"; let taskStatus: Status = "pending";(8)交叉类型(注:交叉类型如果是基本数据类型,合并的时候同名会出现never
type Name = { name: string }; type Age = { age: number }; type Person = Name & Age; // 同时包含name和age属性 const person: Person = { name: "zhangsan", age: 18 };(9)函数类型
// 写法一: type AddFunc = (a: number, b: number) => number; const add: AddFunc = (x, y) => x + y; // 写法二: function add(a: number, b:number): number { return a + b; }(10)索引类型
function getValue<T extends object, U extends keyof T>(obj: T, key: U) { return obj[key]; }
3、类型守卫(其实这和JS一致
typeof、in、instanceof、字面量、自定义类型保护,使用is关键字
示例:
if (typeof name === 'string') {} if ('name' in Person) {} if (arg instanceof Person) {} if (arg.name === 'chuck') {}
4、interface、type区别
使用 interface 定义接口, 接口名称一般首字母大写,定义接口的时候,只定义声明即可,不包含具体内容
实现接口的时候,要实现里面的内容,定义的变量比接口少了或多了属性都是不允许的
5、内置类型、infer
Readonly:只读
Required:全部变成必填
Partial:让所有属性都变成可选
NonNullable:去除null和undefined
Pick:在对象中挑选
infer:在 extends 条件语句中待推断的类型变量
type Readonly<T> = { readonly [P in keyof T]: T[P]; }; // T所有属性只读 type Required<T> = { [P in keyof T]-?: T[P] }; // T所有属性必选 type Partial<T> = { [K in keyof T]?: T[K] }; // T所有属性可选 type Diff<T, U> = T extends U ? never : T; // 找出T类型中U不包含的部分 type NonNullable<T> = Diff<T, null | undefined>; // 剔除null和undefined //将T类型中的K键列表提取出来,生成新的子键值对类型 type Pick<T, K extends keyof T> = { [P in K]: T[P] } // 如果T能赋值给(param: infer P) => any,则结果是(param: infer P) => any类型中的参数P // 否则返回为 T,infer P表示待推断的函数参数。 type ParamType<T> = T extends (param: infer P) => any ? P : T;
6、.d.ts
npm install --save-dev @types/node
ts会检测根目录下所有.d.ts文件,里面用declare声明的都是全局的。
declare声明的都没有具体的实现,.d.ts只是为了使代码不报错,没有任何实际功能。
eg: 用script引入jq,直接$()会报错,在.d.ts声明:
declare function $(){}
7、常用工具类型
(1)+和-:两个关键字用于映射类型中给属性添加修饰符,比如-?就代表将可选属性变为必选,-readonly代表将只读属性变为非只读。type Required<T> = { [P in keyof T]-?: T[P] };(2)Exclude<T, U>: Exclude 的作用是从T中排除出「可分配」给U的元素。
// 如果T不能赋值给U,则返回该值。如果有多个值不能赋值,则TT是联合类型。 type Exclude<T, U> = T extends U ? never : T; type TT = Exclude<1 | 2, 1 | 3>; // 2(3)Omit<T, K>:
Omit的作用是忽略T中的某些属性// type Omit<T, K> = Pick<T, Exclude<keyof T, K>>; type Foo = Omit<{ name: string, age: number }, 'name'> // { age: number }(4)Compute:将交叉类型合并
type Compute<A extends any> = A extends Function ? A : { [K in keyof A]: A[K] }; type R1 = Compute<{ x: 'x' } & { y: 'y' }> // {x: "x", y: "y"} type R2 = Compute<{ x: 'x'; y: 'y1' } & { y: 'y' }> // {x: "x", y: never}(5)Merge:将两个对象的属性合并
// 先忽略掉O2中存在于O1的属性,然后再与O1合并 type Merge<O1 extends object, O2 extends object> = Compute<O1 & Omit<O2, keyof O1>>; type O1 = { name: string; id: string; }; type O2 = { id: number; from: string; }; type R2 = Merge<O1, O2>; // {name: string, id: string, from: string}(6)Extract<T, U>:提取
T中可以赋值给U的属性type Extract<T, U> = T extends U ? T : never;(7)Intersection<T, U>:取同时存在
T和U的属性type Intersection<T extends object, U extends object> = Pick< T, Extract<keyof T, keyof U> & Extract<keyof U, keyof T> >; type Props = { name: string; age: number; visible: boolean }; type DefaultProps = { age: number }; type DuplicatedProps = Intersection<Props, DefaultProps>; // { age: number; }(8)Overwrite<T, U>:用
U的属性覆盖T的相同属性type Overwrite< T extends object, U extends object, // 将「差集」和「交集」进行「交叉」 I = Diff<T, U> & Intersection<U, T> > = Pick<I, keyof I>; // From T, pick a set of properties whose keys are in the union K type Pick<T, K extends keyof T> = { [P in K]: T[P]; };(9)Mutable<T>:将
T的所有属性的readonly移除type Mutable<T> = { -readonly [P in keyof T]: T[P] } // Make all properties in T readonly type Readonly<T> = { readonly [P in keyof T]: T[P]; };(10)Record
// Construct a type with a set of properties K of type T type Record<K extends keyof any, T> = { [P in K]: T; }; type K = 'A' | 'B' | 'C'; // {A: {id: number}, B: {id: number}, C: {id: number}} type result = Record<K, { id: number }>
8、类与面向对象相关概念
- 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
- 对象(Object):类的实例,通过 new 生成
- 面向对象(OOP)的三大特性:封装、继承、多态
- 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
- 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
- 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat
- 存取器(getter & setter):用以改变属性的读取和赋值行为
- 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法
public 公有属性或方法,可在任何地方被访问,默认所有属性和方法都是 public的
private 私有属性或方法,不能在声明它的类的外部访问,也不可以在子类中访问
protected 受保护的属性或方法,和 private 类似,区别是它可以在子类中访问
- 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
- 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口
示例:
// 1、接口 interface Person { getName(): string; } // 2、类 class Student implements Person { // 4、implements类实现接口 // 5、修饰符 private id: number; // 私有属性,仅类内部可访问 public name: string; // 公共属性,默认可省略 public constructor(id: number, name: string) { this.id = id; this.name = name; } getName(): string { return this.name; } private getStudentId(): number { return this.id; // 私有方法,仅类内部可调用 } } // 3、对象 const student = new Student(101, "Charlie"); console.log(student.getName()); // 正确 console.log(student.id); // 错误:属性“id”为私有属性,只能在类“Student”中访问 // 6、继承 interface User extends Person, Person2 { // 可以继承多个接口 broadcast: () => void }