一. 基础数据类型

1. JS中的类型

TS包含了JS的八种数据类型,并且将它们都定义成基础数据类型。有以下几点需要注意:

  1. nullundefined是任何类型的子集;所以在任何类型都可以被赋值为nullundefined
  2. 配置中设置"strictNullChecks": truenull只能赋值给自身,undefined只能赋值给void和自身
  3. numberbigint类型不兼容,不能直接互相赋值 ```typescript let str: string = ‘a’; let num: number = 2; let bool: boolean = false; let nul: null = null; let undef: undefined = undefined; let bigInt: bigint = 3n; let sym: symbol = Symbol(‘that’); let obj: object = {x: 1};

str = nul; // 只有”strictNullChecks”: false情况下可以 function v(): void{ return undefined; } // “strictNullChecks”: true情况下undefined只能赋值给undefined和void num = bigint // 不可以,两种数据类型不兼容

  1. <a name="tGHHc"></a>
  2. ### 二. 其他类型
  3. <a name="wfkG9"></a>
  4. #### 1. 数组Array
  5. <a name="O7EaG"></a>
  6. ##### 1) 定义数组类型的两种方式:
  7. ```typescript
  8. let arr1: number[] = [1, 2, 3];
  9. let arr2: Array<Number> = [4, 5, 6];

2) 定义联合类型的数组

联合类型的数组可以同时存储多种类型的数据

  1. let uarr: (number | string)[] = [1, 'a', 2, 'b', 'c'];

3) 定义某种对象类型的数组
  1. interface ArrType {
  2. isMe: boolean;
  3. name: string;
  4. }
  5. let tarr: ArrType[] = [
  6. {
  7. isMe: true,
  8. name: 'jay'
  9. },
  10. {
  11. isMe: false,
  12. name: 'chou'
  13. }
  14. ];

2. 函数

1) 函数声明
  1. function funcDec (x: string, y: boolean): void {
  2. return undefined;
  3. }

2) 函数表达式
  1. let funcExpr: (x: string, y: boolean) => void = function (x: string, y: boolean): void {
  2. return undefined;
  3. }

3) 接口定义的函数类型
  1. interface IFunc {
  2. (x: string, y: boolean): void;
  3. }
  4. let FuncObj: IFunc = function(x: string, y: boolean): void {
  5. return undefined;
  6. }

4) 可选参数

可选参数后面不能有必选参数

  1. function optionalFunc(x: string, y: boolean, z?: number): void {
  2. console.log(z)
  3. }
  4. optionalFunc("df", false); // z输出undefined

5) 默认参数

可以通过传入undefined方式告诉函数这个参数不传值

  1. function defaultFunc(x: string = 'fg', y: boolean):void {
  2. console.log(x)
  3. }
  4. defaultFunc(undefined, false);

6) 剩余参数
  1. function restFunc(x: string, y: boolean, ...items: string[]): void {
  2. items.forEach((item) => {
  3. console.log(item)
  4. });
  5. }
  6. restFunc('dg', true, '43', 'hs');

7) 函数重载

函数重载是一种调用相同函数名,不同参数数量,类型和返回值类型的能力。调用的函数会依次匹配声明的函数

  1. type combinable = string | number;
  2. function reloadFunc(x: number, y: number): number;
  3. function reloadFunc(x: number, y: string): string;
  4. function reloadFunc(x: string, y: number): string;
  5. function reloadFunc(x: string, y: string): string;
  6. function reloadFunc(x: combinable, y: combinable): combinable {
  7. if (typeof x === 'string' || typeof y === 'string') {
  8. return x.toString() + y.toString();
  9. }
  10. return x + y;
  11. }

3. 元组 Tuple

元组可以限制数组的长度和类型;可以作为数组的一种形状

1) 声明与使用

使用下标访问

  1. const tup: [number, boolean, string] = [1, false, 'gs'];
  2. console.log(tup[1]) // false

2) 解构赋值

解构赋值的长度不能超过元组的长度,否则会报错

  1. const tup: [number, boolean, string] = [1, false, 'gs'];
  2. const [id, isMe] = tup; // 1 false

3) 可选类型

添加?的形式添加可选参数。可选类型后面不能有必选类型

  1. const tup: [number?, boolean?, string?] = [];

4) 剩余参数

剩余参数在可选类型后面

  1. const tup: [number?, boolean?, ...number[]] = [1, false, 2, 3];

5) 只读元组

只读元组内部的值不能再改变

  1. const tup: readonly [number?, boolean?, ...number[]] = [1, false, 2, 3];
  2. tup[6] = 4; // 报错,因为是readonly,不能将undefined改变为4

4. void类型

void类型和其他类型平级,一般表示没有返回值的函数的返回类型。不能直接赋值为undefeind以外的值。
strictNullChecks: false时可以赋值为nullundefined

  1. let v1: void;
  2. v1 = undefined;
  3. function v(): void {};

5. never类型

never类型代表永远不存在的值的类型;比如:

  1. 一个抛出错误的函数,它会中断函数;所以不存在返回值 ```typescript function throwFunc(): never { throw new Error(‘throw an error’); }

let bar: never = throwFunc();

  1. 2. 一个无限循环的函数
  2. ```typescript
  3. function loopFunc(): never {
  4. while(true);
  5. }
  6. let foo: never = loopFunc();

never类型和undefinednull一样;是其他类型的子类型。但是除了never类型的值,其他任何类型的值都不能赋值给never,包括any

  1. let a: any = 234;
  2. let bar: never = a; // 报错,any类型不能赋值给never

应用场景:可以进行编译时全面的类型检查,排除联合类型中未实现的部分

  1. type combinable = string | boolean | number;
  2. function checkType(x: combinable): boolean {
  3. if (typeof x === 'string') {
  4. return true;
  5. } else if (typeof x === 'boolean') {
  6. return true;
  7. } else {
  8. const check: never = x; // number逻辑未实现,编译器报错
  9. }
  10. return false;
  11. }

6. any类型

any类型可以被赋值为任何类型,any类型可以赋值给其他任何类型(never除外);可以访问任意属性。
声明未指定类型,未赋值的变量默认为any类型

  1. let defaultAn; // any类型
  2. let an: any = {};
  3. an.getType = '43';
  4. an = 325
  5. let str:string = an; // OK
  6. let undef: undefined = an; //OK
  7. let nul: null = an; // OK
  8. let v: void = an; // OK
  9. let nev: never = an; // Error never不可以被赋值为any
  10. function nevFunc():never {
  11. throw new Error('')
  12. }
  13. let an: any;
  14. an = nevFunc(); // OK any可以被赋值为never

7. unknown类型

unknown类型和any一样都可以被任意类型赋值,但是unknown类型只能赋值给unknownany。除非进行了类型断言和类型判断;否则不能赋值给其他类型

  1. function nevFunc():never {
  2. throw new Error('')
  3. }
  4. let unk: unknown = '543';
  5. let unk2: unknown = unk;
  6. unk = false;
  7. unk = nevFunc();
  8. let an: any = unk;
  9. let str: string = unk; // Error

如果不缩小类型,就不能对unknown类型做任何操作

  1. function getDog() {
  2. return true;
  3. }
  4. let d: unknown = {
  5. getDog
  6. };
  7. d.getDog(); // Error
  8. (d as any).getDog(); // OK

8. Number String Boolean Symbol等包装对象类型

原始类型可以赋值给包装类型,包装类型不可以赋值给原始类型

  1. let num: number = 1;
  2. let Num: Number = 2;
  3. Num = num; // OK
  4. num = Num // Error

9. object,Object和{}

1) object

object代表所有的非原始类型,不包含js中的基本数据类型string, number, boolean, symbol, bigint, null, undefined

  1. let obj: object = {
  2. a: 12
  3. }; // OK
  4. obj = new Date(); //OK
  5. obj = 'dsf'; // Error
  6. obj = 234; // Error
  7. obj = false; // Error
  8. obj = Symbol('324'); // Error
  9. obj = 43543n; // Error
  10. obj = null; // Error
  11. obj = undefined; // Error

2) Object

Object代表所有有toString(), hasOwnProperty()等方法的数据类型(编译器显示支持all objects),所以所有的原始类型,非原始类型都可以赋值给它(nullundefined需要开启strictNullCheck

  1. let Obj: Object;
  2. Obj = {}; // OK
  3. Obj = 1; // OK
  4. Obj = 234n; // OK
  5. Obj = 'gd'; // OK
  6. Obj = false; // OK
  7. Obj = Symbol('sdf'); // OK
  8. Obj = null; // Error
  9. Obj = undefined; // Error

Object既是object的父类型,也是object的子类型。他们之间可以互相赋值

  1. type is_obj_extends_Obj = object extends Object ? true : false; // true
  2. type is_Obj_extends_obj = Object extends object ? true: false // true
  3. let a: is_obj_extends_Obj = true;
  4. let b: is_Obj_extends_obj = true;

3) {}

空对象的行为和Object一致,表示原始类型和非原始类型的集合(nullundefinedstrictNullCheck影响)

  1. let Obj: {};
  2. function nevFunc(): never {
  3. throw new Error();
  4. }
  5. Obj = nevFunc()
  6. Obj = 1;

三. 类型推断

  1. 声明时赋值可以自动进行类型推断
  2. 函数返回值根据return判断
  3. 声明时未赋值,未添加类型的会被推断为any ```typescript let str: string = ‘gsh’; // string

function add(a: number, b = 1) { return a + b; } // function add(a: number, b?: number): number

let an; // any类型 an = 324;

  1. <a name="MYiDB"></a>
  2. ### 四. 类型断言
  3. 类型断言就是告诉编译器按照我的意思执行类型检查。只在编译阶段生效,并不作类型转换
  4. <a name="KIP50"></a>
  5. ##### 1) 类型断言语法
  6. 类型断言有`as number`和`<number>`两种语法,其中`<number>`语法不与jsx兼容
  7. ```typescript
  8. const arrNum: number[] = [1, 3 ,4];
  9. const lagestNum1: number = (arrNum.find((num) => num > 3)) as number;
  10. const lagestNum: number = <number>(arrNum.find((num) => num > 3));

2) 非空断言

非空断言就是断言一个变量类型不是nullundefined,用后置的!表示

  1. let combStr: null | undefined | string;
  2. combStr!.toString(); // 断言combStr排除null和undefined
  3. combStr.toString(); // Error 可能为null和undefined
  4. type NumGen = () => number;
  5. function assetFunc(numGen: NumGen | undefined) {
  6. numGen!(); // 断言排除undefined
  7. numGen(); // Error 可能为undefined
  8. }

3) 确定赋值断言

明确告诉编译器变量会被赋值,类型检查的时候不会报未赋值的错误

  1. let x!: number;
  2. initFunc();
  3. console.log(x * 2);
  4. initFunc() {
  5. x = 10;
  6. }

五. 字面量类型

  1. 字面量可以作为变量的类型;字面量类型只有字符串字面量,数字字面量,布尔字面量类型三种。

    1. let spe: 'this' = 'this';
    2. let str: string = spe;
    3. spe = str; // Error 字符串字面量类型'this'是string的子类型
  2. 字面量类型可以限制变量的类型和取值

    1. interface Config {
    2. dir: 'up' | 'down';
    3. isWalk: false | true;
    4. margin: 0 | 2 | 4;
    5. }
  3. let和const的类型推断其实也是一种类型拓展;

str2是字面量类型,它的父类型是string,所以typeof str2 === 'string'
str是字面量类型,类型是'b',它的父类型是string,所以被允许赋值给其他字符串字面量。推断出的类型就是字面量的父类型

  1. let str = 'a';
  2. const str2 = 'b';

六. 类型拓宽(Type Widening)

1. 普通的类型拓宽

类型拓宽指类型推断的时候编译器将变量的类型进行拓宽,推断为合理的类型

  1. let str = 'this is a string' // 类型拓宽,string类型
  2. let strFunc = (str = 'this is a string') => str // 类型拓宽 (str?: string) => string
  3. const specStr = 'this is a string'; // 没有拓宽 'this is a string'类型
  4. let str2 = specStr; // 类型拓宽 string类型
  5. let strFunc2 = (str = specStr) => str; // 类型拓宽 (str?: string) => string

2. 特殊的类型拓展

nullundefined会自动拓宽为any,变量有确定值的时候类型会收窄为特定的类型

  1. let nul = null // any类型
  2. let undef = undefined; // any类型
  3. let nul2 = nul; // 收窄为null类型
  1. interface Vector2 {
  2. x: number;
  3. y: number;
  4. z: number;
  5. }
  6. function getComponent(vec: Vector2, axios: 'x' | 'y' | 'z') {
  7. return vec[axios];
  8. }
  9. let vec = {x: 10, y: 20, z: 30 };
  10. let x = 'x';
  11. getComponent(vec, x); // x类型string,不能赋值给'x' | 'y' | 'z'类型

3. 限制类型拓展

1) 通过字面量类型限制类型拓展
  1. let specStr2: 'this is a string' = 'this is a string';
  2. let str3 = specStr2; // 限制类型拓展 类型为'this is a string'

2) const数据类型

对于原始类型,const可以限制为字面量类型
对于对象和数组,对象内部的类型会被认为是let声明的变量

  1. const x = 'x' // 限制拓展'x'
  2. const obj = {
  3. x: 1,
  4. y: 2
  5. }; // 内部的属性会拓展 {x: number, y: number}
  6. obj.x = 3; // OK

3) const 断言

const断言从值空间引入,将它收窄为最小的类型推断

  1. let obj2 = {
  2. x: 1,
  3. y: 2 as const
  4. } // { x: number, y: 2 }
  5. let obj3 = {
  6. x: 1,
  7. y: 2
  8. } as const; // { readonly x: 1, readonly y: 2 }
  9. let arr = [1, 4] as const; // readonly[1, 4]

七. 类型收窄(Type Narrowing)

将宽泛的类型判断为一个比较明确的类型称之为类型缩小;

  1. TS中用类型守卫来收窄类型的判断,编译器通过分析代码流判断类型。类型守卫是通过代码影响代码分析流的功能

    1. function narrowFunc(varType: number | string): boolean {
    2. if (typeof varType === 'number') {
    3. varType; // number
    4. return true;
    5. }
    6. varType; // string
    7. return false;
    8. }
  2. 一种常用的做法是通过标签联合或可辨识联合帮助接口类型收窄

    1. interface TypeA {
    2. type: 'A',
    3. name: string
    4. }
    5. interface TypeB {
    6. type: 'B',
    7. age: number
    8. }
    9. type comb = TypeA | TypeB;
    10. function combJudge(x: comb): string {
    11. switch(x.type) {
    12. case 'A':
    13. return 'a';
    14. case 'B':
    15. return 'b';
    16. default:
    17. return 'ha'
    18. }
    19. }

    注意:

  3. 不能通过object排除null类型

    1. const el = document.getElementById('me');
    2. if (typeof el === 'object') {
    3. el; // null | HTMLElement
    4. }
  4. 通过falsy值很多类型无法收窄

    1. function falsyJudge(x: number | string | null | true) {
    2. if (!x) {
    3. x; // number | string | null 只排除了true
    4. }
    5. }

    八. 联合类型

    使用|创建联合类型,联合类型里可以是普通的类型字面量类型
    如果联合类型中普通类型包裹了字面量类型,编译器只会提示普通类型

    1. let str: string | 'hi' = 'jay'; // string 这里是特殊的
    2. let comb: number | 'h1' = 'h1'; // number | 'h1'
    3. let huge: Object | string = true; // Object | string
    4. let bigNum: 12n | 13 = 12n; // 12n | 13

    九. 类型别名

    一个类型联合类型取别名

    1. type otherObj = Object;
    2. type baseNum = 1 | 2 | 4; // 字面量类型也可以取类型别名
    3. let str: otherObj = 'jskdh';

    十. 交叉类型

    交叉类型用&连接两个类型,原子类型取交集,非原子类型取并集
    对于多个接口类型合并,不重名的原子属性取并集,重名的元素属性取交集,对象属性内部规则同理 ```typescript type obj = Object & object; // Object & object 收窄为非原始数据类型 let o: obj = {} // OK let o1: obj = 1; // Error 不符合object type str = ‘hi’ & string; // 收窄为’hi’ type useless = string & number; // never 原子类型没有交集

function nevFunc(): never {throw new Error()} interface ICat { id: string; age: number; feature: { miao: string } } interface IDog { id: number; age: number; isDog: boolean; feature: { wang: string } } type IPet = ICat & IDog; let pet: IPet = { id: nevFunc(), // 重名的原子类型取交集 age: 5, // 不重名的原子类型取并集 isDog: false, feature: { // 对象属性内部和外部同理,不重名合并,重名相交 miao: ‘miao’, wang: ‘wang’ } }

  1. <a name="S1FC3"></a>
  2. ### 十一. 接口
  3. TS中的接口可以是**对类部分行为的抽象**,也可以**描述对象的形状**。
  4. <a name="p3TWB"></a>
  5. #### 1. 接口描述对象形状,要严格一致,属性不能多也不能少
  6. ```typescript
  7. interface ICat {
  8. name: string;
  9. age: number;
  10. }
  11. let c: ICat = { // Error 少了age属性
  12. name: 'ksduhf'
  13. };
  14. let d: ICat = { // Error 多了isDog属性
  15. name: 'dsg',
  16. age: 43,
  17. isDog: true;
  18. };

2. 可选参数,只读参数

使用readonly表明只读参数,可选只读参数也只能在初始化时赋值

  1. interface ICat {
  2. readonly isDog: boolean;
  3. readonly isCat?: boolean;
  4. name: string;
  5. age?: number;
  6. }
  7. let cat: ICat = {
  8. isDog: true,
  9. name: 'cat'
  10. };
  11. cat.age = 32;
  12. cat.name = 'rwe';
  13. cat.isCat = true; // Error
  14. cat.isDog = false; // Error

还有一个ReadonlyArray,是一个只读的数组类型,不能修改数组内容

  1. let rarr: ReadonlyArray<number> = [1, 3, 4];
  2. rarr.push(4); // 不能修改

3. 任意参数

使用索引签名为接口添加任意参数;任意参数只能有一个;任意参数类型必须包含其他参数的类型(可选参数多一个undefined类型)

  1. interface IAnimal {
  2. isDog: boolean;
  3. name: string;
  4. age?: number; // 类型 number|undefined
  5. [k: string]: number | string | boolean | null | undefined;
  6. }

4. 类型兼容性

TS类型的兼容性是基于结构子类型的(也叫鸭式辨型法),结构类型是一种只使用其成员来描述类型的方法,结构类型的兼容性或等价性不要求显示的声明类型,它是基于类型的组成结构。和名义类型形成对比。

  1. 普接口,通对象的兼容性

如果x兼容y,则y需要把包含x的所有属性

  1. interface IX {
  2. name: string;
  3. }
  4. let x: IX;
  5. let y = {
  6. name: 'hi',
  7. age: 23
  8. };
  9. x = y; // OK y有x所有成员
  10. y = x; // Error x没有y所有成员
  1. 函数兼容性

参数类型:如果x兼容y,则x需要包含y所有参数类型且能对齐

  1. let x = (x: string, y: number) => 0;
  2. let y = (a: string) => 0;
  3. x = y; // OK y的参数类型都能在x中找到
  4. y = x; // Error x的参数类型number在y中找不到

返回值类型:如果x兼容y,则y需要包含x所有参数

  1. let x = () => ({age: 13});
  2. let y = () => ({name: 'jay', age: 12});
  3. x = y; // OK y包含了x的所有参数
  4. y = x; // Error x没有包含y的所有参数
  1. 类的兼容性

类型兼容只比较实例属性,不比较静态属性和构造函数
X要兼容Y,则Y需要有X所有实例属性

  1. class X {
  2. jump: boolean = false;
  3. constructor(name: string, jump: boolean){}
  4. }
  5. class Y {
  6. jump: boolean = true;
  7. constructor(){}
  8. run() {
  9. }
  10. }
  11. let x = new X('jay', false);
  12. let y = new Y();
  13. x = y; // OK y有x所有实例属性
  14. y = x; // Error x没有y的run属性

5. 绕开额外属性检查的办法

1) 鸭式辨型法

长得像鸭子并嘎嘎叫的就是鸭子,这就是鸭子辨型法。下面的例子如果直接将{name: 'A', age: 23}传入字面量对象会被类型检查限制。但是如果作为普通对象传入就会因为类型兼容不报错。

  1. interface duck {
  2. name: string;
  3. }
  4. function getDuck(d: duck): duck { return d }
  5. let duckA: {name: string, age: number} = {
  6. name: 'A',
  7. age: 23
  8. }
  9. getDuck(duckA); // OK
  10. getDuck({name: 'A', age: 23}) // Error

2) 类型断言 as

使用as告诉编译器自己知道在做什么,只要对象包含接口的属性就可以将对象断言为接口的实现

  1. interface duck {
  2. name: string;
  3. }
  4. function getDuck(d: duck): duck { return d }
  5. getDuck({name: 'A', age: 23} as duck); // OK

3) 在接口定义的时候就是用索引签名
  1. interface duck {
  2. name: string;
  3. [key: string]: any
  4. }
  5. function getDuck(d: duck): duck { return d }
  6. getDuck({name: 'A', age: 23}); // OK

十二. 接口和类型别名的区别

二者都可用来声明对象和方法。
区别1:人如其名,类型别名可用于基本类型,联合类型,元组等取别名

  1. type str = string;
  2. type son = string | number;
  3. type point = [number, number];

区别 2:interface,type可以拓展且可以相互拓展,interface使用extends,type使用&

  1. interface IPoint {
  2. x: number;
  3. y: number;
  4. }
  5. interface I3DPoint extends IPoint { z: number };
  6. type TPoint = {
  7. x: number,
  8. y: number
  9. }
  10. type T3DPoint = TPoint & { z: number };

区别3:interface被重复声明会合并不报错,type重复声明会报错

  1. interface IPoint {
  2. x: number;
  3. y: number;
  4. }
  5. interface IPoint {
  6. isPoint: boolean;
  7. }
  8. // 变成{x: number, y: number, isPoint: boolean}
  9. let p: IPoint = {
  10. x: 1,
  11. y: 2,
  12. isPoint: true
  13. }; // OK

十三. 泛型

泛型就是在定义函数,接口,类的时候不预先定义类型,而是在使用的时候再指定类型。

1. 泛型约束

如果需要泛型支持某个属性,就需要让这个泛型拓展某个接口<T extends someinterface>

  1. interface IPoint {
  2. x: number;
  3. y: number;
  4. }
  5. function getDistance<T extends IPoint>(p: T): number { // 让p具有了x, y属性
  6. return p.x + p.y;
  7. }

2. 泛型工具类型

1) typeof

根据实例获取泛型类型,返回一个type。这个类型可以是推断出来的

  1. interface IPoint {
  2. x: number;
  3. y: number;
  4. }
  5. let p:IPoint = {x: 1, y: 2};
  6. type I = typeof p; // IPoint
  7. let i: I = {x: 2, y: 3};
  8. console.log(i)
  9. // 支持推断出的类型
  10. const cat = {
  11. name: 'Kitty',
  12. age: 3,
  13. feature: {
  14. isBig: false
  15. }
  16. };
  17. type c = typeof cat;
  18. /* 推断出的类型
  19. {
  20. name: string,
  21. age: number,
  22. feature: {
  23. isBig: boolean
  24. }
  25. }
  26. */
  27. const cat2: c = {
  28. name: 'Jay',
  29. age: 1,
  30. feature: {
  31. isBig: true
  32. }
  33. };

2) keyof

keyof返回一个类型,获取类型的所有键。只能用于类型

  1. interface cat {
  2. name: string;
  3. age: number;
  4. feature: {
  5. isBig: boolean
  6. }
  7. }
  8. type k = keyof cat;
  9. const k1: k = 'feature' // OK 类型是"name" | "age" | "feature"

TypeScript支持字符串索引数字索引,其中数字索引是字符串索引的子集。所以keyof字符串索引得到的类型是string|number

  1. interface cat {
  2. [key: string]: string
  3. }
  4. const kit: cat = {
  5. 23: "hello"
  6. }
  7. type c = keyof cat; // string | number
  8. interface cat {
  9. [key: number]: string
  10. }
  11. const kit: cat = {
  12. 23: "hello"
  13. }
  14. type c = keyof cat; // number

应用场景:获取某个泛型对象的key

  1. function getProps<T, K>(obj: T, key: K) {
  2. return obj[key]; // Error
  3. }
  4. function getProps<T, K extends keyof T>(obj: T, key: K): T[K] {
  5. return obj[key];
  6. }
  7. let res = getProps({a: 1, b: 'hi'}, 'b');