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

是不是特别简单。

相关推荐
那年窗外下的雪.1 天前
鸿蒙ArkUI布局与样式进阶(十五)—— 模块化 · 自定义组件 · 泛型机制深度解析
javascript·华为·typescript·harmonyos·鸿蒙·arkui
guangzan1 天前
React 状态管理的“碎片化”
typescript·zustand
慢知行1 天前
从 0 到 1 搭建 Vite+Vue3+TS 工程模板:能上手操作的指南
前端·vue.js·typescript
濮水大叔1 天前
VonaJS业务抽象层: 验证码体系
typescript·nodejs·nestjs
锦瑟弦音2 天前
2048游戏开发笔记4 & 音效 cocos3.8.7
笔记·typescript·cocos2d
小码编匠3 天前
Three.js 遇上 Vue3 开发现代化 3D 可视化编辑系统
vue.js·typescript·three.js
木木子99993 天前
Next.js, Node.js, JavaScript, TypeScript 的关系
javascript·typescript·node.js
苏卫苏卫苏卫4 天前
【码源】智能无人仓库管理系统(详细码源下~基于React+TypeScript+Vite):
前端·react.js·typescript·vite·项目设计·智能无人仓库管理系统·码源
yume_sibai4 天前
TS 常用内置方法
前端·javascript·typescript