You can do something like this in TypeScript, and we are going to explain how everything works...
此处我们介绍一些 TypeScript 的进阶用法...
type CustomAwaited<T> = T extends Promise<infer P> ? P : T
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type CustomReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never
export type A = CustomReturnType<typeof fetch>
export type B = CustomAwaited<A>
infer allows you to "deconstruct type structures" and extract the parts you need in the type system, used for building type inference, reuse, and abstract logic. It's a powerful tool for advanced TypeScript type programming. If you write hooks, form validation, data mapping, API encapsulation libraries, or use libraries like Zod, React Hook Form, TRPC, etc., infer is working behind the scenes.
infer 让你在类型系统中"拆解类型结构"并提取需要的部分,用于构建类型推导、重用、抽象逻辑,是 TypeScript 高级类型编程的利器。如果你写 hook、form validation、数据映射、API 封装库、或者用 Zod, React Hook Form, TRPC 等库时,infer 背后都在工作。
📌 Usage: Automatically get the parameter types of a function, used for overloading, higher-order functions, hook encapsulation, etc.
📌 用途: 自动获取一个函数的参数类型,用于重载、高阶函数、hook 封装等。
export type Params<T> = T extends (...args: infer P) => any ? P : never;
export type Fn = (a: string, b: number) => void;
export type FnParams = Params<Fn>; // => [a: string, b: number]
// example (实例):
export const fetchUser = (name: string, age: number) => console.log(`user: ${name}, age: ${age}`);
function withLogging<F extends (...args: any[]) => any>(fn: F) {
return (...args: [...Params<F>]) => {
console.log("[LOG] Args:", args);
return fn(...args);
};
}
const loggedFetch = withLogging(fetchUser);
//自动推导参数类型,无需手动写:
//loggedFetch(name: string, age: number)
loggedFetch("Alice", 25);
📌 Usage: Automatically infer return value types in utility libraries, hooks, middleware.
📌 用途: 在工具库、hook、middleware 中可自动推导返回值类型。
export type MyReturn<T> = T extends (...args: any[]) => infer R ? R : never;
export type MyFn = () => boolean;
export type Result = MyReturn<MyFn>; // boolean
// example (实例):
export const getToken = () => "abc123";
function wrapWithTimestamp<F extends (...args: any[]) => any>(fn: F) {
return (...args: Parameters<F>): { data: MyReturn<F>; timestamp: number } => {
const result = fn(...args);
return {
data: result,
timestamp: Date.now()
};
};
}
const wrapped = wrapWithTimestamp(getToken);
const res = wrapped();
// ✅ 类型安全推导返回值类型:
// { data: string; timestamp: number }
console.log(res.data, res.timestamp);
📌 Usage: Infer the actual return type in async function encapsulation.
📌 用途: 在 async 函数封装中可推断真实返回类型。
export type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
export type A13 = UnwrapPromise<Promise<string>>; // string
export type B13 = UnwrapPromise<number>; // number
📌 Usage: Analyze tuple types, implement type-safe parameter extraction.
📌 用途: 分析元组类型、实现类型安全的参数提取。
export type First<T> = T extends [infer F, ...any[]] ? F : never;
export type A14 = First<[string, number, boolean]>; // string
Although this example doesn't directly use infer, when combined with infer it can further extract nested properties, such as:
虽然这个例子不直接用 infer,但配合 infer 可进一步抽出嵌套属性,如:
export type PropType<T, K extends keyof T> = T[K];
export type User = { id: number; name: string };
export type NameType = PropType<User, "name">; // string
export type NestedProp<T> = T extends { inner: infer U } ? U : never;
export type Obj = { inner: { value: string } };
export type Res = NestedProp<Obj>; // { value: string }
📌 Usage: Dynamically extract field types in generic components, table libraries, form libraries.
📌 用途: 在泛型组件、表格库、表单库中动态提取字段类型。
export type GetArrayItemType<T> = T extends Array<infer U> ? U : never;
export type A16 = GetArrayItemType<number[]>; // number
export type B16 = GetArrayItemType<readonly string[]>; // string
Using infer + template literal types, you can parse string format content at the type level, building safe and flexible type logic. These techniques are commonly used for:
- Building string-type APIs (such as routes, commands, query parameters)
- Mapping string structures to objects/tuples
- Writing strongly-typed DSLs, rule interpreters, utility libraries This is especially useful if you're doing any development involving "string format conventions", such as internal platforms, config tools, file rules, schema validation.
使用 infer + 模板字面量类型,你可以在类型层面解析字符串格式内容,构建安全且灵活的类型逻辑。 这类技巧多用于:
- 构建字符串类型 API(如路由、命令、查询参数)
- 将字符串结构映射为对象/元组
- 编写强类型的 DSL、规则解释器、工具库 如果你做任何涉及"字符串格式约定"的开发,比如内部平台、config 工具、文件规则、schema 校验,这特别有用。
📌 Usage: Type-level processing of file systems, building filename inference logic in build systems.
📌 用途: 类型级处理文件系统、构建系统中的文件名推断逻辑。
export type FileName<T extends string> =
T extends `${infer Name}.${infer Ext}` ? { name: Name; ext: Ext } : never;
export type A21 = FileName<"index.ts">; // { name: "index", ext: "ts" }
// example (实例):
function parseFileName<T extends string>(fname: T): FileName<T> {
const [name, ext] = fname.split('.') as [string, string];
return { name, ext } as FileName<T>;
}
const result = parseFileName("index.ts"); // { name: "index", ext: "ts" }
📌 Usage: Type-safe handling of query string formats.
📌 用途: 类型安全地处理 query string 字符串格式。
export type Param<T extends string> =
T extends `${infer Key}=${infer Val}` ? [Key, Val] : never;
export type A22 = Param<"lang=en">; // ["lang", "en"]
export type B22 = Param<"page=2">; // ["page", "2"]
📌 Usage: Convert string data structures to strongly-typed objects, commonly used for form fields, API response mapping.
📌 用途: 将字符串数据结构转成强类型对象,常用于表单字段、API 响应映射。
export type ToKV<T extends string> =
T extends `${infer K}:${infer V}` ? { key: K; value: V } : never;
export type A23 = ToKV<"id:123">; // { key: "id"; value: "123" }
📌 Usage: Build type-safe frontend routes (such as Next.js dynamic routes).
📌 用途: 构建类型安全的前端路由(如 Next.js dynamic routes)。
export type RouteParam<T extends string> =
T extends `:${infer Param}` ? Param : never;
export type A24 = RouteParam<":userId">; // "userId"
export type B24 = RouteParam<"static">; // never
📌 Usage: Write type-safe command-line tools, robot commands, configuration string parsing, etc.
📌 用途: 写类型安全的命令行工具、机器人命令、配置字符串解析等。
export type CommandParser<T extends string> =
T extends `set:${infer Key}:${infer Val}` ? { key: Key; value: Val } : never;
export type Cmd25 = CommandParser<"set:age:30">; // { key: "age"; value: "30" }
📌 Usage: Convert internal Objects to DTO Objects, etc.
${}
usages that modify properties
📌 用途: 内部 Object 转化成 DTO Object 等等
${}
这些用法对属性的改变
type MyRecord = {
readonly name: string
age?: number
};
type Modifier<T> = {
-readonly [K in keyof T]-?: T[K]
};
type WithGetter<T, U = Modifier<T>> = Modifier<T> & {
[K in keyof U as `get${Capitalize<K & string>}`]: ()=> U[K]
}
const rec: WithGetter<MyRecord> = {
name: "asdf",
age: 2343,
getName: () => "asdf",
getAge: () => 2342
};
rec.name = "sdfg";
//a.age = undefined; //error age is not optional
const name = rec.getName();
const age = rec.getAge();