手撕ts面试题——不能不掌握的ts高级特性(三)

引言

上一篇我重点讲述了 ts 的 keyof、in 以及 infer。本期将结合一道笔试题重点讲述 ts 的一些其余内置操做符 。git

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

  • Partial
  • Required
  • Readonly
  • Pick<T,K extends keyof T>
  • Record<K extends keyof any, T>
  • Exclude<T,U>
  • Extract<T,U>
  • Omit<T, K extends keyof any>

首先仍是先讲述一下ts中的这些高级操做符,若是都已经掌握了,能够直接跳到末尾的手撕笔试题。手撕笔试题typescript

Partial

Partial 将属性变为可选属性。举个栗子,iUser 这个接口 name 和 age 是必须的,可是同时又有另外一个接口 iOptionUser,接口属性彻底同样,只是里面的 name 和 age 是可选的。比较笨的方法固然是手动再写一个。bash

interface iUser {
  name: string;
  age: number;
}
interface iOptionUser {
  name?: string;
  age?: number;
}
复制代码

其实,咱们能够看到的是,iOptionUser 只是在属性后添加一个?接口。咱们能够简单实现以下(该方法已内置)微信

type Partial<T> = {
  [P in keyof T]?: T[P];
};
复制代码

Required

Required和Partial方法正好相反,是将属性变成必须。方法一样很是简单,能够这样实现(该方法已内置)markdown

type Required<T> = {
    [P in keyof T]-?: T[P];
};
复制代码

效果以下:async

Readonly

Readonly是将属性变成只读。方法一样很是简单,能够这样实现(该方法已内置)函数

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};
复制代码

效果以下:oop

Pick<T,K extends keyof T>

Pick顾名思义,就是把一些属性挑选出来。效果以下:post

你们能够思考一下怎么实现,官方源码以下:

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};
复制代码

Record<K extends keyof any, T>

Record用于建立一个具备同类型属性值的对象。

type Record<K extends keyof any, T> = {
    [P in K]: T;
};
复制代码

Exclude<T,U>

从类型 T 中剔除全部能够赋值给 U 的属性,而后构造一个类型。主要用于联合类型。

官方源码以下:

type Exclude<T, U> = T extends U ? never : T;
复制代码

Extract<T,U>

功能与 Exclude相反

type Extract<T, U> = T extends U ? T : never;
复制代码

Omit<T, K extends keyof any>

主要用于剔除interface中的部分属性。 好比接口iUser包含name、age、firstName、lastName、location属性,而接口iUser2不包含location属性,咱们可使用前面提到的Pick实现,但这样会比较复杂,因此有了Omit 操做符。

interface iUser {
    name: string;
    age: number;
    firstName: string;
    lastName: string;
    location: string;
}
interface iUser2 {
    name: string;
    age: number;
    firstName: string;
    lastName: string;
}
复制代码

效果以下:

Omit源码以下:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
复制代码

手撕笔试题

这是一道 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的函数类型定义。

复制代码

首先咱们须要明白这个题义,这里是须要咱们把initInterface里的非函数属性去除,而且函数签名发生了变化。

  1. 第一步:获取函数属性
type RemoveNonFunctionProps<T> = {
    [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];

type FunctionProps = RemoveNonFunctionProps<initInterface>;
复制代码

2. 将只包含函数属性的类型Pick出来

type PickFunction<T> = Pick<T, RemoveNonFunctionProps<T>>;
type iFunctionInterface = PickFunction<initInterface>;
复制代码

3.接下来就是函数转换的过程,这里须要用到我 上篇博文提到的infer。

咱们对比一下,转换先后的函数签名,发现只是去除了参数和返回结果的Promsie。

type asyncMethod<T, U> = (input: Promise<T>) => Promise<Action<U>>;
type transformAsyncMethod<T,U> = (input: T) => Action<U>;
复制代码

咱们使用infer能够这样作

type TransformASyncMethod<T> = T extends (
  input: Promise<infer U>
) => Promise<Action<infer S>>
  ? (input: U) => Action<S>
  : never;
复制代码

同理咱们看一下方法二,转换先后:

type syncMethod<T, U> = (action: Action<T>) => Action<U>;
type transformSyncMethod<T, U> = (action: T) => Action<U>;
复制代码

咱们依旧使用infer

type TransformSyncMethod<T> = T extends (
  action: Action<infer U>
) => Action<infer S>
  ? (action: U) => Action<S>
  : never;
复制代码

因此转换函数能够这样写:

type TransformMethod<T> = T extends (
  input: Promise<infer U>
) => Promise<Action<infer S>>
  ? (input: U) => Action<S>
  : T extends (action: Action<infer U>) => Action<infer S>
  ? (action: U) => Action<S>
  : never;
复制代码

4.整合 前三步,咱们已经有了完整的思路,如今就是把Connect类型定义整合起来。

type RemoveNonFunctionProps<T> = {
  [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
type PickFunction<T> = Pick<T, RemoveNonFunctionProps<T>>;
type TransformMethod<T> = T extends (
  input: Promise<infer U>
) => Promise<Action<infer S>>
  ? (input: U) => Action<S>
  : T extends (action: Action<infer U>) => Action<infer S>
  ? (action: U) => Action<S>
  : never;
type ConnectAll<T> = {
  [K in keyof T]: TransformMethod<T[K]>;
};
type Connect<T> = ConnectAll<PickFunction<T>>;

复制代码

本次就到这里了,下面是我我的的微信公众号。

@Author: WaterMan

相关文章
相关标签/搜索