likes
comments
collection
share

🌐 TS 高级类型 交叉索引类型

作者站长头像
站长
· 阅读数 3

TS 高级类型 交叉/索引类型

所谓的高级类型就是指TS为了保障语言的灵活性所引入的一些语言特性。 这些特性将帮助我们应对复杂多变的开发场景。

交叉类型

交叉类型是将多个类型合并为一个类型。新的类型具有所有类型的特性。所以交叉类型特别适合对象混入(mixin)的场景。

interface Person{
    run():void;
}

interface Teacher{
   goto():void;
}

let ative:Person & Teacher = {
   run(){},
   goto(){}
};

交叉类型同 "&" 进行连接。 此时的 ative 变量就应该具备两个接口类型所拥有的成员方法。 这里需要注意的是虽然从名称上看交叉类型给人的感觉是类型的交替。 但实际上它是取所有类型的并集。 接下来我们在看联合类型。

联合类型

关所谓的联合类型就是指什么的类型并不确定,可以为多个类型中的一个

const n:string|number = 1;

这里变量 n 的类型是string 和 number 的联合类型,那么它的取值就可以是数字和字符串。 这里我们顺便介绍下字面量类型。

有的时候我们不仅需要限定一个变量的类型,而且还需要限定变量的取值在某一个特定的范围内。

const m:"m"| 2 = 2;

比如我们这里设置了一个变量m,它的类型是字面量的联合类型。 也就是 m 的取值只能是字符串的 "m" 和 2 里面的一种。

接下来我们在讲讲对象的联合类型,我们给两个类都新增加了一个实例方法toString。

enum Type {obj, arr}
class IsObject{
    toOjbect(){
      console.log("hello object");
    }
    toString(){
      console.log("hello toString"); 
   }
}

class IsArray{
    toArray(){
     console.log("hello Array");
    }
    toString(){
      console.log("hello toString"); 
   }
}

function getType(type:Type){
  let target = type === Type.obj ? new IsObject() : new IsArray();
  return target;
}

getType(Type.obj);

当我们在 TypeScript Playground 中把鼠标指向taget 查看其类型的时候输出:let target:isObject | isArray。

function getType(type:Type){
  let target = type === Type.obj ? new IsObject() : new IsArray();
  target.toString();
  return target;
}

此时当我们调用target.toString 并不会报错。

function getType(type:Type){
  let target = type === Type.obj ? new IsObject() : new IsArray();
  target.toObject();
  return target;
}

调用toObject 会报编译错误这是为什么? 如果一个对象被确认是联合类型,当它的类型未被确认的情况下只能访问所有类型的共有成员。 isObject / isArray 的共有成员是toString。 如果我们访问非共有成员方法就会报错。

那么这个时候有趣的事情又发生了,联合类型看起来好像是取所有类型的并集,然而在这种情况下只能访问所有联合类型的交集。所以这里我们要区分下这个概念。

总结:交叉类型适合做对象的混入。 联合类型可以使类型具有不确定性可以增强代码的灵活性。

索引类型

在JavaScript中我们经常会遇到这样一种场景从对象中去获取一些属性的值,然后建立一个集合。

let obj = {
   x: 1,
   y: 2,
   n: 3,
   m: 4
}

我们通过JavaScript来实现下这个需求:

let obj = {
   x: 1,
   y: 2,
   n: 3,
   m: 4
}

function getValue(obj:any, keys:string[]){
     return keys.map(key=>{
            return obj[key];
      });
}

console.log(getValue(obj, ["x","n"]));

这里我们定义了一个名为 getValue 的函数,它接收两个参数 any 类型的对象, 字符串类型的数组。 通过keys.map 获取 x,n 这两个属性的值。

如果我们随意的去指定两个不存在的属性呢?

getValue(obj, ["a","b"]);

//输出: [undefined, undefined] 

此时并不会报错,那么如何使用TS 对这种现象进行约束呢? 这里我们就要利用到索引类型。 要了解索引类型我们首先要了解下其他的概念。

1.索引类型的查询操作符 keyof T

keyof T 表示类型 T 所有公共属性的字面量联合类型。 举个简单例子说明下:

interface Person {
    name: string;
    age: number;
}

let person: keyof Person; // 'name' | 'age'

2.索引访问操作符 T[K]

T[K] 这个的含义就是对象T的属性K 所代表的类型。我们再来举个例子:

interface Person {
    name: string;
    age: number;
}

let person: Person = {
    name: 'Jarid',
    age: 35
};

let personProps:Person['age']; 

这里我们指定 personProps 的类型是Person.age的类型,那么 personProps 类型就为 number。

3. T extend U

表示泛型变量可以通过继承某个类型获得某些属性。 清楚了这三个概念我们就来改造下 getValue 这个函数。

首先我们想把getValue 改造成一个泛型函数,我们需要做一些约束。这个约束就是keys里面的元素,一定是obj 的属性。如何做这种约束呢?

let obj = {
   x: 1,
   y: 2,
   n: 3,
   m: 4
}

function getValue<T>(obj:T, keys:string[]){
     return keys.map(key=>{
            return obj[key];
      });
}

getValue(obj, ["x","n"]);

我们先来定义一个泛型变量T ,来约束obj 。 然后再来定义一个泛型变量K , 用他来约束 keys 数组。

let obj = {
   x: 1,
   y: 2,
   n: 3,
   m: 4
}

function getValue<T, k>(obj:T, keys:K[]){
     return keys.map(key=>{
            return obj[key];
      });
}

getValue(obj, ["x","n"]);

然后我们给 K 来做个类型约束。 让他继承obj 所有类型的联合类型。

let obj = {
   x: 1,
   y: 2,
   n: 3,
   m: 4
}

function getValue<T, K extends keyof T>(obj:T, keys:K[]){
     return keys.map(key=>{
            return obj[key];
      });
}

console.log(getValue(obj, ["x","y"]));


然后我们来设置下返回值:

let obj = {
   x: 1,
   y: 2,
   n: 3,
   m: 4
}

function getValue<T, K extends keyof T>(obj:T, keys:K[]):T[K][]{
     return keys.map(key=>{
            return obj[key];
      });
}

console.log(getValue(obj, ["x","y"]));

首先返回值的类型是个数组,数组的成员的类型就是T[k] 对应的类型。 这样我们就通过一个索引类型把getValue改造完毕了。

getValue(obj, ["a","b"]); 

这个时候当我们指定一个非obj 的属性,编译器就会报错。

Type '"a"' is not assignable to type '"x" | "n" | "y" | "m"'.
Type '"b"' is not assignable to type '"x" | "n" | "y" | "m"'.

由此可见索引类型可以实现对对象属性的查询和访问。 然后在配合泛型约束就能够使我们使用对象,对象属性 / 以及属性值之间的约束关系。