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

是不是特别简单。

相关推荐
shenyaofeng2 天前
React 封装高阶组件 做路由权限控制
前端·javascript·react.js·职场和发展·typescript
草明3 天前
TypeScript 学习
javascript·学习·typescript
Dragon Wu5 天前
electron typescript运行并设置eslint检测
前端·javascript·typescript·electron·前端框架
草明5 天前
TypeScript 学习 -类型 - 9
javascript·学习·typescript
草明5 天前
TypeScript 学习 -类型 - 8
javascript·学习·typescript
觉醒法师7 天前
JS通过ASCII码值实现随机字符串的生成(可指定长度以及解决首位不出现数值)
开发语言·前端·javascript·typescript
徐_三岁8 天前
TypeScript 中的 object 和Object的区别
前端·javascript·typescript
ThomasChan1238 天前
Typesrcipt泛型约束详细解读
前端·javascript·vue.js·react.js·typescript·vue·jquery
我能与泰森过两招9 天前
鸿蒙next 自定义日历组件
typescript·harmonyos·鸿蒙
yg_小小程序员9 天前
鸿蒙开发(32)arkTS、通过关系型数据库实现数据持久化封装
数据库·华为·typescript·harmonyos