TS进阶!深入探索 TypeScript 工具类型:内置神器与高阶扩展

前言

TypeScript 的类型系统是其最强大的特性之一,它提供了丰富的内置工具类型(Utility Types),让我们能够以声明式的方式操作和转换类型。这些工具类型就像是类型系统的"瑞士军刀",能大幅提升前端同学的开发效率并减少错误。本文将带您了解 TypeScript 常用的内置工具类型,并基于它们构建强大的高阶TS类型扩展。✨✨✨

一、TypeScript 常用内置工具类型详解📝📝📝

1. 基本对象工具

Partial<T> - 将一个类型的所有属性设置为可选

ts 复制代码
interface User {
  id: number;
  name: string;
  email: string;
}

type PartialUser = Partial<User>;
/* 等价于:
{
  id?: number;
  name?: string;
  email?: string;
}
*/

Required<T> - 将一个类型的所有属性设置为必需

ts 复制代码
type OptionalUser = {
  id?: number;
  name?: string;
};

type RequiredUser = Required<OptionalUser>;
/* 等价于:
{
  id: number;
  name: string;
}
*/

Readonly<T> - 使一个类型的所有属性变为只读

ts 复制代码
interface MutableConfig {
  apiUrl: string;
  timeout: number;
}

type ImmutableConfig = Readonly<MutableConfig>;
/* 等价于:
{
  readonly apiUrl: string;
  readonly timeout: number;
}
*/

const config: Readonly<Config> = { theme: "dark" };
// config.theme = "light" // 错误:只读属性

Record<K, T> - 创建一个具有特定键类型和值类型的对象类型

创建一个具有特定键类型和值类型的对象类型。

ts 复制代码
type PagePaths = Record<'home' | 'about' | 'contact', string>;
/* 等价于:
{
  home: string;
  about: string;
  contact: string;
}
*/

type UserMap = Record<number, User>;
/* 等价于:
{
  [key: number]: User;
}
*/

2. 属性选择工具

Pick<T, K> - 从一个类型T挑选指定的属性K来创建新类型

ts 复制代码
interface FullUser {
  id: number;
  name: string;
  email: string;
  password: string;
}

type PublicUser = Pick<FullUser, 'id' | 'name' | 'email'>;
/* 等价于:
{
  id: number;
  name: string;
  email: string;
}
*/

Omit<T, K> - 从类型T排除指定的属性K来创建新类型

ts 复制代码
interface FullUser {
  id: number;
  name: string;
  email: string;
  password: string;
}

type SecureUser = Omit<FullUser, 'password'>;
/* 等价于:
{
  id: number;
  name: string;
  email: string;
}
*/

3. 联合类型工具

Exclude<T, U> - 从类型T中排除可赋值给类型U的类型

ts 复制代码
type T = number | string | boolean;
type Numeric = Exclude<T, string | boolean>; // number

Extract<T, U> - 从类型T中提取可赋值给类型U的类型

ts 复制代码
type T = string | number | boolean;
type StringOnly = Extract<T, string>; // string
type Numeric = Extract<T, number | bigint>; // number

NonNullable<T> - 从类型T中排除null和undefined

ts 复制代码
type Nullable = string | null | undefined;
type SafeString = NonNullable<Nullable>; // string

4. 函数类型工具

ReturnType<T> - 获取函数类型的返回类型

ts 复制代码
type Fn = () => Promise<string>;
type Result = ReturnType<Fn>; // Promise<string>

Parameters<T> - 获取函数类型的参数元组类型

ts 复制代码
type Handler = (req: Request, res: Response) => void;
type HandlerArgs = Parameters<Handler>; // [Request, Response]

ConstructorParameters<T> - 获取构造函数类型的参数元组类型

ts 复制代码
class User {
  constructor(public id: number, public name: string) {}
}
type UserParams = ConstructorParameters<typeof User>; // [number, string]

二、TS进阶!基于内置类型的高阶TS扩展🚀🚀🚀

1. 基础类型扩展🏍🏍🏍

KeyOf<T> - 获取类型 T 的所有键名的联合类型

ts 复制代码
declare type KeyOf<T> = keyof T;

// 示例
type Obj = { a: number; b: string }; 
type Keys = KeyOf<Obj>; // "a" | "b"

Awaitable<T> - 获取异步函数的返回值类型

ts 复制代码
declare type Awaitable<T> = T | Promise<T> | Awaitable<T>;

const a: Awaitable<number> = 42;
const b: Awaitable<number> = Promise.resolve(42);

ElementType<T> - 获取数组类型的元素类型

ts 复制代码
declare type ElementType<T> = T extends Array<infer U> ? U : never;

type A = ElementType<string[]>; // string
type B = ElementType<number>; // never

Arrayable<T> - 可数组化类型,创建 T 或者 T 的数组类型

ts 复制代码
declare type Arrayable<T> = T | T[];

const a: Arrayable<string> = "hello";
const b: Arrayable<string> = ["hello", "world"];

ToArray<T> - 将类型 T 转换为数组类型,如果已经是数组则保持不变

ts 复制代码
declare type ToArray<T> = T extends (infer U)[] ? U[] : T[];

type A = ToArray<string>; // string[]
type B = ToArray<number[]>; // number[]

ArgumentType<T> - 获取函数类型的参数元组类型

ts 复制代码
declare type ArgumentType<T> = T extends ((...args: infer A) => unknown) ? A : never;

type Fn = (a: number, b: string) => boolean;
type Args = ArgumentType<Fn>; // [number, string]

Shift<T> - 移除元组类型的第一个元素

ts 复制代码
declare type Shift<T> = T extends [_: unknown, ...args: infer A] ? A : never;

type A = Shift<[number, string, boolean]>; // [string, boolean]

Pop<T> - 移除元组类型的最后一个元素

ts 复制代码
declare type Pop<T extends unknown[]> = T extends [...infer Rest, unknown] ? Rest : never;

type A = Pop<[number, string, boolean]>; // [number, string]

FlatObjectTuple<T> - 扁平化对象元组类型,用于解决交叉类型显示问题

ts 复制代码
declare type FlatObjectTuple<T> = { [K in keyof T]: T[K]; };

2. 中级进阶扩展✈️✈️✈️

(1) 类型断言工具

AssertEqual<T, U> - 断言两个类型是否相等

ts 复制代码
declare type AssertEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : false) : false;

(2) 深度类型转换

DeepPartial<T> - 深度可选类型,递归地将所有属性设为可选

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

type Obj = { a: { b: number; c: string } };
type PartialObj = DeepPartial<Obj>; // { a?: { b?: number; c?: string } };

(3) 对象属性控制

PartialByKeys<T, K> - 部分属性可选类型

ts 复制代码
declare type PartialByKeys<T, K extends keyof T = keyof T> = FlatObjectTuple<Partial<Pick<T, Extract<keyof T, K>>> & Omit<T, K>>;

type Obj = { a: number; b: string; c: boolean };
type PartialA = PartialByKeys<Obj, 'a' | 'b'>; // { a?: number; b?: string; c: boolean };

RequiredByKey<T, K> - 部分属性必需类型

ts 复制代码
declare type RequiredByKey<T, K extends keyof T = keyof T> = FlatObjectTuple<Required<Pick<T, Extract<keyof T, K>>> & Omit<T, K>>;

type Obj = { a?: number; b?: string; c?: boolean };
type RequiredA = RequiredByKey<Obj, 'a' | 'b'>; // { a: number; b: string; c?: boolean };

3. 高级函数工具🚀🚀🚀

FunctionKeysObject<T> - 提取对象中所有函数类型的属性

ts 复制代码
declare type FunctionKeysObject<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : never
}

FunctionMapType<F> - 创建类型安全的函数事件系统

ts 复制代码
declare type FunctionMapType<F> = <T extends keyof FunctionKeysObject<F>>(
  key: T,
  ...args: Parameters<F[T]>
) => void

interface EventFunctions {
  a(): void
  b(params: { c: string; d: number }): void
}
declare const emit: FunctionMapType<EventFunctions>
emit('a') // 正确
emit('b', { c: '123', d: 123 }) // 正确
emit('b', { c: 123, d: '123' }) // 错误: 类型不匹配
emit('b', { c: 0, d: 1 }) // 错误: 类型不匹配

结语

类型就是文档,类型就是约束,类型就是力量!

TypeScript 的工具类型系统就像是一个强大类型转换工厂,内置工具类型提供基础能力,而基于它们实现的高阶扩展则可以用更优雅、更安全的方式构建复杂系统。 ✨✨✨

各位看官,如果感觉学到了,烦请抬起优雅的小手,给咱来个三连~~~ 👍🪙❤️

PS:转载请注明出处。

相关推荐
liuyang___5 分钟前
日期的数据格式转换
前端·后端·学习·node.js·node
贩卖纯净水.1 小时前
webpack其余配置
前端·webpack·node.js
码上奶茶1 小时前
HTML 列表、表格、表单
前端·html·表格·标签·列表·文本·表单
抹茶san2 小时前
和 Trae 一起开发可视化拖拽编辑项目(1) :迈出第一步
前端·trae
风吹头皮凉2 小时前
vue实现气泡词云图
前端·javascript·vue.js
南玖i2 小时前
vue3 + ant 实现 tree默认展开,筛选对应数据打开,简单~直接cv
开发语言·前端·javascript
小钻风33662 小时前
深入浅出掌握 Axios(持续更新)
前端·javascript·axios
萌萌哒草头将军2 小时前
🚀🚀🚀尤雨溪推荐的这个库你一定要知道!轻量⚡️,优雅!
前端·vue.js·react.js
三门2 小时前
docker安装mysql8.0.20过程
前端