通过一个例子串起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] 
}

是不是特别简单。

相关推荐
Tiffany_Ho2 小时前
【TypeScript】知识点梳理(三)
前端·typescript
看到请催我学习6 小时前
如何实现两个标签页之间的通信
javascript·css·typescript·node.js·html5
天涯学馆13 小时前
Deno与Secure TypeScript:安全的后端开发
前端·typescript·deno
applebomb16 小时前
【2024】uniapp 接入声网音频RTC【H5+Android】Unibest模板下Vue3+Typescript
typescript·uniapp·rtc·声网·unibest·agora
读心悦1 天前
TS 中类型的继承
typescript
读心悦1 天前
在 TS 的 class 中,如何防止外部实例化
typescript
Small-K2 天前
前端框架中@路径别名原理和配置
前端·webpack·typescript·前端框架·vite
宏辉2 天前
【TypeScript】异步编程
前端·javascript·typescript
LJ小番茄2 天前
TS(type,属性修饰符,抽象类,interface)一次性全部总结
前端·javascript·vue.js·typescript
It'sMyGo3 天前
Javascript数组研究03_手写实现_fill_filter_find_findIndex_findLast_findLastIndex
前端·javascript·typescript