«

基础知识整理-5-TypeScript

codeyx 发布于 阅读:53 笔记


解决 JS 没有类型检测的痛点(根据 V8 优化代码的原理,其实也可以提升一些性能)

TypeScript 的基础类型:数字、布尔、字符串、数组、元组、联合、枚举、any、unknow、void、undefined、Never
高级类型:union 组合类型、Nullable 可空类型、Literal 预定义类型

//一般的写法 添加类型注解
function add(n1: number, n2: number) {}
//数组的写法
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];
//泛型就是指在类定义时不会设置类中的属性或方法参数的具体类型,而是在类使用时(创建对象)再进行类型的定义。
//会在编译期检查类型是否错误。
//tuple元组,是固定长度、固定类型、顺序固定的数组。目前有bug,可以通过push的方式加进去,声明时一定指定类型
// 存放不同类型的数据
let list6: [number, string] = [1, "alwx"];
//联合类型Union和文献(字面量)类型Literal,联合类型就是指一个变量支持多种类型。使用时也需要缩小类型,从而推断出更具体的类型
let union: string | number;
//字面量类型
let union2: 0 | 1 | 2; //如果给他赋值为3就会报错,只能是0、1、2
//枚举类型Enum
enum Color {
  red = 5,
  green = 10,
} //typescript提供基于数字和基于字符串的枚举。
//ts中的枚举类型和普通的js对象本质上没有区别,只是对于开发者来说,相较于直接使用值类型,枚举类型更易读
//any和unknow(未知类型)
//当不知道用什么类型时可以用any,它支持任何类型。
//unknown和any很类似,不同的是unknown要求要经过类型确认才能使用,否则报错。
//使用any的优势是开发快,但是会有安全隐患,unknown会更安全一些,但是要写更多代码。

类型适配(类型断言)

let message: any;
message = "abc"; // 此时还是any 无法自己推导,使用字符串方法时没有正确的代码提示
(<string>message).indexOf(
  // 手动指定类型,即为类型断言
  // 第二种方式
  message as string
); // 使用as关键字

在声明标识符时,如果有直接赋值,那么 ts 会根据赋值的类型推导出标识符的类型注解,这个过程被称为“类型推导”。
let 进行类型推导,推导出来通用类型(string、number 等等)
const 进行推导,推导出来字面量类型

let name = "zhangsan"; // 此时name就是string类型

注意:真实开发中,数组内一般存放相同的类型,不要存放不同类型

在定义函数时,通常参数时需要明确指定类型的,函数结果会自动推导。

function sum(num1: number, num2: number): number {
  return num1 + num2;
}

定义对象的类型可以使用 type 定义
匿名函数最好不要加类型注解,比如给 forEach 传入的匿名函数。它会根据上下文自动推导。
某些情况下,无法确定一个变量的类型,并且可能它会发生一些变化,这时可以用 any 类型(比如服务器返回的复杂数据)
函数在没有返回值的时候就是 void 类型:void 意思是这个东西不存在
undefined 意思就是这个东西存在但没赋值
never 是永远执行不完的函数

类型别名
例如用了很多联合类型后,会导致一行代码过长。此时可以给类型起别名。
type 别名 = 类型...
interface 接口也可以用来声明对象的类型,一般建议对象使用 interface,基本类型数据使用 type
这两个有些区别:
interface 只能用来声明对象,不能用于基本类型
声明对象时,interface 可以声明多次,多次声明会合并(type 不允许两个同名别名同时存在)
interface 支持继承(extends)
interface 可被类实现 (implements)

交叉类型:通常使用在对象中,让一个对象实现多个类型的要求
继承是可以实现属性和方法的复用,但无法约束类型

interface Shape {
 shape: string;
}
interface Color {
 color: string;
}
type Circle = Shape & Color;
interface IPerson {
  name: string;
  age: number;
}
interface IStudent {
  name: string;
  score: number; // ?: 可选
}
const info: IPerson & IStudent = {
  name: "zhangsan",
  age: 18,
  score: 90,
};

交叉类型和联合类型的区别:
交叉类型主要针对对象,是将多个类型合并成一个类型,包含了所有类型的特性
联合类型是将多个类型合并成一个类型,只能使用共有的特性

type和interface的区别:
interface可以继承,type不能
type可以派生新类型,interface不好处理
在Vue或者React里定义Props,直接用interface
针对组件定义一些属性类型的时候,用type

类型断言:as
当 ts 不能自动推导出准确类型的时候可以手动指定
非空类型断言:!.
例如接口的某个属性是可选时,那在使用这个属性时就可能是空,要用可选链?.来使用。在给它赋值时,如果确定它是非空的,可以用非空类型断言。

函数类型表达式

type FunType = (num: number) => number;
const foo: FunType = (num): number => {
  return num + 1;
};

注:对于传入的参数个数不进行检测

函数的调用签名:从对象的角度看待这个函数,也可有其他属性

type Point = {
  (a: number, b: number): number;
};
// 或者
interface IBar {
  name: string;
  age: number;
  (num: number): number; // 函数的调用签名(参数列表): 返回值类型
}

构造签名
用于描述构造函数

class Person {}
interface IPerson {
  new (): Person;
}
function factory(fn: IPerson) {
  new fn();
}

函数的重载
先编写重载签名
再编写通用的函数实现

function add(arg1: number, arg2: number): number;
function add(arg1: string, arg2: string): string;
function add(arg1: any, arg2: any): any {
  return arg1 + arg2;
}

TS 面向对象

class Person {
  name: string; // 必须声明成员属性,否则找不到
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
const p = new Person("zhangsan", 18);
console.log(p.name); // 如果没有声明成员属性会找不到

类的属性和方法支持三种修饰符:public、private、protected
readonly 修饰符可以让属性只读

getters/setters
一些私有属性不能直接访问,或者想要监听某属性的获取和设置的过程,这时使用存取器。
可以规避一些不合理的赋值

class Person {
  private _name: string; // 私有属性一般前面_开头,也是为了防止和存取器同名

  constructor(name: string) {
    this._name = name;
  }
  set name(newV: string) {
    this._name = newV;
  }
  get name() {
    return this._name;
  }
}
const p = new Person("zhangsan");
p.name = "abc";

参数属性
可以把一个构造函数的参数转换成一个同名同值的类属性
直接在构造函数参数前面加上可见性修饰符(public/private/protected 或者 readonly)就可以。(语法糖)

抽象类 abstract、抽象方法
继承是多态的使用前提
所以在定义很多通用的调用接口时,通常会让调用者传入父类,通过多态来实现更灵活的调用方式
但是,父类本身可能并不需要对某些方法进行具体实现,所以父类中定义的方法可以定义为抽象方法

abstract class Shape {
  abstract getArea(); // 抽象方法必须在抽象类中,抽象类不能被实例化
}
class Rect extends Shape {
  constructor(public width: number, public height: number) {
    super();
  }
  // 子类必须实现父类的抽象方法
  getArea() {
    return this.width * this.height;
  }
}
class Circle extends Shape {
  constructor(public radius: number) {
    super();
  }
  // 子类必须实现父类的抽象方法
  getArea() {
    return this.radius ** 2 * Math.PI;
  }
}
// 多个子类有相似的方法,可以在父类中定义抽象方法,在通用函数中就可以通过父类调用不同的子类的方法
function calcArea(shape: Shape) {
  return shape.getArea();
}
calcArea(new Rect(10, 20));
calcArea(new Circle(5));

对象类型的修饰符

type PersonType = {
  name?: string; // 可选属性
  readonly age: number; // 只读属性
};

索引签名
有时候不知道一个类型里所有属性的名字,但知道它们的特征,这时通过索引签名来描述可能的值的类型

interface IPerson {
  [index: number]: string; // 需要使用数字索引获取数据,只能是number或string
}

接口可以被多层实现,而抽象类只能单一继承
抽象类中可以有实现体,接口中只能有函数的声明
类可以实现多个接口

泛型
函数在定义时不固定类型,被使用时再传入类型

function foo<T>(arg: T) {}
// 在使用时再确定参数类型
foo<number>(123); //也可省略,因为它会自动类型推导

泛型接口

interface IPerson<T = string> {
  // 可设置默认值
  name: T;
}
const p: IPerson<string> = {
  name: "zhangsan",
};

泛型类

class Point<T = string> {
  x: T;
  y: T;
}

泛型约束
<T extends xxx> 既有泛型的灵活性也不丢失类型的约束

keyof 拿到对象的 key 作为联合类型
typeof 操作符,type XXX = typeof abc 提取 abc 的类型给 XXX

类型查找

类型映射
[item in obj]:xxx
循环对类型进行修饰
ts 中自带 Readonly 可以通过泛型传入 对每项进行 readonly 修饰

映射修饰符
比如想去掉 readonly 修饰符 可以在前面加-
ts 自带 Partial 泛型 可以让每个属性变成可选属性

类型守卫
避免类型不一致的情况
使用不知道什么类型的变量前先typeof判断它的类型再进行操作

双重断言
xxx as any as Point

常量断言

// 声明常量对象时,还是可以直接修改它的属性
const obj = {
  name: "啊啊啊啊",
};
obj.name = "bbbb";
// 使用常量断言 就可以自动为其属性全部都加上readonly
const obj = {
  name: "啊啊啊啊",
} as const;
obj.name = "bbbb"; // 报错

this
给 this 指定类型时要作为函数的第一个参数
function(this:{ value: number }) 意思是 this 必须包含一个 value 属性并且是 number 类型

映射类型
一个类型需要基于另外一个类型,但又不想拷贝
type xxx = {

}

TS 中使用的模块化方案就是 ES Module
如果 ts 文件中无 export 或者 import,它就是一个非模块。如果希望它被作为模块处理可在文件最后添加export {}

.d.ts文件用来做类型的声明(declare),称之为类型声明(Type Declaration)或者类型定义(Type Definition)文件。仅用来做类型检测,告知 ts 我们有哪些类型
内置类型声明时 ts 自带的,内置了 js 运行时的一些标准化 API 声明文件。比如 DOM API、window、Document 等lib.[xxx].d.ts
一些第三方的包如果自己不提供类型声明文件,可以通过社区公有库 https://github.com/DefinitelyTyped/DefinitelyTyped 查找,这是存放类型声明文件的仓库,真正查找的话要去 npm

tsc --init 创建 tsconfig.json 文件

import type {xxx} from 'aaa' 这种方式导入类型可以在打包时安全删除,推荐。