探索Typescript类型体操(O_o)

779 阅读8分钟

ts 工作流程

a.ts/b.ts 编译==>a.js/b.js 打包==>main.js 部署==>浏览器

ts 中的类型

基本数据类型

boolean/number/string/symbol/array/enum/null/undefined这些是基本数据类型

像boolean类型的值只有true/false两种

构造函数类型

Boolean/Number/String/Symbol/Array/Object/Enum 是上面对应基本类型的封装类,包含属性和方法

其他类型

Ts中还有额外的any/unknown类型/tuple元组类型/void/object/never:

  1. any是类型系统的顶级类型,可以作为类型检测的逃生舱,但是会避开类型检测
  2. 和any一样,所有类型都可以赋值给unknown,unknown类型只能赋值给any和其本身
  3. 数组一般由同类型值组成,tuple元组通常用来存储不同类型的值
  4. void表示没有任何类型返回,通常用于函数没有返回值时
  5. object是ts引入的新类型,表示非原始类型;Object是所有Object类的实例的类型
  6. never表示永不存在值的类型,通常是在新增联合类型后仍然没有对应的实现,目的是写出绝对安全的代码
enum的数字枚举类型支持从成员名称到成员值,成员值到成员名称的双向映射
添加了const之后被称为常量枚举,解析成js代码时不会为枚举类型编译生成任何js代码
const? enum Direction{ NORTH,SOUTH,EAST,WEST }

联合类型

联合类型:const someVal = string|number ; 
type mouseEvents = 'click'|'scroll'|'mouseover'
可辨识联合类型:包含可辨识、联合类型、类型守卫3个要点
类型别名:给一个类型起个新名字 type Msg = string|string[]
交叉类型: & 修饰符,type Ponit = Ponit1&Ponit2,
表示实现Point类型等于同时实现Ponit1和Ponit2类型

ts 的断言

一般使用的是as语法:const valueLength:number = (someVal as string).length
非空断言:后缀表达式操作符 ! 可以用于断言操作对象是非null和非undefined的
和断言类似的还有ts的 ? 可选链: let x?:number 不确定该属性是否会被传入,
可以理解为参数自动加上undefined
与 ? 可选链相反的确定赋值断言: let x!:number 确定赋值断言,
ts编译器知道该属性会被明确赋值
​
延伸:
    ? 的意思基本和 && 是一致的,即: a?.b === a.b? a.b : undefined
    ?? 和 ||的意思有点相似,前者更严谨一些,
    即: ??会排除nullundefined,但是不会排除0

ts 中的类型守卫

1.in关键字: 判断某个属性名是否在接口定义的对象中
2.typeof关键字: 只支持number/string/boolean/symbol四种基础类型的判断,
  支持两种形式:
    typeof v === "typename"
    typeof v !== "typename"
3.instance关键字:将某个属性的类型收窄为xxx

ts 函数

使用箭头函数,有参数类型和返回类型,参数可以是可选并有默认值得,
有函数类型的定义,具备函数重载的能力,函数重载接触较少使用场景比较有限

ts 接口

interface接口是对行为的抽象,具体如何行动需要由类实现
1.定义任意属性 [propName:String]:any
2.和类型别名都可以来描述对象的形状或函数签名;
  类型别名还可以用于一些其他类型:原始类型/联合类型/元组
  interface可以继承interfacetype,type可以继承type但是不能继承interface
3.接口的实现: implements关键字
  class类可以实现接口或者类型别名,但是不能实现类型别名定义的联合类型
4.接口可以定义多次,会被自动合并为单个接口

ts 类

计算机软件程序设计中进行系统设计时需要分成不同的模块,模块内部有详细的关于该模块功能/属性的实现,通常模块的功能是很复杂的,属性也是很多的,为了内部的功能实现和属性定义,模块内部通过class类进行交互,class类中的方法和属性进行不同class类间的信息交流和功能实现

1.class类的组成:
    constructor构造函数===>执行类中数据的初始化操作
    静态属性/静态方法==>类本身调用
    成员属性/方法===>定义到了类的prototype(原型链)上
    private/protected修饰符和 # 私有字段字符:
        # 私有字符修饰的字段不能在包含的类之外访问也不能被检测到
        private修饰的字段也是类的私有字段,但是可以被检测到
        protected修饰的字段可以被继承
2.extends继承:
    和es6的规则一样,extends字段声明继承,
    需要在constructor中调用super()函数获取父类属性和方法并绑定自身的this
3.抽象类:
    abstract关键字声明的类,抽象类不支持实例化;
    包含一个或多个抽象方法(不包括具体实现的方法);
    可以实例化实现了抽象类所有(包括抽象)方法的子类
4.类方法重载:
    根据传递参数是否传入或者类型不同进行不同的方法调用返回不同的结果

ts 泛型?

软件工程中考虑可重用性,不仅需要支持当前数据类型还要支持未来的数据类型,目的是在成员之间提供有意义的约束,类的实例成员、类的方法、函数参数、函数返回值... 和使用any类型相比,使用泛型创建可复用组件时泛型会保存参数类型

语法
T:Type 表示类型变量名称 function foo<T>(val:T):T { return val}
K:Key 对象中键类型
V:Value 对象中值类型
E:Element 表示元素类型
泛型接口
interface IBaseType<T>{ (arg:T):T } 传入类型T供接口内部定义使用
泛型类
Class BaseType<T>{ zeroValue:T; add(x:T)=>T; }
泛型工具类型(很重要)
typeof操作符
1、获取一个变量声明或对象的类型==>返回类似接口定义的类型
    interface Person {
      name: string;
      age: number;
    }
    const sem: Person = { name: "semlinker", age: 30 };
    type Sem = typeof sem; // type Sem = Person
2、获取函数对象的类型
    function toArray(x: number): Array<number> {
      return [x];
    }
    type Func = typeof toArray; // -> (x: number) => number[]
keyof操作符
获取一个对象中的所有key值,keyof操作符返回类型是联合类型,通常会和typeof结合使用:
   const COLORS = {
      red: 'red',
      blue: 'blue'
    }
​
    // 首先通过typeof操作符获取Colors变量的类型,
    //然后通过keyof操作符获取该类型的所有键,
    // 即字符串字面量联合类型 'red' | 'blue'
    type Colors = keyof typeof COLORS 
    let color: Colors;
    color = 'red' // Ok
    color = 'blue' // Ok
​
    // Type '"yellow"' is not assignable to type '"red" | "blue"'.
    color = 'yellow' // Error
extends操作符
定义的泛型不想过于灵活或想继承某些类,可以对泛型添加约束规定泛型继承某个接口
(T extends IBaseface)
此时用T可以对传参和返回值都进行约束
in和infer操作符
in操作符主要用来遍历枚举类型
infer操作符很有意思,参考以下代码:
    type Flatten<Type> = Type extends Array<infer Item> ? Item : Type
    type strArr = Flatten<string[]> // type StrArr === string
    type strArr = Flatten<'123'[]> // type StrArr === '123'
关键点在于<infer Item>这里
infer进行变量类型推断,推断的变量类型根据能够匹配最接近extends左边能够接收的类型!
从而推断的类型,会尝试匹配最靠近的类型!
ReturnType
ReturnType可以获取方法的返回类型
1、使用:
ReturnType<T>
    function getUser(){return {name:‘xxx’,age:10}}
    type GetUserType = typeof getUser //()=>{name:'xxx',age:10}
    type ReturnUser = ReturnType<GetUserType>
    
2、内部实现: type ReturnType<T extends (...args: any) => any> = 
T extends (...args: any) => infer R ? R : any;
很明显 ReturnType 内部也是利用条件类型和 infer 关键字,来实现获取方法的返回类型
Parameters
获取函数参数的类型
1、使用:
    Parameters<T>
    type ReturnUserParam = Parameters<GetUserType>
​
2、内部实现:
type Fn1 = (a: number) => string;
type ArgType<T> = T extends ((a: (infer U)) => any) ? U : never;
type Fn1Arg = ArgType<Fn1>; // number
Required
标记属性都是必须的
interface Props{a?:number;b?:string}
const obj:Required<Props> = {a:5} //报错提示b是必须的
Partial
规定给定一个输入的object类型T,返回一个新的object类型,
且返回的object类型的每一个属性都是可选的
type Partial<T> = {
    [P in keyof T]?:T[P]
}
通过keyof T拿到T的所有属性名,使用in进行遍历,并将值赋给P,通过T[P]拿到P?对应的属性值,
此时P代表的每一个属性都是可选属性
Readonly
修饰泛型,转换为只读
interface Todo {
  title: string;
}
​
const todo: Readonly<Todo> = {
  title: "Delete inactive users",
};//报错 title是只读属性不能赋值
Pick
Pick(Type,keys)
从一个类型里取出需要用的属性,组成一个新类型
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}
​
type TodoPreview = Pick<Todo, "title" |"completed">;
//此时TodoPreview就是只有title和completed属性的Todo类型
Record
Record<Type,interface>
将Type做成key对应值得类型,组成一个新类型
interface PageInfo { title: string;}
type Page = "home" | "about" | "contact";
const nav: Record<Page, PageInfo> = {
  about: { title: "about" },
  contact: { title: "contact" },
  home: { title: "home" },
};
//type定义的联合类型的每一个类型作为新类型的key
//第二个参数传入的类型作为value值,组成新的类型对象
ConstructorParameters
获取构造函数的参数类型
type Params = ConstructorParameters<typeof Person>
InstanceType
InstanceType<typeof Person>
返回一个拥有构造函数的实例的类