Typescript工具类型 - 整理汇总

Typescript工具类型已经内置在es5类型声明中,无需定义即可使用。

不需要一次记住所有工具,只需留下印象,将来需要时再来查阅即可。文章目录已经组织成易于查阅的形式。

函数工具

ConstructorParameters

/**
 * Obtain the parameters of a constructor function type in a tuple
 */
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;

输入构造函数的类型,输出构造函数的参数的类型。
例子:

class Animal {
  constructor(name: string, age: number) {
    return {
      name,
      age
    };
  }
}
type Result = ConstructorParameters<typeof Animal>;
// type Result = [string, number]

InstanceType

/**
 * Obtain the return type of a constructor function type
 */
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;

输入构造函数的类型,输出实例的类型。
例子:

class Animal {
  constructor(name: string, age: number) {
    return {
      name,
      age
    };
  }
}
type Result = InstanceType<typeof Animal>;
// type Result = Animal

Parameters

/**
 * Obtain the parameters of a function type in a tuple
 */
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

输入函数类型,输出函数的参数的类型。

ReturnType

/**
 * Obtain the return type of a function type
 */
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

输入函数类型,输出函数的返回值的类型。

ThisParameterType

/**
 * Extracts the type of the 'this' parameter of a function type, or 'unknown' if the function type has no 'this' parameter.
 */
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;

函数类型信息除了包含(约束)参数类型和返回值类型以外,还可以包含(约束)this的类型。this对象其实也可以看作一种调用参数。
ThisParameterType就用于提取函数类型中的this类型信息。

Note: This type only works correctly if--strictFunctionTypesis enabled. 下面与this类型相关的工具类型同理。

OmitThisParameter

/**
 * Removes the 'this' parameter from a function type.
 */
type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;

输入一个函数类型,移除其中的this类型信息,返回新的函数类型。

ThisType

This utility does not return a transformed type. Instead, it serves as a marker for a contextual this type.

在编写一个对象字面量类型的时候,可以标注这个对象中的成员函数的this类型(contextual 'this' type)。contextual 'this' type 是保存在“对象类型”中的一种类型信息。

image.png

// 测试代码
interface Person {
    name: string
    age: number
}

type ObjType = { [k: string]: any } & ThisType<Person>

const obj: ObjType = {
    method(arg1: boolean) {
        // this的类型被约束为Person
        console.log(this.age)
    }
}

ThisType的实现只是一个空接口,会被tsc特殊对待。

Note that the--noImplicitThisflag must be enabled to use this utility

集合论工具

Exclude

/**
 * Exclude from T those types that are assignable to U
 */
type Exclude<T, U> = T extends U ? never : T;

从T类型中剔除U类型。
注意,只能用于从并类型(Type1|Type2)中剔除其中的类型。不支持更加精确的剔除(比如从string里剔除'1')。
例子:

type Result = Exclude<"1" | "2", "2" | "3">;
// type Result = "1"

type Result2 = Exclude<string, '1'>;
// type Result2 = string

type Result3 = Exclude<{a: string | number}, {a: number}>;
/*
type Result3 = {
    a: string | number;
}
*/

Extract

/**
 * Extract from T those types that are assignable to U
 */
type Extract<T, U> = T extends U ? T : never;

从T类型中选择U类型。
注意,只能用于从并类型(Type1|Type2)中选择其中的类型。不支持更加精确的选择(比如从string里选择'1')。
例子:

type Result1 = Extract<"1" | "2", "2" | "3">;
// type Result1 = "2"

type Result2 = Extract<string, "1">;
// type Result2 = never

type Result3 = Extract<{ a: number }, { a: string | number }>;
/*
type Result3 = {
    a: number;
}
*/
type Result4 = Extract<string | number, number | boolean>;
// type Result4 = number

NonNullable

/**
 * Exclude null and undefined from T
 */
type NonNullable<T> = T extends null | undefined ? never : T;

从T类型中剔除null和undefined。

对象工具

Partial

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

将类型T中的属性全部变成可选属性。

Required

/**
 * Make all properties in T required
 */
type Required<T> = {
    [P in keyof T]-?: T[P];
};

将类型T中的属性全部变成必选属性。

Readonly

/**
 * Make all properties in T readonly
 */
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

将类型T中的属性全部变成只读属性。
注意,它仅仅让对象本身变成immutable(不可对属性赋值),但是不会改变属性值的可变性(Immutability)。

类似于浅拷贝与深拷贝的区别。

ReadonlyArray

类型定义比较长。总而言之:

  1. 不能调用pushsplice等修改数组的操作。
  2. 因为包含属性定义:readonly [n: number]: T;,所以不能对数组元素进行赋值修改。

例子:

let arr: ReadonlyArray<string> = ["a", "b"];
arr.push("c"); // error
arr[0] = "c"; // error

注意,它仅仅让数组本身变成immutable(不可对数组项赋值),但是不会改变数组Item的可变性(Immutability)。

类似于浅拷贝与深拷贝的区别。

Pick

/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

从类型T中,选出key为K的属性。
例子:

type Result1 = Pick<{ a: number; b: string; c: boolean }, "b" | "c">;
/*
type Result1 = {
    b: string;
    c: boolean;
}
*/

Omit

/**
 * Construct a type with the properties of T except for those in type K.
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

从类型T中,过滤key为K的属性。
例子:

type Result1 = Omit<{ a: number; b: string; c: boolean }, "b" | "c">;
/*
type Result1 = {
    a: number;
}
*/

Record

/**
 * Construct a type with a set of properties K of type T
 */
type Record<K extends keyof any, T> = {
    [P in K]: T;
};

返回一个对象类型,以K为key,以T为value。
例子:

type Result1 = Record<"a" | "b" | "c", string>;
/*
type Result1 = {
    a: string;
    b: string;
    c: string;
}
*/

type Result2 = Record<"a" | "b" | "c" | number, boolean>;
/*
type Result2 = {
    [x: number]: boolean;
    a: boolean;
    b: boolean;
    c: boolean;
}
*/

GetPropertyType

如果你在做类型推导的时候,想从Obj类型中读取key属性的类型,但是不确定key属性是否在Obj类型中,就可以使用这个方法。

type GetPropertyType<
  Obj,
  Key extends string,
  Fallback = undefined
> = Obj extends Record<Key, infer Value> ? Value : Fallback

这是笔者自己定义的工具类型,ts标准库里面没有提供。需要的时候,需要拷贝以上定义到你的代码中。

使用示例:

type ObjTest = { testKey: 'test value' }
// Result1 = "test value"
type Result1 = GetPropertyType<ObjTest, 'testKey'>
// Result2 = undefined
type Result2 = GetPropertyType<ObjTest, 'unknownKey'>
// Result3 = "customFallback"
type Result3 = GetPropertyType<ObjTest, 'unknownKey', 'customFallback'>

// 函数类型推导时,不确定optionalKey是否存在Param中
function testFun<Param extends { optionalKey?: string }>(
  param: Param
): GetPropertyType<Param, 'optionalKey'> {
  return param.optionalKey as any
}
// res1: "val"
const res1 = testFun({ optionalKey: 'val' } as const)
// res2: undefined
const res2 = testFun({})

参考资料


csRyan的学习专栏
分享对于计算机科学的学习和思考,只发布有价值的文章: 对于那些网上已经有完整资料,且相关资料已经整...

So you're passionate? How passionate? What actions does your passion lead you to do? If the heart...

1.1k 声望
181 粉丝
0 条评论
推荐阅读
手写一个Parser - 代码简单而功能强大的Pratt Parsing
在编译的流程中,一个很重要的步骤是语法分析(又称解析,Parsing)。解析器(Parser)负责将Token流转化为抽象语法树(AST)。这篇文章介绍一种Parser的实现算法:Pratt Parsing,又称Top Down Operator Precede...

csRyan阅读 2.9k

过滤/筛选树节点
又是树,是我跟树杠上了吗?—— 不,是树的问题太多了!🔗 相关文章推荐:使用递归遍历并转换树形数据(以 TypeScript 为例)从列表生成树 (JavaScript/TypeScript) 过滤和筛选是一个意思,都是 filter。对于列表来...

边城18阅读 7.6k评论 3

封面图
掌握 TypeScript:20 个提高代码质量的最佳实践
本文首发于微信公众号:大迁世界, 我的微信:qq449245884,我会第一时间和你分享前端行业趋势,学习途径等等。更多开源作品请看 GitHub [链接] ,包含一线大厂面试完整考点、资料以及我的系列文章。

前端小智9阅读 1.6k

不要用100vh做移动响应
本文首发于微信公众号:大迁世界, 我的微信:qq449245884,我会第一时间和你分享前端行业趋势,学习途径等等。更多开源作品请看 GitHub [链接] ,包含一线大厂面试完整考点、资料以及我的系列文章。

前端小智6阅读 418

为什么JSON.parse会损坏大数字,如何解决这个问题?
微信搜索 【大迁世界】, 我会第一时间和你分享前端行业趋势,学习途径等等。本文 GitHub [链接] 已收录,有一线大厂面试完整考点、资料以及我的系列文章。

前端小智8阅读 878

5个async/await最佳实践
微信搜索 【大迁世界】, 我会第一时间和你分享前端行业趋势,学习途径等等。本文 GitHub [链接] 已收录,有一线大厂面试完整考点、资料以及我的系列文章。

前端小智5阅读 781

掌握TypeScript:10个最佳实践提高代码质量
TypeScript 是一种强类型的 JavaScript 超集,提供了很多优秀的工具和语言特性,可以帮助开发者提高代码质量和开发效率。在本文中,我们将介绍 10 个 TypeScript 最佳实践,帮助初级和中级的 Web 前端开发工程师...

pingan87874阅读 1.4k

封面图

So you're passionate? How passionate? What actions does your passion lead you to do? If the heart...

1.1k 声望
181 粉丝
宣传栏