Skip to content

ylli2000/ts-infer-extend-ternaries-examples

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TypeScript Advanced Type Features

🧠 Intro

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>

1. Common Usage (常见用途)

🧠 Summary

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 背后都在工作。

✅ 1.1 Extract function parameter types (utility type encapsulation)

📌 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);

✅ 1.2 Get the return type of a function (similar to built-in ReturnType)

📌 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);

✅ 1.3 Extract Promise result type (like automatic unwrap) or an immediately returned T

📌 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

✅ 1.4 Automatically destructure array or tuple element types

📌 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

✅ 1.5 Conditionally process class, object properties (simulate keyof or extract properties)

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 }

✅ 1.6 Simulate type pattern matching (similar to type-level regex)

📌 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

2. Template Literal Types (模板字面量类型例子)

🧠 Summary

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 校验,这特别有用。

✅ 2.1 Parse filenames or paths

📌 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" }

✅ 2.2 Parse string-type key-value pairs (such as URL parameters)

📌 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"]

✅ 2.3 Convert string-type arrays to object types (mapping)

📌 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" }

✅ 2.4 Route parameter parsing (for typed routers)

📌 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

✅ 2.5 Build DSL (Domain-Specific Language) type APIs

📌 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" }

3. Object Type Modification

✅ Change type properties, names, add Getters

📌 Usage: Convert internal Objects to DTO Objects, etc.

⚠️ Note the -readonly, -?, as ${} usages that modify properties

📌 用途: 内部 Object 转化成 DTO Object 等等

⚠️ 注意 -readonly, -?, as ${} 这些用法对属性的改变

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();

About

Some advanced Typescript use examples, infer, extend, ternaries, key looping

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published