编程进阶网 编程进阶网
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • Android提升进阶

  • iOS开发和进阶

  • Web开发和进阶

    • README
    • HTML工具手册

    • CSS样式与布局

    • JavaScript核心

    • TypeScript入门

      • README
      • TS类型系统基础
      • TS接口与对象类型
      • TS函数与类实战
      • TS泛型编程实战
        • 1. 案例引入
          • 1.1 没有泛型的痛苦
          • 1.2 泛型登场
        • 2. 泛型函数
          • 2.1 基本语法与类型推断
          • 2.2 多类型参数
          • 2.3 泛型在数组与 Promise 中的应用
        • 3. 泛型约束(extends)
          • 3.1 为什么要约束
          • 3.2 extends 约束语法
          • 3.3 类型参数之间的约束
          • 3.4 多重约束
        • 4. 泛型接口与泛型类
          • 4.1 泛型接口
          • 4.2 泛型类
          • 4.3 泛型类的静态成员
        • 5. keyof 与 typeof(类型上下文)
          • 5.1 keyof:获取对象类型的 key 联合
          • 5.2 typeof 在类型上下文
        • 6. 泛型条件类型
          • 6.1 条件类型语法
          • 6.2 分布式条件类型
          • 6.3 阻止分发
        • 7. infer 关键字
          • 7.1 infer 只能在条件类型中使用
          • 7.2 infer 的四种经典模式
          • 7.3 infer 的递归推断
        • 8. 实战案例:类型安全的 API 客户端
        • 9. 速查表
      • TS高级类型编程
      • TS类型守卫机制
      • TS模块系统详解
      • TS工程配置实践实践
    • Vue高级进阶

    • Web工程化实践

  • Linux应用开发

  • IoT智能硬件开发

  • Apps
  • Web开发和进阶
  • TypeScript入门
杨充
2025-06-24
目录

TS泛型编程实战

# 04.TS泛型编程实战

泛型是 TS 类型系统的灵魂——让你写"一次定义,多种类型复用"的代码。从约束到条件类型,从 infer 到实战模式。

# 1. 案例引入

# 1.1 没有泛型的痛苦

// 需求:返回数组第一个元素
function first(arr: number[]): number { return arr[0]; }    // 只能 number[]
function firstStr(arr: string[]): string { return arr[0]; } // 再来一个 string[]
function firstUser(arr: User[]): User { return arr[0]; }    // 又来一个 User[]

// 用 any?失去类型安全
function firstAny(arr: any[]): any { return arr[0]; }
const n = firstAny([1, 2, 3]);   // n: any —— 后续完全失去类型提示
n.toFixed();  // 编译不会报错,但如果实际是 string 就崩了

# 1.2 泛型登场

function first<T>(arr: T[]): T {
    return arr[0];
}

const n = first([1, 2, 3]);           // T = number → n: number
const s = first(["a", "b", "c"]);     // T = string → s: string
const u = first([{ name: "张三" }]);   // T = { name: string } → u 类型完整保留

泛型的本质:不是"可以接受任何类型",而是"延迟确定具体类型,但不丢失类型信息"。


# 2. 泛型函数

# 2.1 基本语法与类型推断

// <T> —— 类型参数,调用时确定
function identity<T>(arg: T): T {
    return arg;
}

// 显式指定类型
let a = identity<string>("hello");  // T = string

// 自动推断(最常用)
let b = identity("hello");          // T 自动推断为 "hello"
let c = identity(42);               // T 自动推断为 number

# 2.2 多类型参数

// 多个泛型参数
function pair<K, V>(key: K, value: V): [K, V] {
    return [key, value];
}

const p = pair("name", 42);  // [string, number]
// K = string, V = number

# 2.3 泛型在数组与 Promise 中的应用

// 数组
function reverse<T>(arr: T[]): T[] {
    return arr.reverse();
}

// Promise
async function fetchData<T>(url: string): Promise<T> {
    const response = await fetch(url);
    return response.json();  // 返回类型与 T 绑定
}

const user = await fetchData<User>("/api/user");
// user: User

# 3. 泛型约束(extends)

# 3.1 为什么要约束

function getLength<T>(arg: T): number {
    // return arg.length;  // ❌ T 上不存在属性 length
    return 0;
}

# 3.2 extends 约束语法

// 约束 T 必须有 length 属性
function getLength<T extends { length: number }>(arg: T): number {
    return arg.length;  // ✅ T 保证有 length
}

getLength("hello");     // ✅ string 有 length
getLength([1, 2, 3]);   // ✅ 数组有 length
// getLength(42);       // ❌ number 没有 length

# 3.3 类型参数之间的约束

// T 必须有 K 属性
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

const user = { name: "张三", age: 25 };
getProperty(user, "name");   // ✅ string
getProperty(user, "age");    // ✅ number
// getProperty(user, "email"); // ❌ email 不存在于 user

这是泛型最常用的模式——约束第二个类型参数必须是第一个的 key。

# 3.4 多重约束

interface HasId { id: number; }
interface HasTimestamp { createdAt: Date; }

// T 必须同时满足 HasId 和 HasTimestamp
function save<T extends HasId & HasTimestamp>(record: T): void {
    console.log(`保存 ${record.id}, 创建于 ${record.createdAt}`);
}

# 4. 泛型接口与泛型类

# 4.1 泛型接口

// 泛型接口的基础用法
interface Box<T> {
    value: T;
}

let stringBox: Box<string> = { value: "hello" };
let numberBox: Box<number> = { value: 42 };

// 实战:泛型仓库模式
interface Repository<T> {
    findById(id: number): Promise<T | null>;
    findAll(): Promise<T[]>;
    save(entity: T): Promise<void>;
    delete(id: number): Promise<void>;
}

// User 仓库
class UserRepository implements Repository<User> {
    async findById(id: number) { /* ... */ }
    async findAll() { /* ... */ }
    async save(user: User) { /* ... */ }
    async delete(id: number) { /* ... */ }
}

# 4.2 泛型类

class Stack<T> {
    private items: T[] = [];

    push(item: T): void {
        this.items.push(item);
    }

    pop(): T | undefined {
        return this.items.pop();
    }

    peek(): T | undefined {
        return this.items[this.items.length - 1];
    }

    get size(): number {
        return this.items.length;
    }
}

const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
const top = numberStack.pop();  // number | undefined

# 4.3 泛型类的静态成员

class Utility<T> {
    // 静态成员不能使用类的泛型类型参数
    // static default: T;  // ❌ 静态成员属于类本身,不属于实例

    // 静态方法可以有自己独立的泛型
    static create<T>(value: T): Utility<T> {
        return new Utility<T>();
    }
}

# 5. keyof 与 typeof(类型上下文)

# 5.1 keyof:获取对象类型的 key 联合

interface Person {
    name: string;
    age: number;
    email: string;
}

type PersonKeys = keyof Person;
// "name" | "age" | "email"

// 与泛型约束结合——安全的属性访问
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

const p: Person = { name: "张三", age: 25, email: "a@b.com" };
getValue(p, "name");   // ✅ string
getValue(p, "age");    // ✅ number
// getValue(p, "foo"); // ❌ 编译错误

# 5.2 typeof 在类型上下文

// 值空间 → 类型空间
const config = {
    host: "localhost",
    port: 3000,
    ssl: true
};

// 获取 config 的类型
type Config = typeof config;
// { host: string; port: number; ssl: boolean }

// 配合 keyof 使用
type ConfigKey = keyof typeof config;
// "host" | "port" | "ssl"

// 实战:从函数返回值提取类型
function makeUser(name: string, age: number) {
    return { name, age, createdAt: new Date() };
}
type User = ReturnType<typeof makeUser>;
// { name: string; age: number; createdAt: Date }

# 6. 泛型条件类型

# 6.1 条件类型语法

// T extends U ? X : Y
// 如果 T 可赋值给 U,则类型为 X,否则为 Y

type IsString<T> = T extends string ? "yes" : "no";

type A = IsString<string>;  // "yes"
type B = IsString<number>;  // "no"

# 6.2 分布式条件类型

// 当 T 是联合类型时,条件类型会"分发"
type ToArray<T> = T extends any ? T[] : never;

type Result = ToArray<string | number>;
// (string[] | number[]) ← 不是 (string | number)[]

// 这是条件类型最强大的特性——逐成员处理联合
type Exclude<T, U> = T extends U ? never : T;
type T1 = Exclude<"a" | "b" | "c", "a" | "c">;  // "b"

type Extract<T, U> = T extends U ? T : never;
type T2 = Extract<"a" | "b" | "c", "a" | "c">;   // "a" | "c"

# 6.3 阻止分发

// 用 [] 包裹阻止分发
type NoDistribute<T> = [T] extends [any] ? T[] : never;
type Result2 = NoDistribute<string | number>;
// (string | number)[] ← 不会分发,整个联合放入 []

# 7. infer 关键字

# 7.1 infer 只能在条件类型中使用

// infer R:声明 R 以捕获"推断出的类型"
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// 应用
type FnReturn = ReturnType<() => string>;     // string
type FnReturn2 = ReturnType<typeof Math.max>; // number

# 7.2 infer 的四种经典模式

// 1. 提取函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
type P1 = Parameters<(a: number, b: string) => void>;  // [number, string]

// 2. 提取数组元素类型
type ArrayElement<T> = T extends (infer E)[] ? E : never;
type E1 = ArrayElement<string[]>;      // string
type E2 = ArrayElement<number[]>;      // number

// 3. 提取 Promise 包裹的类型
type Awaited<T> = T extends Promise<infer V> ? V : T;
type V1 = Awaited<Promise<string>>;    // string
type V2 = Awaited<string>;             // string(不包装 Promise 原样返回)

// 4. 提取构造函数实例类型
type InstanceType<T extends new (...args: any[]) => any> =
    T extends new (...args: any[]) => infer I ? I : never;

class MyClass { name = "x"; }
type I = InstanceType<typeof MyClass>;  // MyClass

# 7.3 infer 的递归推断

// 递归展开 Promise(直到非 Promise 类型)
type DeepAwaited<T> = T extends Promise<infer V>
    ? DeepAwaited<V>
    : T;

type D1 = DeepAwaited<Promise<Promise<number>>>;  // number

# 8. 实战案例:类型安全的 API 客户端

// 定义 API 路由映射
interface APIRoutes {
    "/api/user": { id: number; name: string };
    "/api/users": { id: number; name: string }[];
    "/api/login": { token: string; expiresAt: number };
}

// 泛型 API 客户端——完全类型安全
class ApiClient<Routes extends Record<string, any>> {
    constructor(private baseUrl: string) {}

    async get<K extends keyof Routes & string>(
        path: K
    ): Promise<Routes[K]> {
        const response = await fetch(`${this.baseUrl}${path}`);
        return response.json();
    }

    async post<K extends keyof Routes & string, Body extends Routes[K]>(
        path: K,
        body: Body
    ): Promise<Routes[K]> {
        const response = await fetch(`${this.baseUrl}${path}`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(body)
        });
        return response.json();
    }
}

const client = new ApiClient<APIRoutes>("https://api.example.com");

// 类型自动推断!
const user = await client.get("/api/user");    // { id: number; name: string }
const users = await client.get("/api/users");   // { id: number; name: string }[]
// client.get("/api/nonexistent");              // ❌ 编译错误

# 9. 速查表

语法 含义 示例
<T> 声明类型参数 function id<T>(x: T): T
T extends U 泛型约束 <T extends HasLength>
K extends keyof T 约束为对象 key (obj: T, key: K)
T[K] 索引访问(取属性类型) T["name"]
T extends U ? X : Y 条件类型 IsString<T>
infer R 捕获推断类型 ReturnType<T>
keyof T 获取 T 的所有 key keyof Person
typeof x 获取值的类型 typeof config

泛型三问(写泛型前自检):

  1. 有没有多个类型共享同一个逻辑?(→ 泛型函数)
  2. 不同实体需要相同的结构契约?(→ 泛型接口/类)
  3. 类型之间有关联关系?(→ 泛型约束 + keyof)

下一篇:05.TS高级类型编程

上次更新: 2026/06/24, 12:59:24
TS函数与类实战
TS高级类型编程

← TS函数与类实战 TS高级类型编程→

最近更新
01
CSS选择器入门
06-23
02
CSS定位与层级
06-23
03
CSS盒模型详解
06-23
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 鄂ICP备2024073355号-1 | 鄂ICP备2024073355号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式