TypeScript(十一)泛型工具类型

简介: TypeScript(十一)泛型工具类型

前言

本文收录于TypeScript知识总结系列文章,欢迎指正!

前面的文章我们对泛型的用法有了大概的认识,也通过泛型实现了一些常用的类型,那么这篇文章将带各位深入了解一些常用泛型工具类型的使用及实现,那么话不多说,我们直接开始。

Partial<T>

Partial<T>的作用是将类型T中的所有属性变为可选属性

interface IAnimal {
    name: string
    age: number
    color: string
}
type MyPartial = Partial<IAnimal>

相当于转换成了

type MyPartial = {
    name?: string;
    age?: number;
    color?: string;
}

它的实现是使用映射类型对对象属性遍历

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

Required<T>

Required与Partial相反,作用是将所有属性变成必选属性,用到的原理就是 '-?' 符号

type MyRequired = Required<MyPartial>

上面的代码中的MyRequired变回了IAnimal,以下是Required的实现

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

Readonly<T>

只读属性我们在前面的文章也提及过,将所有的属性换成只读

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

Pick<T, K>

Pick的作用是在对象中选取一部分属性,比如我们选取name和age

type MyPick = Pick<IAnimal, "name" | "age">

他的实现过程是传入两个类型参数,第一个是对象类型,第二个是属性集合

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

Exclude<T, U>

Exclude<T, U>表示从T中排除U类型或类型集合,如下面代码排除了string类型

type MyExclude = Exclude<string | number | boolean, string>

此外,我们可以将Exclude融入对象类型,针对属性值进行排除,如

type ExcludeAnimal = {
    [key in Exclude<keyof IAnimal, string>]: IAnimal[key]
}

上述代码实现的是将IAnimal中字符类型属性名全部排除,也就是将属性全清除

Exclude的实现

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

Omit<T, K>

Omit<T, K>的含义是从T对象类型中删除K,比如我们可以删除IAnimal的age和color

type OmitAnimal = Omit<IAnimal, "age" | "color">

它的实现方式也是通过Pick+Exclude实现的,仅需记住两步:Exclude排除T中K的类型,返回值传给Pick;使用Pick生成新的对象类型

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

Record<K, T>

Record可以理解为批量创建一个对象中的属性,其中键值或其集合是K,类型是T。我们使用Record表示一下上面的IAnimal

type RecordAnimal = Record<"name" | "color", string> & Record<"age", number>

实现方式如下,其中P是K的子集代表着对象的属性

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

上面的代码实际上我们可以这么写,通过string | number | symbol集合保证key的安全

type IRecord<K extends string | number | symbol, T> = { [key in K]: T }

NonNullable<T>

NonNullable<T>一般用来从类型T中排除空类型,即判断是否存在null或undefined,如果不是则返回原类型

NonNullable<null | undefined | string | boolean | number> // 表示 string | number | boolean

类型实现

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

ReturnType<T>

ReturnType是用来获取函数的返回值类型,其中T表示函数类型

type foo<T> = (params: T) => T[]
type IReturnType = ReturnType<foo<string>>

当函数并未调用时,我们无法获得泛型的类型,此时使用ReturnType则会获得unknown类型

function foo<T>(params: T) {
    return [params]
}
type IReturnType = ReturnType<typeof foo> // unknown[]

ReturnType的实现就需要使用到我们前面文章说到的infer关键字了

我们复习一下返回函数的返回值类型是怎么实现的?

type IReturn<T> = T extends (...args: any[]) => infer R ? R : T;
type foo = () => string
type IReturnType = IReturn<foo>// string

然后我们给泛型加个约束,泛型只能传入函数类型

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

这样,一个获取函数返回值的类型就实现完成了

Parameters<T>

说完了获取函数返回值类型,咱们看看Parameters<T>获取函数参数类型,通过传入函数T来获取T的参数类型

type foo<T> = (params: T) => T
type IGetParameters = Parameters<foo<string>>// [params: string]

参数类型像arguments数组一样,思考以下代码,使用infer实现获取函数参数类型

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

与函数返回值一样,通过添加一个函数的约束来实现完整功能

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

ConstructorParameters<T>

ConstructorParameters表示获取构造函数T的参数类型,我们使用接口定义一个类的构造函数,并通过ConstructorParameters获取其参数的伪数组

interface IAnimal {
    new(name: string, age: number)
}
type getConstructorParameters = ConstructorParameters<IAnimal> // [name: string, age: number]

实现方式与Parameters类似,只不过把函数类型换成了类

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

tips:官方的对此类型的实现中有个巧妙的地方:使用抽象类实现了该类型。原因是什么?我们看看下面的代码

// 抽象类
abstract class Animal { }
 
// 类
class Dog { }
 
// 使用类类型实现获取构造函数参数类型
type ICParameters<T> = T extends new (...args: infer P) => any ? P : never;
 
// 使用抽象类类型实现获取构造函数参数类型
type IAbstractCParameters<T> = T extends abstract new (...args: infer P) => any ? P : never;
 
// 抽象类类型-抽象类
type getAbstractCParametersAnimal = IAbstractCParameters<typeof Animal>// []
 
// 抽象类类型-类
type getAbstractCParametersDog = IAbstractCParameters<typeof Dog>// []
 
// 类类型-抽象类
type getCParametersAnimal = ICParameters<typeof Animal>// never
 
// 类类型-类
type getCParametersDog = ICParameters<typeof Dog>// []

可以看到由于抽象类只能被类继承,而不能反过来继承类,还记得之前讲述的关于类的文章吗?抽象类只能被继承,不能被实例化,并且只能被类或者抽象类继承。所以理解为使用抽象类实现获取构造函数参数类型可以运用于抽象类,而使用类实现获取构造函数参数类型无法运用于抽象类。描述的比较乱,仅供参考,具体看看代码会清晰一点

最后在上述的基础上给ConstructorParameters类型增加一个抽象类的约束就可以通过以下代码实现完整类型了

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

InstanceType<T>

InstanceType用于获取类的实例化类型,即 new 类 的产物,可以说是执行构造函数的返回值,与上面的ConstructorParameters类型相对应

class Animal {
    constructor(name?: string, age?: number) { }
}
const animal: InstanceType<typeof Animal> = new Animal()

实现方式也类似,就不多做描述

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

ThisParameterType<T>

ThisParameterType获取函数参数的this的类型,常常用在call,apply,bind中。

function getThis(this: any) {
    console.log(this);
}
type getThisType = ThisParameterType<typeof getThis>;// string

但同时,只要函数内部的第一个参数不命名为this,这个类型就表示unknown

function getThis(str: string) {
    console.log(str);
}
type getThisType = ThisParameterType<typeof getThis>;// unknown

tips:编译后的函数第一个参数会消失

function getThis() {
    console.log(this);
}

实现方式是获取函数的第一个为this参数的类型

type ThisParameterType<T> = T extends (this: infer U, ...args: never) => any ? U : unknown;

OmitThisParameter<T>

OmitThisParameter的用途是若函数的第一个参数为this,则将其删除并返回函数类型

function getThis(this: string) {
    console.log(this);
}
type getThisType = OmitThisParameter<typeof getThis>;// () => void

其实现方式如下

type OmitThisParameter<T> = T extends (this: any, ...args: infer P) => infer R ? (...args: P) => R : T;

写在最后

以上就是文章全部内容了,本篇文章针对泛型常用的工具类型做了详细的解读及实现,有什么问题还望多多指教,感谢你看到了最后,如果文章对你有帮助,还望支持一下,谢谢!

相关文章
|
24天前
|
设计模式 JavaScript 安全
TypeScript性能优化及代码质量提升的重要性、方法与策略,包括合理使用类型注解、减少类型断言、优化模块导入导出、遵循编码规范、加强代码注释等
本文深入探讨了TypeScript性能优化及代码质量提升的重要性、方法与策略,包括合理使用类型注解、减少类型断言、优化模块导入导出、遵循编码规范、加强代码注释等,旨在帮助开发者在保证代码质量的同时,实现高效的性能优化,提升用户体验和项目稳定性。
41 6
|
23天前
|
开发框架 JavaScript 前端开发
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势。通过明确的类型定义,TypeScript 能够在编码阶段发现潜在错误,提高代码质量;支持组件的清晰定义与复用,增强代码的可维护性;与 React、Vue 等框架结合,提供更佳的开发体验;适用于大型项目,优化代码结构和性能。随着 Web 技术的发展,TypeScript 的应用前景广阔,将继续引领 Web 开发的新趋势。
35 2
|
1月前
|
JavaScript 安全 前端开发
TypeScript类型声明:基础与进阶
通过本文的介绍,我们详细探讨了TypeScript的基础与进阶类型声明。从基本数据类型到复杂的泛型和高级类型,TypeScript提供了丰富的工具来确保代码的类型安全和可维护性。掌握这些类型声明能够帮助开发者编写更加健壮和高效的代码,提高开发效率和代码质量。希望本文能为您在使用TypeScript时提供实用的参考和指导。
35 2
|
1月前
|
JavaScript 开发者
在 Babel 插件中使用 TypeScript 类型
【10月更文挑战第23天】可以在 Babel 插件中更有效地使用 TypeScript 类型,提高插件的开发效率和质量,减少潜在的类型错误。同时,也有助于提升代码的可理解性和可维护性,使插件的功能更易于扩展和升级。
|
1月前
|
JavaScript 前端开发 安全
TypeScript进阶:类型系统与高级类型的应用
【10月更文挑战第25天】TypeScript作为JavaScript的超集,其类型系统是其核心特性之一。本文通过代码示例介绍了TypeScript的基本数据类型、联合类型、交叉类型、泛型和条件类型等高级类型的应用。这些特性不仅提高了代码的可读性和可维护性,还帮助开发者构建更健壮的应用程序。
30 0
|
3月前
|
JavaScript
typeScript进阶(9)_type类型别名
本文介绍了TypeScript中类型别名的概念和用法。类型别名使用`type`关键字定义,可以为现有类型起一个新的名字,使代码更加清晰易懂。文章通过具体示例展示了如何定义类型别名以及如何在函数中使用类型别名。
47 1
typeScript进阶(9)_type类型别名
|
2月前
|
JavaScript 前端开发 安全
深入理解TypeScript:增强JavaScript的类型安全性
【10月更文挑战第8天】深入理解TypeScript:增强JavaScript的类型安全性
58 0
|
2月前
|
JavaScript 前端开发 开发者
深入理解TypeScript:类型系统与实用技巧
【10月更文挑战第8天】深入理解TypeScript:类型系统与实用技巧
|
3月前
|
存储 JavaScript
typeScript进阶(11)_元组类型
本文介绍了TypeScript中的元组(Tuple)类型,它是一种特殊的数组类型,可以存储不同类型的元素。文章通过示例展示了如何声明元组类型以及如何给元组赋值。元组类型在定义时需要指定数组中每一项的类型,且在赋值时必须满足这些类型约束。此外,还探讨了如何给元组类型添加额外的元素,这些元素必须符合元组类型中定义的类型联合。
52 0
|
3月前
|
JavaScript
typeScript进阶(10)_字符串字面量类型
本文介绍了TypeScript中的字符串字面量类型,这种类型用来限制变量只能是某些特定的字符串字面量。通过使用`type`关键字声明,可以确保变量的值限定在预定义的字符串字面量集合中。文章通过示例代码展示了如何声明和使用字符串字面量类型,并说明了它在函数默认参数中的应用。
40 0