陌小路的个人博客 陌小路的个人博客
首页
  • 技术专区

    • 面试
    • Vue
    • Electron
    • TypeScript
    • Serverless
    • GraphQL
  • 我的秋招之旅
  • 2019年终总结
Todo
收藏夹
关于作者
GitHub

陌小路

前端切图仔
首页
  • 技术专区

    • 面试
    • Vue
    • Electron
    • TypeScript
    • Serverless
    • GraphQL
  • 我的秋招之旅
  • 2019年终总结
Todo
收藏夹
关于作者
GitHub
  • Vue

  • React

  • 面试

  • Electron

  • Serverless

  • GraphQL

  • TypeScript

    • type 与 interface 的异同
    • typescript 内置类型实现
      • Pick
      • Exclude
      • Extract
      • Omit
      • Partial
      • Required
      • Readonly
      • Record
      • NonNullable
      • Parameters
      • ConstructorParameters
      • ReturnType
      • InstanceType
  • RxJS

  • 工程化

  • Webpack

  • Nestjs

  • WebRTC & P2P

  • Docker

  • Linux

  • Git

  • Svelte

  • 踩坑日记 & 小Tips

  • 其他

  • technology
  • TypeScript
陌小路
2020-11-09

ts内置类型实现

# Pick

顾名思义,该内置类型主要用于挑选需要的属性,剔除不必要的元素。

type myPick<T, U extends keyof T> = {
    [P in U]: T[P];
}

interface obj {
    a: number;
    b: string;
}

const pickTest: myPick<obj, "a"> = {
    a: 13123
};
1
2
3
4
5
6
7
8
9
10
11
12

这里的myPick<obj, "a">相当于type pickTest = { a: number }

从代码量上来看,实现一个Pick内置类型还是比较轻松的,我们只要利用in的特性进行条件限制,做到除需要被保留的元素外,都进行过滤掉即可。同时,通过extents keyof来约束传入的类型必须是被挑选类型的子项,这样一个完整的Pick类型就实现完成了。

# Exclude

用于剔除指定类型,局限在于它只能用于联合类型的剔除。

type myExclude<T, U extends keyof any> = T extends U ? never : T;

// 相当于:type excludeTest = string | boolean;
const excludeTest: myExclude<number | string | boolean, number>;
1
2
3
4

never类型表示永远不会出现的值的类型。

ts中的never关键字在这里用就十分恰当了,一般情况应该用到它的场景应该很少。

整体而言也是属于比较简单的一种内置类型了,主要涉及到extends与?:表达式的配合,可以理解为T中的类型如果是U的子集,那么就返回never表示删除。

# Extract

与Exclude,正好相反,这里是取两个类型的交集。


type Extract<T, U> = T extends U ? T : never;

1
2
3

这里借助了我们熟悉的三元运算符?:,这样就能判断哪些是属于T和U共同拥有的类型了。

使用场景:


type E = Extract<number | string | 'b', string | 'b' | 'd'> // 等价于 type E = string | 'b'

1
2
3

# Omit

这个内置类型就是用来互补Exclude的,该泛型的主要用处就在于移除目标接口的指定属性。


type myPick<T, U extends keyof T> = {
    [P in U]: T[P];
}

type myExclude<T, U extends keyof any> = T extends U ? never : T;

type myOmit<T, U extends keyof T> = myPick<T, myExclude<keyof T, U>>

interface obj {
    a: number;
    b: string;
}

// 等价于:type omitTest = { b: string };
const omitTest: myOmit<obj, 'a'> = {
    b: '3123123'
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

在这里我们可以清晰的看到Omit结合了我们上述介绍的两个泛型,从而达到移除指定项的能力。

整体的含义还是比较好理解的,首先使用myExclude泛型返回除需要被删除属性外的剩余属性的联合类型,然后借用myPick选择出目标对象中需要保留的属性,最终返回一个新的删除操作完成的接口。

# Partial

这个泛型就比较简单了,主要用于将传入的接口属性变为可选。

type myPartial<T> = {
    [P in keyof T]?: T[P]
}

interface obj {
    a: number;
    b: string;
}

// 等价于:type partialTest = { a?: number; b?: string}
const partialTest: myPartial<obj> = {}
1
2
3
4
5
6
7
8
9
10
11

使用了该泛型处理后的接口,则接口所有属性将变为可选的。

# Required

Required泛型就刚好与Partial相反,它是将传入的接口属性变为必传。

type myRequired<T> = {
    [P in keyof T]-?: T[P]
}

interface obj {
    a: number;
    b: string;
}

// 等价于:type requiredTest = { a?: number; b?: string}
const requiredTest: myRequired<obj> = {
    a: 13,
    b: '323'
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Readonly

Readonly也算是一种我们熟知的类型之一了,将指定类型变为只读。

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

1
2
3
4

从类型定义来上看还是比较好理解的,给传入类型的所有属性值设定readonly关键字,实现效果。

# Record

该类型主要用于将目标对象中的属性类型进行修改,并返回一个新的类型,具体实现如下。

type Record<K extends of keyof any, T> = {
    [P in K]: T
}
1
2
3

如果上面其他类型都能看懂的话,这里的代码其实已经不需要太多赘述了,就类似于修改传入的类型对象上的属性类型。

主要应用场景如下:

interface Foo {
    name: string;
    age: number;
}

const fooOther: Record<Foo, any> = {
    name: 999,
    age: '243'
};
1
2
3
4
5
6
7
8
9

经过这番操作后,其实fooOther的类型已经变成了如下情况:

interface FooOther {
    name: any;
    age: any;
}
1
2
3
4

# NonNullable

用于排除目标类型中null或undefined属性。

type NonNullable<T> = T extends null | undefined ? never : T;
1

示例:


const typeTest: NonNullable<string | undefined | null>;

1
2
3

这里typeTest的类型就等价于:typeTest: string;

# Parameters

获取函数的参数类型。

这里将会涉及到一个比较有意思的玩意:infer,表示在 extends 条件语句中待推断的类型变量。


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

1
2
3

在T extends (...args: infer P) => any中,infer P表示待推断的函数参数类型,结合三元运算符,整体含义为,如果T能赋值给(...args: infer P) => any,则返回结果为(...args: infer P) => any中的P,否则返回never

示例:


type P = Parameters<(name: string, age: number) => any>; // [string, number]

1
2
3

# ConstructorParameters

获取构造函数的参数类型。这个与上面的原理差不多,都是借助infer就能达到效果


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

1
2
3

示例


class TestClass {
  constructor(public name: string, public age: number) {}
}

type Params = ConstructorParameters<typeof TestClass>; // [string, number]

1
2
3
4
5
6
7

# ReturnType

获取函数返回值的类型。


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

1
2
3

基本和上述两种没啥差别,这里应该比较好理解了,就是位置不一致,其他都差不多的。

# InstanceType

获取类的实例类型。

type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
1

同理,也很好理解,只是从函数返回值类型换成了类的实例类型。

未完待续哦...

编辑
上次更新: 2023/11/25, 4:11:00
type 与 interface 的异同
介绍

← type 与 interface 的异同 介绍→

最近更新
01
github加速
01-01
02
在线小工具
01-01
03
Lora-Embeddings
11-27
更多文章>
Theme by Vdoing | Copyright © 2020-2024 STDSuperman | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式