类型变换 - 11


类型变换

前言:TS 要区分类型和值

关键字 作为类型使用 作为值使用
class y y
enum(枚举) y y
interface(接口) y n
type y n
function n y
var/let/const n y

接口是不能当做 值 来用的,比如我们不能把一个接口赋值给另一个接口或变量

1 交叉类型

  • 交叉类型(Intersection Types)表示将多个类型合并为一个类型(其实就是两个接口类型的属性的并集)
interface Bird {
  name: string
  fly(): void
}
interface Person {
  name: string
  eat(): void
}
// 将多个类型合并为一个类型
type BirdMan = Bird & Person;
let p: BirdMan = {
  name: "ruhua",
  fly() { },
  eat() { }
}

2 typeof

  • 可以获取一个变量的类型
  let p = {
    name: "ruhua",
    age: 10
  }
  // 通过 typeof 获取变量 p 的类型
  // type 是用来定义类型的。 let 只能定义值。
  type Person = typeof p;
  let p2: Person = {
    name: "ruhua2",
    age: 20
  }

3 索引访问操作符 []

  • 可以通过[]来获取一个类型的子类型
  interface Person {
    name: string;
    age: number;
    job: {
      name: string
    };
    interests: {
      name: string
      level: number
    }[];
    // interests: Array<{ name: string; level: number}> // 另一种写法
  }

  let myname: Person["job"]["name"] = "ruhua";
  let myLevel: Person["interests"][0]["level"] = 10;

4 keyof 索引类型查询操作符

  interface Person {
    name: string;
    age: number;
    gender: "male" | "female";
    // [orooName: string]: any; // 不能使用 任意属性
  }
  // type PersonKeys = "name" | "age" | "gender"
  type PersonKeys = keyof Person; // 返回一个接口的 key 的集合
  function getValueByKey(val: Person, key: PersonKeys): any {
    return val[key]
  }
  let person: Person = {
    name: "ruhua",
    age: 10,
    gender: "male"
  };
  getValueByKey(person, "name")

5 映射类型

  • 在定义的时候用 in 操作符去批量定义
  interface Person {
    name: string;
    age: number;
    gender: "male" | "female";
  }
  type PartialPerson = { // 通过 映射类型 批量操作 接口 的属性,将其改为可选
    [key in keyof Person]?: Person[key] // ? 代表可选的意思
  }
  let p: Person = { // p 的属性必须完全和 Person 接口的一样
    name: "ruhua",
    age: 10,
    gender: "male"
  }
  let p2: PartialPerson = {
    name: "ruhua",
  }

6 内置工具类型

  • TS 中内置了一些工具类型来帮助我们更好的使用类型系统

6.1 Partial (部分的)

  • Partial 可以将传入的属性由非可选变为可选,具体使用如下:
  interface Person {
    name: string;
    age: number;
    gender: "male" | "female";
  }
  // Partial 的实现原理:
  // type Partial<T> = {
  //   [key in keyof T]?: T[key]
  // }
  type PartialPerson = Partial<Person>
  let p: PartialPerson = {
    name: "ruhua",
  }

6.2 Required (要求的)

  • Required 可以来将传入的属性中的可选项变为必选项,这里用了 -?修饰符来实现
  interface Person {
    name?: string;
    age?: number;
    gender: "male" | "female";
  }
  // Required 的实现原理:(通过 -? 来实现)
  // type Required<T> = {
  //   [key in keyof T]-?: T[key]
  // }
  // 通过 Required 将接口 Person 里的每个属性都改成必选项
  type RequiredPerson = Required<Person>
  let p: RequiredPerson = {
    name: "ruhua",
    age: 0,
    gender: "male"
  }

6.3 Readonly

  • 将属性变成只读属性
  interface Person {
    name: string;
    age: number;
    gender: "male" | "female";
  }
  // Readonly 实现原理
  // type Readonly<T> = {
  //   readonly [key in keyof T]: T[key]
  // } 

  // Readonly
  type ReadonlyPerson = Readonly<Person>
  let p: ReadonlyPerson = {
    name: "ruhua",
    age: 0,
    gender: "male"
  }
  p.name = "xiaomei" // 报错:无法分配到 "name" ,因为它是只读属性

6.4 Pick (挑选)

  • 将接口的某一项属性挑选出来
  interface Person {
    name: string;
    age: number;
    gender: "male" | "female";
  }
  // Pick 实现原理
  // type Pick<T, K extends keyof T> = {
  //   [key in K]: T[key]
  // }

  // Readonly
  type PickPerson = Pick<Person, "name">
  let p: PickPerson = {
    name: "ruhua"
  }

6.5 映射类型修饰符的控制

  • TS 中增加了对映射类型修饰符的控制
  • 具体而言,一个 readonly修饰符在一个映射类型里可以使用前缀+-来表示这个修饰符应该被添加或移除
  • TS 中部分内置工具类型就利用了这个特性(Partial、Required、Readonly、...),这里我们可以参考 Partial、Required 的实现

7 条件类型

  • 在定义泛型的时候能够添加进逻辑分支,以后泛型更加灵活

7.1 定义条件类型

namespace e {
  interface Fish {
    name1: string;
  }
  interface Water {
    name2: string;
  }
  interface Bird {
    name3: string;
  }
  interface Sky {
    name4: string;
  }
  // TS 中的 extends 并不是真的继承,而是判断属性的: `obj1 extends obj2 ? true : false`, obj1中的属性是否包含obj2中的全部属性
  type Condition<T> = T extends Fish ? Water : Sky
  let condition: Condition<Fish> = {
    name2: "waooo water"
  }
}

7.2 条件类型的分发

  interface Fish {
    name1: string;
  }
  interface Water {
    name2: string;
  }
  interface Bird {
    name3: string;
  }
  interface Sky {
    name4: string;
  }
  type Condition<T> = T extends Fish ? Water : Sky
  let c: Condition<Fish | Bird> = {
    name2: "water"
  }
  let c2: Condition<Fish | Bird> = {
    name4: "sky"
  }
  // 等同于如下写法
  let c3: Water | Sky = {
    name2: "water" 
  }

7.3 内置条件类型

  • TS 内置了一些常用的条件类型
7.3.1 Exclude 排除类型
  • 从 T 可分配给的类型中排除 U
type Exclude<T, U> = T extends U ? never : T;
type MyExclude = Exclude<'1' | '2' | '3', '1' | '2'> // type MyExclude = "3"
7.3.2 Extract 抽取类型 取交集
  • 从 T 可分配的类型中提取 U
type Extract<T, U> = T extends U ? T : never;
type MyExtract = Extract<'1' | '2' | '3', '1' | '2'> // type MyExtract = "1" | "2"
7.3.3 NonNullable 非空检测
  • 从 T 中排除 null 和 undefined
type NonNullable<T> = T extends null | undefined ? never : T
type MyNone = NonNullable<'a' | null | undefined> // type MyNone = "a"
7.3.4 ~ 7.3.7 下面的内置条件类型,将用到 infer 类型推断
  • ReturnType 返回值类型(获取函数类型的返回类型,redux 中会用到)
function getUser(a: number, b: number) {
  return { name: 'xiaomi', age: 10 }
}
// 实现原理
type ReturnType<T> = T extends (...args: any) => infer R ? R : never
type MyReturn = ReturnType<typeof getUser> // type MyReturn = { name: string; age: number; }
  • Parameters 参数类型
function getUser(a: number, b: number) {
  return { name: 'xiaomi', age: 10 }
}
// 实现原理
type Parameters<T> = T extends (...args: infer R) => any ? R : any;
// 使用举例
type MyParams = Parameters<typeof getUser>; // type MyParams = [a: number, b: number]
  • ConstructorParameters 构造函数参数类型
class Person {
  constructor(name: string, age: number) { }
}
// 实现原理
type ConstructorParameters<T> = T extends { new(...args: infer R): any } ? R : never;
// 使用举例
type MyConstructor = ConstructorParameters<typeof Person> // type MyConstructor = [name: string, age: number]
  • InstanceType 实例类型(获取构造函数的实例类型)
class Person {
  constructor(public name: string, public age: number) {}
}
// 实现原理
type InstanceType<T> = T extends { new(...args: any): infer R } ? R : any;
// 使用举例
type MyInstance = InstanceType<typeof Person> // type MyInstance = Person
let p: MyInstance = new Person("ruhua", 12);

8 infer实践

// 将数组类型转化为联合类型
// 实现原理
type ElementOf<T> = T extends Array<infer E> ? E : never;
// 使用举例
type TupleToUnion = ElementOf<[string, number, boolean]>; // type TupleToUnion = string | number | boolean

// 将两个函数的参数转化为交叉类型
type T1 = { name: string };
type T2 = { age: number };
// 实现原理
type ToIntersection<T> = T extends ([(x: infer U) => any, (x: infer U) => any]) ? U : never;
// 使用举例
type t3 = ToIntersection<[(x:T1)=>any,(x:T2)=>any]> // type t3 = T1 & T2
// 表示要把T1、T2赋予给x,那么x的值就是T1、T2的交集。(参数是逆变的可以传父类)
// TS的类型:TS主要是为了代码的安全性来考虑。所以所有的兼容性问题都要从安全性来考虑!
posted @   真的想不出来  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示