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

是不是特别简单。

相关推荐
Bl_a_ck35 分钟前
【React】Craco 简介
开发语言·前端·react.js·typescript·前端框架
Bl_a_ck19 小时前
开发环境(Development Environment)
开发语言·前端·javascript·typescript·ecmascript
菜鸟una1 天前
【layout组件 与 路由镶嵌】vue3 后台管理系统
前端·vue.js·elementui·typescript
浪裡遊2 天前
Typescript中的对象类型
开发语言·前端·javascript·vue.js·typescript·ecmascript
从味书2 天前
安装typescript时,npm install -g typescript报错
javascript·typescript·npm
風吹过4 天前
A* (AStar) 寻路
typescript·cocos2d
geovindu4 天前
vue3: pdf.js 2.16.105 using typescript
javascript·vue.js·typescript·pdf
geovindu4 天前
vue3: pdf.js5.2.133 using typescript
javascript·vue.js·typescript·pdf
-Camellia007-5 天前
TypeScript学习案例(1)——贪吃蛇
javascript·学习·typescript
Jenna的海糖5 天前
ts axios中报 Property ‘code‘ does not exist on type ‘AxiosResponse<any, any>‘
前端·vue.js·react.js·typescript