分享几个TypeScript泛型的使用场景

55次阅读

共计 1878 个字符,预计需要花费 5 分钟才能阅读完成。

最近使用 TypeScript 写了一个项目, 过程中不断感受到 TypeScript 的魅力, 现在来分享几个业务中关于泛型的场景

1. 深度 Partial

TS 内置了一个 Partial 类型, 用于把一个类型的成员属性设置为成可选模式, 例如

type Person = {
  name: string;
  age: number;
}

Person类型中有两个成员属性, 如果我们要把这个类型赋予变量 tom, 那tom 必须拥有 nameage两个属性

let tom:Person = {
    name: 'tom',
    age: 20
};

现在我们要让 Person 的两个属性都变为可选, 可以使用 Partial 类型进行转换

type PartialPerson = Partial<Person>;

let partialPerson: PartialPerson = {name: 'tom'};

这时 PartialPersonnameage 属性都已经变为可选的了, 但是如果我们在 Person 中再加一点东西

type Person = {
  name: string;
  age: number;
  contact: {
    email: string;
    phone: number;
    wechat: string;
  }
}

现在我们加入了一个 contact 属性值是一个对象, 如果我们想把 contact 里面的属性也变为可选 Partial 就不灵了

可以看到 Partial 是把 contact 变成了可选而不是里面的属性, 插个题外话通常的做法是为 contact 另外创建一个类型, 如果是这样的话 Partial 就可以用了, 但是我们不使用这种方法, 我们先来看看 TS 是怎么定义 Partial

/**
 * Make all properties in T optional
 */
type Partial<T> = {[P in keyof T]?: T[P];
};

很容易理解关键点是在 ?: 上, 现在我们改造一下

type DeepPartial<T> = {[P in keyof T]?: T[P] extends Object ? DeepPartial<T[P]> : T[P];
}

可以看到改造的 DeepPartialPartial差别在把直接赋值 T[P] 换成了 T[P] extends Object ? DeepPartial<T[P]> : T[P], 即判断 T 的属性 P 的类型是否是Object 然后进行再次 DeepPartial 或者返回 T[P] 的类型


这时编译器就不会再提示错误了

2. 更智能的 array_column 函数

在 PHP 中有一个 array_column 函数, 用于在数组中提取一列的内容, 用 JavaScript 表现就是

function array_column(arr, key) {return arr.map(item => item[key])
}

假如现在有一个 persons 数组

type Person = {
  name: string;
  age: number;
}

let persons: Person[] = [];

我们需要提取数组中的 name 属性, 可以很方便的使用 Array.map 方法提取, 但是如果又有别的数组需要提取, 我们可以实现一个更优雅的 array_column 函数

function array_column<T, K extends keyof T>(input: T[], key: K) {return input.map(item => item[key])
}

现在我们使用这个函数提取 persons 数组


可以看到当我们输入 persons 时, 编辑器已经推断出了 key 的类型, 再来一个 animals 数组

3. Proxy

在刚开始学习 TS 的时候看官方文档有一个 Proxy 的例子, 只给出了类型声明但却没有给出实现, 当时花了不少时间琢磨, 现在来实现它

class Proxy<T>{constructor(private data: T) { }

  get<K extends keyof T>(key: K) {return this.data[key]
  }

  set<K extends keyof T, V extends T[K]>(key: K, value: V) {this.data[key] = value;
  }
}

可以看到 Proxy 类提供了 getter 方法 getsetter方法 set, 现在我们基于上面的Person 类型创建一个 Proxy 实例

let person: Person = {
  name: 'tom',
  age: 18
};

let proxy = new Proxy(person);

现在我们来看看调用 get 方法


可以看到编辑器也推断出了参数 key 的类型, 再来看看 set 方法


get 方法一样, 编辑器也推断出了参数 key 的类型, 同时也推断出了参数 value 的类型为string

正文完
 0