编程进阶网 编程进阶网
首页
  • 在线工具
  • 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泛型编程实战
      • TS高级类型编程
        • 1. 案例引入
          • 1.1 烦人的重复定义
          • 1.2 类型编程的解法
        • 2. 索引访问类型
          • 2.1 基础语法:T[K]
          • 2.2 配合 keyof 和泛型
          • 2.3 数组元素的索引访问
        • 3. 映射类型(Mapped Types)
          • 3.1 核心语法
          • 3.2 映射的三种遍历方式
          • 3.3 映射修饰符
        • 4. 模板字面量类型(Template Literal Types)
          • 4.1 字符串类型的"模板引擎"
          • 4.2 内置字符串工具类型
          • 4.3 实战:API 路径类型系统
        • 5. 递归类型
          • 5.1 为什么需要递归类型
          • 5.2 递归映射——深只读
          • 5.3 递归类型的更多应用
        • 6. 内置工具类型深度拆解
          • 6.1 Partial / Required / Readonly
          • 6.2 Pick / Omit
          • 6.3 Record
          • 6.4 Exclude / Extract / NonNullable
          • 6.5 ReturnType / Parameters / ConstructorParameters
        • 7. 实战:类型安全的表单校验系统
        • 8. 速查表
      • TS类型守卫机制
      • TS模块系统详解
      • TS工程配置实践实践
    • Vue高级进阶

    • Web工程化实践

  • Linux应用开发

  • IoT智能硬件开发

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

TS高级类型编程

# 05.TS高级类型编程

映射类型、模板字面量类型、递归类型——掌握"类型体操"的核心技巧,让类型为你生成类型。

# 1. 案例引入

# 1.1 烦人的重复定义

// 产品接口——创建时需要所有字段
interface Product {
    id: number;
    name: string;
    price: number;
    description: string;
}

// 更新产品时,所有字段都可选
// 又写一遍?万一原始类型有字段增删……
interface ProductUpdate {
    id?: number;
    name?: string;
    price?: number;
    description?: string;
}

# 1.2 类型编程的解法

// 一行搞定——永远不会与原始定义脱节
type ProductUpdate = Partial<Product>;
// 自动包含了所有字段的可选版本

// 当 Product 新增 stock 字段时,ProductUpdate 自动跟上

这就是高级类型编程的魅力:不是"一个类型写一个类型",而是用类型运算符从已有类型推导出新类型。


# 2. 索引访问类型

# 2.1 基础语法:T[K]

interface User {
    name: string;
    age: number;
    tags: string[];
}

type NameType = User["name"];  // string
type TagsType = User["tags"];  // string[]

// 联合访问——同时取多个属性
type NameOrAge = User["name" | "age"];  // string | number

# 2.2 配合 keyof 和泛型

// 安全的属性类型提取
type PropType<T, K extends keyof T> = T[K];

type U1 = PropType<User, "name">;  // string
type U2 = PropType<User, "age">;   // number
// type U3 = PropType<User, "email">;  // ❌

// 访问所有属性的类型
type AllTypes = User[keyof User];  // string | number | string[]

# 2.3 数组元素的索引访问

type Arr = string[];
type ElementType = Arr[number];  // string

// 结合泛型——获取任意数组的元素类型
type ElementOf<T> = T extends (infer E)[] ? E : T[number];
type E1 = ElementOf<number[]>;     // number
type E2 = ElementOf<User[]>;       // User

# 3. 映射类型(Mapped Types)

# 3.1 核心语法

// 语法:{ [K in 联合类型]: 新类型 }
// 遍历每个 key,生成新的属性类型

// 将 T 的所有属性变为只读
type MyReadonly<T> = {
    readonly [K in keyof T]: T[K];
};

// 使用
type ReadonlyUser = MyReadonly<User>;
// { readonly name: string; readonly age: number; readonly tags: string[] }

# 3.2 映射的三种遍历方式

// 1. 遍历 keyof T——保留所有属性
type Clone<T> = { [K in keyof T]: T[K] };

// 2. 遍历固定联合类型——创建指定属性
type Tags = { [K in "title" | "author"]: string };
// { title: string; author: string }

// 3. 结合 as 重映射(TypeScript 4.1+)
type Getters<T> = {
    [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number; getTags: () => string[] }

# 3.3 映射修饰符

// + 和 - 控制 readonly 和 ?
type Mutable<T> = {
    -readonly [K in keyof T]: T[K];  // 去掉 readonly
};

type Required<T> = {
    [K in keyof T]-?: T[K];          // 去掉 ?(强制必填)
};

// 练习:组合使用
type PartialReadonly<T> = {
    readonly [K in keyof T]?: T[K];  // 全部只读 + 可选
};

# 4. 模板字面量类型(Template Literal Types)

# 4.1 字符串类型的"模板引擎"

// TypeScript 4.1+ 新特性
type Greeting = `Hello, ${string}`;
let g1: Greeting = "Hello, World";   // ✅
// let g2: Greeting = "Hi, World";   // ❌

// 与字面量联合类型组合——真正的"类型计算"
type EventName = "click" | "hover" | "focus";
type ElementName = "Button" | "Input";

type ComponentEvent = `${ElementName}${Capitalize<EventName>}`;
// "ButtonClick" | "ButtonHover" | "ButtonFocus"
// | "InputClick" | "InputHover" | "InputFocus"  ——6种组合自动生成

# 4.2 内置字符串工具类型

// TS 内置四个字符串操作类型
type T1 = Uppercase<"hello">;          // "HELLO"
type T2 = Lowercase<"HELLO">;          // "hello"
type T3 = Capitalize<"hello">;         // "Hello"
type T4 = Uncapitalize<"Hello">;       // "hello"

# 4.3 实战:API 路径类型系统

// 为 REST API 生成类型安全的路径
type Resource = "user" | "post" | "comment";
type Method = "get" | "create" | "update" | "delete";

type APIPath = `/${Resource}/${Method}`;
// 自动生成:
// "/user/get" | "/user/create" | "/user/update" | "/user/delete"
// | "/post/get" | "/post/create" | ...

// 进一步约束
type APIPathWithID = `/${Resource}/${string}/${Method}`;
let path: APIPathWithID = "/user/123/update";  // ✅
// let path2: APIPathWithID = "/user/get";     // ❌ 缺少 ID

# 5. 递归类型

# 5.1 为什么需要递归类型

// 需求:深层嵌套的对象全部 readonly
interface Nested {
    name: string;
    settings: {
        theme: string;
        notifications: {
            email: boolean;
            push: boolean;
        };
    };
}

// 普通映射类型只到第一层
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type R = Readonly<Nested>;
// R.settings.notifications.push = false;  // ✅ 内层不是 readonly!

# 5.2 递归映射——深只读

type DeepReadonly<T> = {
    readonly [K in keyof T]: T[K] extends object
        ? T[K] extends Function
            ? T[K]                    // 函数不需要深层只读
            : DeepReadonly<T[K]>       // 递归处理嵌套对象
        : T[K];
};

type DeepR = DeepReadonly<Nested>;
// DeepR.settings.notifications.push = false;  // ❌ 深层也被 readonly 了

# 5.3 递归类型的更多应用

// 深 Partial
type DeepPartial<T> = {
    [K in keyof T]?: T[K] extends object
        ? DeepPartial<T[K]>
        : T[K];
};

// 平展联合数组
type Flatten<T> = T extends (infer E)[] ? E : T;
type Flat = Flatten<string[][]>;  // string[] — 只平展了一层

// 递归平展
type DeepFlatten<T> = T extends (infer E)[] ? DeepFlatten<E> : T;
type DeepFlat = DeepFlatten<string[][][]>;  // string

# 6. 内置工具类型深度拆解

# 6.1 Partial / Required / Readonly

// TypeScript 标准库实现
type Partial<T> = {
    [P in keyof T]?: T[P];
};

type Required<T> = {
    [P in keyof T]-?: T[P];     // -? 移除可选
};

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

# 6.2 Pick / Omit

// Pick:从 T 中挑出指定属性
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};
type NameOnly = Pick<User, "name">;  // { name: string }

// Omit:从 T 中移除指定属性(组合 Exclude + Pick)
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type WithoutAge = Omit<User, "age">;    // { name: string; tags: string[] }
type WithoutNameAndAge = Omit<User, "name" | "age">;  // { tags: string[] }

# 6.3 Record

// Record:创建键值类型映射
type Record<K extends keyof any, V> = {
    [P in K]: V;
};

// 统一值类型
type PageInfo = Record<"home" | "about" | "contact", { title: string }>;
// { home: {title:string}; about: {title:string}; contact: {title:string} }

// 字典模式
type StringMap = Record<string, unknown>;

# 6.4 Exclude / Extract / NonNullable

type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
type NonNullable<T> = T extends null | undefined ? never : T;

type T1 = Exclude<"a" | "b" | "c", "b" | "c">;   // "a"
type T2 = Extract<"a" | "b" | "c", "a" | "b">;     // "a" | "b"
type T3 = NonNullable<string | null | undefined>;   // string

# 6.5 ReturnType / Parameters / ConstructorParameters

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

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

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

# 7. 实战:类型安全的表单校验系统

// 步骤 1:定义表单数据类型
interface LoginForm {
    username: string;
    password: string;
    remember: boolean;
}

// 步骤 2:为表单生成校验规则类型
type ValidationRule<T> = {
    [K in keyof T]: (value: T[K]) => string | null;
};

// 步骤 3:定义校验器(每个字段类型不同,校验也不同)
const loginValidator: ValidationRule<LoginForm> = {
    username: (v: string) => v.length >= 3 ? null : "用户名至少3个字符",
    password: (v: string) => v.length >= 6 ? null : "密码至少6个字符",
    // remember: (v: boolean) => null  // 可选
};

// 步骤 4:错误信息生成——Partial + Record
type FormErrors<T> = Partial<Record<keyof T, string>>;

function validate<T>(data: T, rules: Partial<ValidationRule<T>>): FormErrors<T> {
    const errors: FormErrors<T> = {};
    for (const key in rules) {
        const rule = rules[key];
        if (rule) {
            const error = rule(data[key]);
            if (error) errors[key] = error;
        }
    }
    return errors;
}

// 使用——全类型安全
const errors = validate({ username: "ab", password: "1234", remember: true }, loginValidator);
// errors: { username?: string; password?: string; remember?: string }

# 8. 速查表

类型操作 语法 效果
索引访问 T["key"] 提取属性类型
遍历映射 { [K in T]: V } 为联合每个成员生成属性
带 as 映射 { [K in T as New]: V } 重命名 key
只读 +readonly / -readonly 加/去 readonly
可选 +? / -? 加/去 ?
模板字面量 `前${T}后` 拼接字符串类型
条件 T extends U ? X : Y 分支类型
递归 DeepReadonly<T> = ... 自引用类型
Partial [P in keyof T]?: T[P] 全部可选
Pick Pick<T, K> 选择属性子集
Omit Omit<T, K> 排除指定属性
Record Record<K, V> 键值对映射
ReturnType ReturnType<Fn> 取函数返回值

下一篇:06.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号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式