TS的类型

164 阅读5分钟
  1. 类型系统

更确切地说,TypeScript的类型系统是结构类型系统(Structural type system),任两个以相同结构所描述的值的类型都是等价的。(与之相反的是,标明类型系统Nominative type system,表示类型若要相等,就必须具有相同的“名字”)
  1. never类型

    这个类型在其他语言里比较少见,Java8里没有这个类型。 never代表永远不会发生的类型的值,和throw搭配使用,表示它后续的代码将unreachable,即不可到达。

    never特性:

    • 特性1: 通常在方法里和抛出异常throw搭配使用
    • 特性2: never是所有类型的子类型,因此可以把never类型值赋给任意类型变量。
    • 特性3: never没有子类型,因此所有类型的值都不能赋给never类型变量 注:在ts中,父类型不能赋值给子类型,但是子类型可以赋值给父类型
  2. 交叉类型(融合了多个类型): &

    interface Bird {
     name: string;
     fly(): void;
    }
    interface Person {
     name: string;
     talk(): void;
    }
    type BirdPerson = Bird & Person;
    let p: BirdPerson = { name: "zhufeng", fly() {}, talk() {} };
    
  3. 联合类型

    interface Bird {
     name: string;
     fly(): void;
    }
    interface Person {
     name: string;
     talk(): void;
    }
    type BirdPerson = Bird | Person;
    let p: BirdPerson = { name: "zfeng", fly() {} }; 
    let p1: BirdPerson = { name: "zfeng", talk() {} };
    
  4. 别名类型: type

    type JavaCoder = {
     name: string,
     age: number,
     language: 'Java'
    }
    
    type Container<T> = { value: T };
    别名类型不像interface、class、字面量类型那样,它不是新建的类型,新建的是名称。定义一个新的名称和已有的类型对应起来,然后已有的类型就有了新的类型名字。
    
  5. 内置条件类型

    type Extract<T, U> = T extends U ? T : never;
    type Exclude<T, U> = T extends U ? never : T;
    type NonNullable<T> = T extends null | undefined ? never : T;
    
    type N = NonNullable<string | number | null | undefined>;// 删除null和undifined;
    type E = Exclude<string | numberstring>; // 排除关系 输出 string;
    type I = Extract<string | numberstring>; // 包含关系 输出 number;
    
  6. 索引类型

包括两个方面,类型里的字段名(键)和字段值的类型(值)的访问。对类型的键的访问使用keyof操作符,对类型的字段值类型的访问类似通过对象属性名访问值的操作。

访问索引键 -- keyof

keyof实现:生成以类型属性名为字符串字面量类型的联合类型

class Task {
    name: string;
    createTime: string;
    taskStatus: number;
    taskType: number;
    private duration: number;
}

type TaskKey = keyof Task;

type TaskKey2 = 'name' | 'createTime' | 'taskStatus' | 'taskType';

访问索引值

type TaskKey = keyof Task;
type TaskValueType = Task[TaskKey]; // string | number
type TaskValueType2 = Task[keyof Task];

一个特殊的例子

interface Dictionary {
    name: string;
    type: string;
    [key: string]: string
}
let keys: keyof Dictionary;

keys的类型是什么?直观感觉是string,但是变量keys实际的类型是string|number。TypeScript编译器之所以给出string|number联合类型,是因为考虑到转成JavaScript后,访问对象属性时,键的类型可以是string或者number,比如obj['key'], array[3],为此做了兼容。

  1. 映射类型

需要使用操作符: in, 映射类型是循环访问类型内部的字段名,然后按照一定的条件生成新类型,也就是以相同的形式去转换旧类型的每个属性。

type Person = {
    name:string,
    age:number
}
type PersonKey = keyof Person;
type PersonValueType = Person[PersonKey]  //string|number

type PersonMapKey = { [K in PersonKey]: PersonValueType } 

type Person2 = {
    name: string |number;
    age: string | number;
}
//PersonMapKey和Person2等效

在类型中,对in操作符的使用和在for..in..遍历的使用相同,引入了变量K,只是这里的K代表的是类型变量。

上述PersonMapKey的效果是Person2,和Person存在差异。

type PersonKey = keyof Person;
type PersonMapKey3 = { [K in PersonKey]: Person[PersonKey] }

type PersonMapKey4 = { [K in keyof Person]: Person[K] }

PersonMapKey4和Person效果一样的,PersonMapKey4就是对Person的映射类型。

简单应用

定义一个Person类型的所有属性都为可选属性的类型,如何实现?

type PersonWrapper5 = { [K in keyof Task]?: Task[K] }

再拓展一步,现在不是对特定的Person类型,而是对任意某个类型里的所有属性都变为可选属性,如何对这样的类型的做定义呢?

任意类型,即要求类型是输入参数(类型做参数化),因此需引入泛型:

type PersonWrapper<T> = { [K in keyof T]?: T[K] }

内置的映射类型

Pick

从 T 中取出 一系列 K 的属性

type Pick<T, K extends keyof T> = { [P in K]: T[P] };

interface Person {
  name: string;
  age: number;
  visiable: boolean;
}
type Person1 = Pick<Person, 'name'|'age';  // Person{name:string;age:number}

Omit(反向)

interface Person {
  namestring;
  agenumber;
  visiableboolean;
}
type Exclude<T, U> = T extends U ? never : T;
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type Person2 = Omit<Person"age">;  //Person{age:number}

infer关键字

用来修饰待推断的类型变量,并定义为参数。看个例子

type ParamType<T> = T extends (param: infer P) => any ? P : T;

如果泛型是函数类型,则返回入参类型;否则返回泛型本身。infer强大之处在于提供了另一种声明类型参数的方式。

类型体操

在讲解类型编程之前,先提出一个问题:

类型在类元素里扮演什么角色?

一个类的构成元素:

1.属性字段

  • 修饰词
    • readonly
    • private, protected, public
    • static
  • 属性类型
  • 默认值

2.方法

1).种类

  • 构造方法
  • 静态方法
  • 实例方法

2).组成成分

  • 修饰词- 权限修饰词(private,protected, public), static, super
  • 方法名
  • 方法入参,参数类型
  • 返回类型
  • 方法体

由此可知,类型在类的属性字段,方法入参,方法返回这三处出现,类型扮演对数据分类的作用。

处理类型的常用操作符一览表:

  • instanceof:实例判断
  • typeof:类型判断
  • as: 类型强制转换
  • is:断言返回布尔类型
  • ?:条件类型
  • keyof:键名索引
  • in:映射
  • infer:声明待推断的类型
  • <>:泛型
  • type:别名
  • |: 联合类型
  • &: 交叉类型

什么是TypeScript的类型编程?

通过TypeScript操作符,把类型当作参数进行逻辑处理,从而获得新的类型的过程称为类型编程。

把类型声明为参数,有三种方法:

  1. 泛型<>
  2. in映射
  3. infer

其中大部分都是通过泛型来引入,因此泛型是TS类型编程的基础。