typescript不能不掌握的高级特性(二)

引言

上一篇我重点讲述了 ts 的交叉类型,本期将结合实例重点讲述 ts 中的一些高级操做符。本篇文章略长,笔者以前的文章都略短,做为男人仍是要好好学习,文章仍是长点好。javascript

本期涉及的操做符以下:html

  • keyof
  • in
  • infer 关键字
  • Parameters
  • ReturnType
  • InstanceType
  • ConstructorParameters
  • ThisParameterType
  • OmitThisParameter

本篇文章适合有必定基础的 ts 开发,若是你彻底没有用过,请先到官网学习官方文档java

经过上述操做符的学习,但愿能达到如下效果:git

  • 再也不为大佬写的 ts 定义而苦恼了
  • 看源码定义再也不吃力了
  • 本身的 ts 代码更加智能,再也不是满屏的 any 了。

下面我将结合具体实栗向你们讲述 ts 中的高级操做符。github

keyof

定义

keyofObject.keys略有类似,只是 keyof 是取 interface 的键,并且 keyof 取到键后会保存为联合类型。typescript

interface iUserInfo {
  name: string;
  age: number;
}
type keys = keyof iUserInfo;
复制代码

keyof 的简单栗子

咱们有这样一个需求,实现一个函数 getValue 取得对象的 value。在未接触 keyof 时,咱们通常会这样写:数组

function getValue(o: object, key: string) {
  return o[key];
}
const obj1 = { name: '张三', age: 18 };
const name = getValue(obj1, 'name');
复制代码

可是,这样写就丧失了 ts 的优点:bash

  • 没法肯定返回值类型
  • 没法对 key 进行约束,可能会犯拼写的错误

这时咱们可使用 keyof 来加强 getValue 函数的类型功能。async

使用 keyof 后咱们能够看到,能够完整的提示能够输入的值,当拼写错误时也会有清晰的提示。

function getValue<T extends Object, K extends keyof T>(o: T, key: K): T[K] {
  return o[key];
}

const obj1 = { name: '张三', age: 18 };
const a = getValue(obj1, 'hh');
复制代码

in

in用于取联合类型的值。主要用于数组和对象的构造。函数

type name = 'firstName' | 'lastName';
type TName = {
  [key in name]: string;
};
复制代码

const data1 = [
  {
    a1: 'a',
    b1: 'b',
    c1: 'c',
    d1: 'd',
  },
];

const data2 = [
  {
    a2: 'a',
    b2: 'b',
  },
];
复制代码

但切记不要用于 interface,不然会出错

infer

先看官方解释:

Within the extends clause of a conditional type, it is now possible to have infer declarations that introduce a type variable to be inferred. Such inferred type variables may be referenced in the true branch of the conditional type. It is possible to have multiple infer locations for the same type variable.

翻译过来就是:

如今在有条件类型的 extends 子语句中,容许出现 infer 声明,它会引入一个待推断的类型变量。 这个推断的类型变量能够在有条件类型的 true 分支中被引用。 容许出现多个同类型变量的 infer。

初步看来,这个 ts 关键字限制比较多,也是笔者以为比较难理解的,可是它对咱们获取一些比较复杂的类型特别有用。使用过程当中须要注意如下几个关键点

  • 只能出如今有条件类型的 extends 子语句中;
  • 出现 infer 声明,会引入一个待推断的类型变量;
  • 推断的类型变量能够在有条件类型的 true 分支中被引用;
  • 容许出现多个同类型变量的 infer

要完全理解这个关键词的使用必须结合一些实例。

infer 实例

使用 infer 获取函数参数 Parameters

好比咱们这里定义了一个函数类型 TArea,如今要实现将函数的参数类型取出来,咱们该怎么作呢?

type TArea = (width: number, height: number) => number;
type params = Parameters<TArea>;
复制代码

其实 Parameters 方法 ts 已内置,源码以下:

type Parameters<T extends (...args: any) => any> = T extends (
  ...args: infer P
) => any
  ? P
  : never;
复制代码

咱们仔细研读一下以上源码,发现遵循咱们上面所说的 infer 知足的四个特色:

  • 只能出如今有条件类型的 extends 子语句中;
  • 出现 infer 声明,会引入一个待推断的类型变量;
  • 推断的类型变量能够在有条件类型的 true 分支中被引用;
  • 容许出现多个同类型变量的 infer

这里再啰嗦几句,由于咱们要获取函数参数,因此传递的参数必须是个函数,因此有 T extends (...args: any) => any,因为咱们要获取的是函数参数的类型,因此 infer 出如今了函数参数位置。

同理获取函数返回值的方法就呼之欲出了,若是仍是写不出来,当我没说。

使用 infer 获取函数返回值 ReturnType

ReturnType 方法 ts 已内置

type ReturnType<T extends (...args: any) => any> = T extends (
  ...args: any
) => infer R
  ? R
  : any;
复制代码

再看一下图,不要说我骗你!

了不起了,infer 真是太强大了,下面咱们继续看 infer 如何获取一个类实例的类型。

获取实例类型 InstanceType

type InstanceType<T extends new (...args: any) => any> = T extends new (
  ...args: any
) => infer R
  ? R
  : any;
复制代码

偷偷告诉你,聪明的 ts 官方也内置了这个工具。

获取构造函数类型 ConstructorParameters

该方法 ts 已内置咱们看一下源码

type ConstructorParameters<
  T extends new (...args: any) => any
> = T extends new (...args: infer P) => any ? P : never;
复制代码

咱们能够这样使用它

获取参数 this 参数 ThisParameterType

type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any
  ? U
  : unknown;
复制代码

剔除 this 参数 OmitThisParameter

实现效果以下,你们能够本身手动实现一下,这能够很好的训练一下 infer 的使用。

官方源码以下:

type OmitThisParameter<T> = unknown extends ThisParameterType<T>
  ? T
  : T extends (...args: infer A) => infer R
  ? (...args: A) => R
  : T;
复制代码

咱们能够这样理解:若是传递的函数不包含 this 参数,则直接返回。如下语法用于判断是否包含 this 参数

unknown extends ThisParameterType<T>
复制代码

总结

咱们重点讲述了 ts 中 keyof 和 infer 的高级用法,下面以两个思考题结束本篇文章,具体答案会在下篇文章揭晓。

思考题 1

这是一道 leetcode 的 ts笔试题,原题目略长,就不直接贴出来了,这里简化一下:

// 假设有一个这样的类型:
interface initInterface {
  count: number;
  message: string;
  asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>;
  syncMethod<T, U>(action: Action<T>): Action<U>;
}
// 在通过 Connect 函数以后,返回值类型为

type Result {
  asyncMethod<T, U>(input: T): Action<U>;
  syncMethod<T, U>(action: T): Action<U>;
}
// 其中 Action<T> 的定义为:
interface Action<T> {
  payload?: T
  type: string
}
// 如今要求写出Connect的函数类型定义。
复制代码

思考题二

// 有原数组以下
const data1 = [
  {
    a1: 'a',
    b1: 'b',
    c1: 'c'
  }
];
// 实现一个函数 transformData ,传递一个keyMap后,结果返回通过keyMap转换后的数组

const A2 = transformData(data1, { a1: 'a2' }); // 返回 [{a2: 'a'}]
const A2 = transformData(data1, { a1: 'a2',b2: 'b1' }); // 返回 [{a2: 'a', b2: 'b']

// 要求用ts完成,必须有完善类型推断,不能出现any
复制代码

@Author: WaterMan

相关文章
相关标签/搜索