通过一个例子串起TypeScript中常用的keyof、extends、in

在阅读本文之前,请先阅读TypeScript解决了JavaScript的什么问题?,需要在你脑海中建立这样一个概念:TypeScript是一门语言,既然是一门语言,它就可以编程,只是它的编程对象是类型,不是值,即类型编程实际也是编程

下面来实现一个简单的函数:key 是 obj 的属性名。

js 复制代码
function pickValue(obj, key) {
  return obj[key];
}

如果要为其进行类型定义的话,有哪些需要定义的地方?

需要定义的有:参数 obj、参数 key、返回值。

同时,这三者有存在一些关联关系,比如:key 必然是 obj 中的键值名之一,一定为string类型;返回的值一定是obj中的键值

js 复制代码
function pickValue<T>(obj: T, key: keyof T) {
  return obj[key]
}

keyof

keyof索引类型查询的语法,它返回由 obj 的属性名组成的字面量联合类型。

js 复制代码
interface foo {
  a: number;
  b: string;
}

type A = keyof foo; // "a" | "b"

上面标注了参数,但是没有标注返回值,那返回值怎么标注呢?

我们拿到了key,就能拿到对应的value,那么value的类型也就不在话下了:

js 复制代码
function pickValue<T>(obj: T, key: keyof T): T[keyof T] {
  return obj[key]
}

伪代码解释下:

js 复制代码
interface T {
  a: number;
  b: string;
}

type TKeys = keyof T; // "a" | "b"

type PropAType = T['a']; // number

如果你能深刻认识到 TypeScript 是一门语言且能编程的话,上面就好理解了,它无非操作的就是类型而已。T[keyof T]就能很好的说明这一特性

但这种写法很明显有可以改进的地方:keyof出现了两次,以及泛型T应该被限制为对象类型,就像我们平时会做的那样:用一个变量把多处出现的存起来,在类型编程里,泛型就是变量

js 复制代码
function pickValue<T extends object, U extends keyof T>(obj: T, key: U): T[U] {
  return obj[key]
}

extends

extends是撒东西呢?

你可以把T extends object理解为T被限制为对象类型U extends keyof T理解为泛型U必然是泛型T的属性名组成的联合类型(以字面量类型的形式)

假设现在我们不只要取出一个值了,我们要取出一系列值:

js 复制代码
function pickValue<T extends object, U extends keyof T>(
  obj: T,
  keys: U[]
): T[U][] {
  return keys.map(key => obj[key])
}

这里有两个变化:

  • keys: U[]:U是T的属性名组成的联合类型,那么要表示一个内部元素均是T的属性名的数组,就可以使用这种方式。
  • T[U][] 它的原理实际上和上面一条相同,之所以单独拿出来是因为我认为它是一个很好地例子:简单的表现了TS类型编程的组合性,你不感觉这种写法就像搭积木一样吗?

映射类型 Mapped Types

映射类型同样是类型编程的重要底层组成,通常用于在旧有类型的基础上进行改造,包括接口包含字段、字段的类型、修饰符(readonly与?)等等。

从一个简单场景入手:

js 复制代码
interface IUser {
  name: string
  title: string
  age: number
  isMan: boolean
}

现在我们有个需求,实现一个接口,它的字段与接口A完全相同,但是其中的类型全部为string,你会怎么做?直接重新声明一个然后手写吗?

这真是太笨了。

如果把接口换成对象再想想,其实很简单,new一个新对象,然后遍历A的键名(Object.keys())来填充这个对象。

js 复制代码
type IUserStringObj<T> = {
  [K in keyof T]: string
}

是不是很熟悉这个in操作符,你完全可以把它理解为就是for...in,也就是说你可以通过in遍历到联合类型里面的每个类型,比如利用它还可以复制一个接口:

js 复制代码
type Clone<T> = {
  [K in keyof T]: T[K]
}

掌握这种思路,其实你已经接触到一些工具类型的底层实现了。比如Partial工具类型:

js 复制代码
type Partial<T> = {
    [K in keyof T]?: T[K] 
}

是不是特别简单。

相关推荐
Chikaoya3 小时前
项目中用户数据获取遇到bug
前端·typescript·vue·bug
我认不到你5 小时前
antd proFromSelect 懒加载+模糊查询
前端·javascript·react.js·typescript
奔跑草-1 天前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
新星_1 天前
构造函数类型
typescript
清灵xmf1 天前
TypeScript 中的 ! 和 ? 操作符
前端·javascript·typescript·?·
葫芦鱼1 天前
怎么打造一个舒适的nodejs开发环境
前端·typescript
前端要努力2 天前
深入解析 TypeScript 的 unknown 和 any:安全性与灵活性的平衡
前端·typescript
郝晨妤3 天前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
红尘炼心4 天前
一个困扰我许久的TypeScript定义问题
前端·react.js·typescript
baiduguoyun4 天前
TypeScript 中的三斜杠指令语法
typescript