普通视图

发现新文章,点击刷新页面。
昨天 — 2025年7月1日首页

TypeScript入门(七)高级类型:解锁TypeScript的"终极形态"

2025年7月1日 17:28

第7章 高级类型:解锁TypeScript的"终极形态"

想象你正在探索一座神秘的类型魔法学院——高级类型(Advanced Types) 就是这里最强大的魔法咒语。如果说基础类型是编程世界的砖瓦,那么高级类型就是建筑大师的蓝图工具。这一章,我们将学会如何运用这些类型魔法,让TypeScript从优秀走向卓越!

7.1 类型别名——给复杂类型起个"好记的名字"

类型别名(Type Aliases)就像给你的类型定制一张专属名片——复杂类型从此有了简洁易记的称呼。

🏷️ 基础类型别名:简化复杂类型

// 1. 基础类型别名 - 联合类型的简化
type UserID = string | number;
type Coordinate = [number, number];

// 使用示例
const userId1: UserID = "U1001";
const userId2: UserID = 12345;
const position: Coordinate = [100, 200];

console.log(userId1); // "U1001"
console.log(userId2); // 12345
console.log(position); // [100, 200]

// 2. 对象类型别名 - 复杂结构的命名
type UserProfile = {
    id: UserID;
    name: string;
    email?: string;
    createdAt: Date;
};

// 使用示例
const user: UserProfile = {
    id: "U1001",
    name: "技术宅",
    email: "tech@example.com",
    createdAt: new Date()
};

console.log(user.name); // "技术宅"
console.log(user.id); // "U1001"
console.log(typeof user.createdAt); // "object"

// 3. 函数类型别名 - 函数签名的命名
type StringProcessor = (input: string) => string;
type NumberValidator = (value: number) => boolean;

// 实现函数
const toUpper: StringProcessor = (str) => {
    const result = str.toUpperCase();
    console.log(`转换:"${str}" -> "${result}"`);
    return result;
};

const isPositive: NumberValidator = (num) => {
    const result = num > 0;
    console.log(`验证 ${num} > 0: ${result}`);
    return result;
};

// 使用示例
console.log(toUpper("hello")); 
// "转换:"hello" -> "HELLO""
// 返回:"HELLO"

console.log(isPositive(-5)); 
// "验证 -5 > 0: false"
// 返回:false

console.log(isPositive(10)); 
// "验证 10 > 0: true"
// 返回:true

🔄 类型别名的特性与注意事项

// 别名不是新建类型 - 只是引用
type ID = UserID;
const newUserId: ID = 1002; // 完全合法
console.log(typeof newUserId); // "number"

// 递归类型别名
type TreeNode = {
    value: string;
    children?: TreeNode[];
};

const tree: TreeNode = {
    value: "根节点",
    children: [
        { value: "子节点1" },
        { 
            value: "子节点2", 
            children: [
                { value: "孙节点" }
            ]
        }
    ]
};

console.log(tree.value); // "根节点"
console.log(tree.children?.[0].value); // "子节点1"
console.log(tree.children?.[1].children?.[0].value); // "孙节点"

// 泛型类型别名
type ApiResponse<T> = {
    success: boolean;
    data: T;
    message: string;       
};

const userResponse: ApiResponse<UserProfile> = {
    success: true,
    data: user,
    message: "获取用户成功"
};

console.log(userResponse.success); // true
console.log(userResponse.data.name); // "技术宅"
console.log(userResponse.message); // "获取用户成功"

🚨 重要提醒:类型别名不会创建新类型,它只是现有类型的引用(就像给文件创建快捷方式)。使用typeof检查时,仍然是原始类型。

🎯 类型别名的应用场景

  • 简化复杂联合类型:让代码更易读
  • 统一接口定义:保持项目类型一致性
  • 函数签名复用:避免重复定义
  • 递归数据结构:树形、链表等结构

7.2 字符串字面量类型——精确到字符的"选择题"

字符串字面量类型(String Literal Types)把字符串从"任意文本"变成"选择题选项",让你的代码更加精确和安全。

🎯 基础字面量类型:限定选项范围

// 1. 状态管理 - 精确的状态定义
type LightStatus = "on" | "off" | "dimmed";
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type Theme = "light" | "dark" | "auto";

// 灯光控制函数
function setLight(status: LightStatus) {
    console.log(`灯光状态设置为:${status}`);
    // 模拟硬件控制
    switch(status) {
        case "on":
            console.log("💡 灯光已开启");
            break;
        case "off":
            console.log("🌙 灯光已关闭");
            break;
        case "dimmed":
            console.log("🔅 灯光已调暗");
            break;
    }
}

// 使用示例
setLight("on");    
// "灯光状态设置为:on"
// "💡 灯光已开启"

setLight("dimmed"); 
// "灯光状态设置为:dimmed"
// "🔅 灯光已调暗"

// 错误示例:不在选项中
// setLight("flash"); // 错误!"flash"不在LightStatus类型中

// 2. HTTP请求处理
function makeRequest(method: HttpMethod, url: string) {
    console.log(`发送 ${method} 请求到:${url}`);
    // 模拟网络请求
    return { method, url, timestamp: Date.now() };
}

const getResult = makeRequest("GET", "/api/users");
console.log(getResult); 
// { method: "GET", url: "/api/users", timestamp: 1704067200000 }

const postResult = makeRequest("POST", "/api/users");
console.log(postResult.method); // "POST"

🏗️ 实战应用:Redux Action系统

// Redux Action类型定义
type ActionType = "ADD_TODO" | "REMOVE_TODO" | "EDIT_TODO" | "TOGGLE_TODO";

interface BaseAction {
    type: ActionType;
    timestamp: number;
}

interface AddTodoAction extends BaseAction {
    type: "ADD_TODO";
    payload: {
        id: string;
        text: string;
        completed: boolean;
    };
}

interface RemoveTodoAction extends BaseAction {
    type: "REMOVE_TODO";
    payload: {
        id: string;
    };
}

type TodoAction = AddTodoAction | RemoveTodoAction;

// Action创建器
function createAddTodoAction(text: string): AddTodoAction {
    const action = {
        type: "ADD_TODO" as const,
        payload: {
            id: `todo_${Date.now()}`,
            text,
            completed: false
        },
        timestamp: Date.now()
    };
    console.log(`创建添加Todo Action:`, action);
    return action;
}

function createRemoveTodoAction(id: string): RemoveTodoAction {
    const action = {
        type: "REMOVE_TODO" as const,
        payload: { id },
        timestamp: Date.now()
    };
    console.log(`创建删除Todo Action:`, action);
    return action;
}

// 使用示例
const addAction = createAddTodoAction("学习TypeScript高级类型");
// "创建添加Todo Action:" { type: "ADD_TODO", payload: { id: "todo_1704067200000", text: "学习TypeScript高级类型", completed: false }, timestamp: 1704067200000 }

const removeAction = createRemoveTodoAction("todo_123");
// "创建删除Todo Action:" { type: "REMOVE_TODO", payload: { id: "todo_123" }, timestamp: 1704067200000 }

console.log(addAction.type); // "ADD_TODO"
console.log(addAction.payload.text); // "学习TypeScript高级类型"

🎨 模板字面量类型(TypeScript 4.1+)

// 模板字面量类型 - 动态生成字符串类型
type EventName<T extends string> = `on${Capitalize<T>}`;
type ButtonEvent = EventName<"click" | "hover" | "focus">; // "onClick" | "onHover" | "onFocus"

// CSS属性生成
type CSSProperty = "margin" | "padding";
type CSSDirection = "top" | "right" | "bottom" | "left";
type CSSPropertyWithDirection = `${CSSProperty}-${CSSDirection}`;
// "margin-top" | "margin-right" | "margin-bottom" | "margin-left" | "padding-top" | "padding-right" | "padding-bottom" | "padding-left"

// 使用示例
function setCSSProperty(property: CSSPropertyWithDirection, value: string) {
    console.log(`设置CSS属性:${property} = ${value}`);
    return { [property]: value };
}

const marginTop = setCSSProperty("margin-top", "10px");
// "设置CSS属性:margin-top = 10px"
console.log(marginTop); // { "margin-top": "10px" }

const paddingLeft = setCSSProperty("padding-left", "20px");
// "设置CSS属性:padding-left = 20px"
console.log(paddingLeft); // { "padding-left": "20px" }

💡 智能提示加成:在VSCode中,当你输入字面量类型时,编辑器会自动提示所有可选值,大大提升开发效率!

7.3 元组类型——数组的"精确版本"

元组(Tuples)是数组的升级版——它精确规定了数组的长度和每个位置的类型,就像给数组穿上了"定制西装"。

📏 基础元组:固定长度和类型

// 1. 基础元组定义
type PersonData = [string, number]; // [姓名, 年龄]
type Coordinate = [number, number]; // [x, y]
type RGB = [number, number, number]; // [红, 绿, 蓝]

// 使用示例
const userData: PersonData = ["张三", 30];
const position: Coordinate = [100, 200];
const redColor: RGB = [255, 0, 0];

console.log(userData[0]); // "张三"
console.log(userData[1]); // 30
console.log(position); // [100, 200]
console.log(`RGB颜色:rgb(${redColor.join(", ")})`); // "RGB颜色:rgb(255, 0, 0)"

// 错误示例:类型或长度不匹配
// const wrongData: PersonData = ["李四", "三十"]; // 错误!第二个元素应为数字
// const wrongData2: PersonData = ["王五"]; // 错误!缺少第二个元素

// 2. 解构赋值的类型安全
const [name, age] = userData;
console.log(`姓名:${name},年龄:${age}`); // "姓名:张三,年龄:30"
console.log(typeof name); // "string"
console.log(typeof age); // "number"

const [x, y] = position;
console.log(`坐标:(${x}, ${y})`); // "坐标:(100, 200)"

const [r, g, b] = redColor;
console.log(`红色分量:${r}`); // "红色分量:255"

🔧 可选元素与剩余元素

// 1. 带可选元素的元组
type RGBA = [number, number, number, number?]; // 最后的alpha通道可选

const white: RGBA = [255, 255, 255];
const semiTransparent: RGBA = [0, 0, 0, 0.5];

console.log(white); // [255, 255, 255]
console.log(semiTransparent); // [0, 0, 0, 0.5]
console.log(white.length); // 3
console.log(semiTransparent.length); // 4

// 颜色处理函数
function formatColor(color: RGBA): string {
    const [r, g, b, a] = color;
    if (a !== undefined) {
        const result = `rgba(${r}, ${g}, ${b}, ${a})`;
        console.log(`格式化RGBA颜色:`, result);
        return result;
    } else {
        const result = `rgb(${r}, ${g}, ${b})`;
        console.log(`格式化RGB颜色:`, result);
        return result;
    }
}

console.log(formatColor(white)); 
// "格式化RGB颜色:" "rgb(255, 255, 255)"
// 返回:"rgb(255, 255, 255)"

console.log(formatColor(semiTransparent)); 
// "格式化RGBA颜色:" "rgba(0, 0, 0, 0.5)"
// 返回:"rgba(0, 0, 0, 0.5)"

// 2. 剩余元素元组 (TypeScript 4.0+)
type Student = [string, ...number[]]; // 姓名 + 任意数量的分数
type DatabaseRow = [number, string, ...any[]]; // ID + 名称 + 其他字段

const tom: Student = ["Tom", 95, 88, 92, 87];
const lucy: Student = ["Lucy", 100];
const bob: Student = ["Bob", 85, 90, 88, 92, 94, 89];

console.log(tom[0]); // "Tom"
console.log(tom.slice(1)); // [95, 88, 92, 87]

// 计算平均分函数
function calculateAverage(student: Student): number {
    const [name, ...scores] = student;
    const average = scores.reduce((sum, score) => sum + score, 0) / scores.length;
    console.log(`${name}的平均分:${average.toFixed(2)}`);
    return average;
}

console.log(calculateAverage(tom)); 
// "Tom的平均分:90.50"
// 返回:90.5

console.log(calculateAverage(lucy)); 
// "Lucy的平均分:100.00"
// 返回:100

🎯 实战应用:React Hooks模拟

// React useState Hook的类型定义
type StateHook<T> = [T, (newValue: T) => void];
type EffectHook = [() => void, any[]];

// 模拟useState实现
function useState<T>(initialValue: T): StateHook<T> {
    let value = initialValue;
    
    const setValue = (newValue: T) => {
        const oldValue = value;
        value = newValue;
        console.log(`状态更新:${oldValue} -> ${newValue}`);
    };
    
    const getter = () => {
        console.log(`获取当前状态:${value}`);
        return value;
    };
    
    return [getter() as T, setValue];
}

// 使用示例
const [count, setCount] = useState(0);
// "获取当前状态:0"

console.log(count); // 0

setCount(5);
// "状态更新:0 -> 5"

const [name, setName] = useState("Alice");
// "获取当前状态:Alice"

console.log(name); // "Alice"

setName("Bob");
// "状态更新:Alice -> Bob"

// 数据库查询结果元组
type QueryResult = [boolean, any[], string?]; // [成功状态, 数据, 错误信息]

function mockQuery(sql: string): QueryResult {
    console.log(`执行SQL:${sql}`);
    
    if (sql.includes("SELECT")) {
        const mockData = [{ id: 1, name: "用户1" }, { id: 2, name: "用户2" }];
        console.log(`查询成功,返回 ${mockData.length} 条记录`);
        return [true, mockData];
    } else {
        console.log(`查询失败:不支持的SQL语句`);
        return [false, [], "不支持的SQL语句"];
    }
}

const [success, data, error] = mockQuery("SELECT * FROM users");
// "执行SQL:SELECT * FROM users"
// "查询成功,返回 2 条记录"

console.log(success); // true
console.log(data.length); // 2
console.log(error); // undefined

const [success2, data2, error2] = mockQuery("DROP TABLE users");
// "执行SQL:DROP TABLE users"
// "查询失败:不支持的SQL语句"

console.log(success2); // false
console.log(data2.length); // 0
console.log(error2); // "不支持的SQL语句"

7.4 枚举类型——常量的"豪华套装"

枚举(Enums)是为数值常量提供语义化名称的最佳工具,就像给一堆数字穿上了"有意义的外衣"。

🔢 数字枚举:自动递增的常量

// 1. 基础数字枚举
enum Direction {
    Up = 1,    // 指定起始值
    Down,      // 自动递增为2
    Left,      // 自动递增为3
    Right      // 自动递增为4
}

// 使用示例
function move(direction: Direction) {
    console.log(`移动方向:${Direction[direction]} (值: ${direction})`);
    
    switch(direction) {
        case Direction.Up:
            console.log("⬆️ 向上移动");
            break;
        case Direction.Down:
            console.log("⬇️ 向下移动");
            break;
        case Direction.Left:
            console.log("⬅️ 向左移动");
            break;
        case Direction.Right:
            console.log("➡️ 向右移动");
            break;
    }
}

move(Direction.Up);
// "移动方向:Up (值: 1)"
// "⬆️ 向上移动"

move(Direction.Right);
// "移动方向:Right (值: 4)"
// "➡️ 向右移动"

// 反向映射
console.log(Direction[1]); // "Up"
console.log(Direction[4]); // "Right"
console.log(Direction.Up); // 1
console.log(Direction.Right); // 4

// 2. 状态码枚举
enum HttpStatus {
    OK = 200,
    NotFound = 404,
    InternalServerError = 500
}

function handleResponse(status: HttpStatus) {
    console.log(`处理HTTP状态码:${status}`);
    
    if (status === HttpStatus.OK) {
        console.log("✅ 请求成功");
    } else if (status === HttpStatus.NotFound) {
        console.log("❌ 资源未找到");
    } else if (status === HttpStatus.InternalServerError) {
        console.log("💥 服务器内部错误");
    }
}

handleResponse(HttpStatus.OK);
// "处理HTTP状态码:200"
// "✅ 请求成功"

handleResponse(HttpStatus.NotFound);
// "处理HTTP状态码:404"
// "❌ 资源未找到"

📝 字符串枚举:语义化的常量

// 1. 媒体类型枚举
enum MediaType {
    JSON = "application/json",
    XML = "application/xml",
    TEXT = "text/plain",
    HTML = "text/html"
}

// HTTP请求处理
function setContentType(type: MediaType) {
    console.log(`设置Content-Type: ${type}`);
    // 模拟设置HTTP头部
    return {
        "Content-Type": type,
        "X-Timestamp": new Date().toISOString()
    };
}

const jsonHeaders = setContentType(MediaType.JSON);
// "设置Content-Type: application/json"
console.log(jsonHeaders);
// { "Content-Type": "application/json", "X-Timestamp": "2024-01-01T12:00:00.000Z" }

const xmlHeaders = setContentType(MediaType.XML);
// "设置Content-Type: application/xml"
console.log(xmlHeaders["Content-Type"]); // "application/xml"

// 2. 日志级别枚举
enum LogLevel {
    ERROR = "error",
    WARN = "warn",
    INFO = "info",
    DEBUG = "debug"
}

function log(level: LogLevel, message: string) {
    const timestamp = new Date().toISOString();
    const logEntry = `[${timestamp}] ${level.toUpperCase()}: ${message}`;
    console.log(logEntry);
    
    // 根据级别选择不同的输出方式
    switch(level) {
        case LogLevel.ERROR:
            console.error("🔴 错误日志");
            break;
        case LogLevel.WARN:
            console.warn("🟡 警告日志");
            break;
        case LogLevel.INFO:
            console.info("🔵 信息日志");
            break;
        case LogLevel.DEBUG:
            console.debug("⚪ 调试日志");
            break;
    }
}

log(LogLevel.INFO, "系统启动成功");
// "[2024-01-01T12:00:00.000Z] INFO: 系统启动成功"
// "🔵 信息日志"

log(LogLevel.ERROR, "数据库连接失败");
// "[2024-01-01T12:00:00.000Z] ERROR: 数据库连接失败"
// "🔴 错误日志"

⚡ 常量枚举:编译时优化

// 常量枚举(编译时完全删除)
const enum Color {
    Red,
    Green,
    Blue
}

// 使用常量枚举
function paintPixel(color: Color) {
    console.log(`绘制像素,颜色值:${color}`);
    
    // 编译后这里会直接使用数字值
    if (color === Color.Red) {
        console.log("🔴 绘制红色像素");
    } else if (color === Color.Green) {
        console.log("🟢 绘制绿色像素");
    } else if (color === Color.Blue) {
        console.log("🔵 绘制蓝色像素");
    }
}

paintPixel(Color.Red);
// "绘制像素,颜色值:0"
// "🔴 绘制红色像素"

paintPixel(Color.Blue);
// "绘制像素,颜色值:2"
// "🔵 绘制蓝色像素"

// 编译后的JavaScript代码中,Color.Red会被替换为0,Color.Blue会被替换为2
console.log(Color.Red); // 编译后:console.log(0);

🆚 现代替代方案:联合类型 vs 枚举

// 方案1:传统枚举
enum TraditionalLogLevel {
    Error,
    Warn,
    Info,
    Debug
}

// 方案2:现代联合类型
const ModernLogLevel = {
    Error: 0,
    Warn: 1,
    Info: 2,
    Debug: 3
} as const;

type ModernLogLevel = typeof ModernLogLevel[keyof typeof ModernLogLevel]; // 0 | 1 | 2 | 3

// 比较两种方案
function compareApproaches() {
    // 传统枚举使用
    const traditionalLevel = TraditionalLogLevel.Error;
    console.log(`传统枚举值:${traditionalLevel}`); // "传统枚举值:0"
    console.log(`传统枚举名称:${TraditionalLogLevel[traditionalLevel]}`); // "传统枚举名称:Error"
    
    // 现代联合类型使用
    const modernLevel = ModernLogLevel.Error;
    console.log(`现代联合类型值:${modernLevel}`); // "现代联合类型值:0"
    
    // 运行时大小比较
    console.log(`传统枚举在运行时存在:${typeof TraditionalLogLevel}`); // "传统枚举在运行时存在:object"
    console.log(`现代联合类型在运行时存在:${typeof ModernLogLevel}`); // "现代联合类型在运行时存在:object"
}

compareApproaches();

⚖️ 选择建议

  • 使用枚举:需要反向映射、与外部API交互、团队习惯枚举
  • 使用联合类型:追求最小运行时开销、现代TypeScript项目、函数式编程风格

7.5 类型保护与类型守卫——类型世界的"安检门"

类型保护(Type Guards)是TypeScript的类型收窄机制,让你在特定代码块内获得更精确的类型信息,就像给代码装上了"智能识别系统"。

🔍 内置类型守卫:基础类型检查

// 1. typeof 守卫 - 处理基本类型
function processValue(value: string | number | boolean) {
    console.log(`处理值:${value},类型:${typeof value}`);
    
    if (typeof value === "string") {
        // 在这个块中,value被收窄为string类型
        const result = value.toUpperCase();
        console.log(`字符串处理结果:${result}`);
        return result;
    } else if (typeof value === "number") {
        // 在这个块中,value被收窄为number类型
        const result = value.toFixed(2);
        console.log(`数字处理结果:${result}`);
        return result;
    } else {
        // 在这个块中,value被收窄为boolean类型
        const result = value ? "真" : "假";
        console.log(`布尔值处理结果:${result}`);
        return result;
    }
}

console.log(processValue("hello"));
// "处理值:hello,类型:string"
// "字符串处理结果:HELLO"
// 返回:"HELLO"

console.log(processValue(3.14159));
// "处理值:3.14159,类型:number"
// "数字处理结果:3.14"
// 返回:"3.14"

console.log(processValue(true));
// "处理值:true,类型:boolean"
// "布尔值处理结果:真"
// 返回:"真"

// 2. instanceof 守卫 - 处理类实例
class Bird {
    fly() {
        console.log("🐦 鸟儿在飞翔");
    }
    
    makeSound() {
        console.log("🎵 鸟儿在歌唱");
    }
}

class Fish {
    swim() {
        console.log("🐟 鱼儿在游泳");
    }
    
    makeSound() {
        console.log("🫧 鱼儿在吐泡泡");
    }
}

class Dog {
    run() {
        console.log("🐕 狗狗在奔跑");
    }
    
    makeSound() {
        console.log("🐕 汪汪汪!");
    }
}

function handleAnimal(animal: Bird | Fish | Dog) {
    console.log(`处理动物:${animal.constructor.name}`);
    
    // 所有动物都有makeSound方法
    animal.makeSound();
    
    if (animal instanceof Bird) {
        // 在这个块中,animal被收窄为Bird类型
        animal.fly();
    } else if (animal instanceof Fish) {
        // 在这个块中,animal被收窄为Fish类型
        animal.swim();
    } else {
        // 在这个块中,animal被收窄为Dog类型
        animal.run();
    }
}

const bird = new Bird();
const fish = new Fish();
const dog = new Dog();

handleAnimal(bird);
// "处理动物:Bird"
// "🎵 鸟儿在歌唱"
// "🐦 鸟儿在飞翔"

handleAnimal(fish);
// "处理动物:Fish"
// "🫧 鱼儿在吐泡泡"
// "🐟 鱼儿在游泳"

handleAnimal(dog);
// "处理动物:Dog"
// "🐕 汪汪汪!"
// "🐕 狗狗在奔跑"

🔑 属性检查守卫:in 操作符

// 接口定义
interface Circle {
    kind: "circle";
    radius: number;
}

interface Square {
    kind: "square";
    sideLength: number;
}

interface Triangle {
    kind: "triangle";
    base: number;
    height: number;
}

type Shape = Circle | Square | Triangle;

// 使用in守卫检查属性存在性
function calculateArea(shape: Shape): number {
    console.log(`计算图形面积,类型:${shape.kind}`);
    
    if ("radius" in shape) {
        // 在这个块中,shape被收窄为Circle类型
        const area = Math.PI * shape.radius ** 2;
        console.log(`圆形面积:π × ${shape.radius}² = ${area.toFixed(2)}`);
        return area;
    } else if ("sideLength" in shape) {
        // 在这个块中,shape被收窄为Square类型
        const area = shape.sideLength ** 2;
        console.log(`正方形面积:${shape.sideLength}² = ${area}`);
        return area;
    } else {
        // 在这个块中,shape被收窄为Triangle类型
        const area = (shape.base * shape.height) / 2;
        console.log(`三角形面积:(${shape.base} × ${shape.height}) ÷ 2 = ${area}`);
        return area;
    }
}

// 测试不同图形
const circle: Circle = { kind: "circle", radius: 5 };
const square: Square = { kind: "square", sideLength: 4 };
const triangle: Triangle = { kind: "triangle", base: 6, height: 8 };

console.log(calculateArea(circle));
// "计算图形面积,类型:circle"
// "圆形面积:π × 5² = 78.54"
// 返回:78.53981633974483

console.log(calculateArea(square));
// "计算图形面积,类型:square"
// "正方形面积:4² = 16"
// 返回:16

console.log(calculateArea(triangle));
// "计算图形面积,类型:triangle"
// "三角形面积:(6 × 8) ÷ 2 = 24"
// 返回:24

🛡️ 自定义类型守卫:类型谓词

// 自定义类型守卫函数
function isCircle(shape: Shape): shape is Circle {
    const result = shape.kind === "circle";
    console.log(`检查是否为圆形:${result}`);
    return result;
}

function isSquare(shape: Shape): shape is Square {
    const result = shape.kind === "square";
    console.log(`检查是否为正方形:${result}`);
    return result;
}

function isTriangle(shape: Shape): shape is Triangle {
    const result = shape.kind === "triangle";
    console.log(`检查是否为三角形:${result}`);
    return result;
}

// 使用自定义类型守卫
function describeShape(shape: Shape) {
    console.log(`描述图形:${shape.kind}`);
    
    if (isCircle(shape)) {
        console.log(`这是一个半径为 ${shape.radius} 的圆形`);
        console.log(`周长:${(2 * Math.PI * shape.radius).toFixed(2)}`);
    } else if (isSquare(shape)) {
        console.log(`这是一个边长为 ${shape.sideLength} 的正方形`);
        console.log(`周长:${4 * shape.sideLength}`);
    } else if (isTriangle(shape)) {
        console.log(`这是一个底边 ${shape.base},高 ${shape.height} 的三角形`);
        // 计算斜边(假设是直角三角形)
        const hypotenuse = Math.sqrt(shape.base ** 2 + shape.height ** 2);
        console.log(`斜边长度:${hypotenuse.toFixed(2)}`);
    }
}

describeShape(circle);
// "描述图形:circle"
// "检查是否为圆形:true"
// "这是一个半径为 5 的圆形"
// "周长:31.42"

describeShape(square);
// "描述图形:square"
// "检查是否为圆形:false"
// "检查是否为正方形:true"
// "这是一个边长为 4 的正方形"
// "周长:16"

// 复杂的类型守卫:检查对象结构
interface User {
    id: number;
    name: string;
    email: string;
}

interface Admin extends User {
    permissions: string[];
    lastLogin: Date;
}

function isAdmin(user: User | Admin): user is Admin {
    const hasPermissions = 'permissions' in user;
    const hasLastLogin = 'lastLogin' in user;
    const result = hasPermissions && hasLastLogin;
    console.log(`检查是否为管理员:${result}`);
    return result;
}

function handleUser(user: User | Admin) {
    console.log(`处理用户:${user.name} (ID: ${user.id})`);
    
    if (isAdmin(user)) {
        console.log(`管理员权限:${user.permissions.join(", ")}`);
        console.log(`最后登录:${user.lastLogin.toISOString()}`);
    } else {
        console.log(`普通用户,邮箱:${user.email}`);
    }
}

const regularUser: User = {
    id: 1,
    name: "张三",
    email: "zhangsan@example.com"
};

const adminUser: Admin = {
    id: 2,
    name: "李四",
    email: "lisi@example.com",
    permissions: ["read", "write", "delete"],
    lastLogin: new Date()
};

handleUser(regularUser);
// "处理用户:张三 (ID: 1)"
// "检查是否为管理员:false"
// "普通用户,邮箱:zhangsan@example.com"

handleUser(adminUser);
// "处理用户:李四 (ID: 2)"
// "检查是否为管理员:true"
// "管理员权限:read, write, delete"
// "最后登录:2024-01-01T12:00:00.000Z"

7.6 映射类型——类型转换的"流水线工厂"

映射类型(Mapped Types)让你能像操作数据一样批量转换类型,就像拥有了一条专门生产类型的"智能流水线"。

🏭 内置映射类型:TypeScript的"标准工具"

// 基础用户接口
interface User {
    id: number;
    name: string;
    email: string;
    age: number;
    isActive: boolean;
}

// 1. Partial<T> - 将所有属性变为可选
type PartialUser = Partial<User>;
/* 等价于:
{
    id?: number;
    name?: string;
    email?: string;
    age?: number;
    isActive?: boolean;
}
*/

// 使用Partial类型
function updateUser(id: number, updates: PartialUser) {
    console.log(`更新用户 ${id},更新字段:`, Object.keys(updates));
    
    // 模拟数据库更新
    const updatedFields: string[] = [];
    if (updates.name !== undefined) {
        console.log(`更新姓名:${updates.name}`);
        updatedFields.push("name");
    }
    if (updates.email !== undefined) {
        console.log(`更新邮箱:${updates.email}`);
        updatedFields.push("email");
    }
    if (updates.age !== undefined) {
        console.log(`更新年龄:${updates.age}`);
        updatedFields.push("age");
    }
    if (updates.isActive !== undefined) {
        console.log(`更新状态:${updates.isActive ? "激活" : "禁用"}`);
        updatedFields.push("isActive");
    }
    
    return { success: true, updatedFields };
}

const result1 = updateUser(1, { name: "新姓名", age: 25 });
// "更新用户 1,更新字段:" ["name", "age"]
// "更新姓名:新姓名"
// "更新年龄:25"
console.log(result1); // { success: true, updatedFields: ["name", "age"] }

const result2 = updateUser(2, { isActive: false });
// "更新用户 2,更新字段:" ["isActive"]
// "更新状态:禁用"
console.log(result2); // { success: true, updatedFields: ["isActive"] }

// 2. Required<T> - 将所有属性变为必需
interface OptionalConfig {
    host?: string;
    port?: number;
    ssl?: boolean;
    timeout?: number;
}

type RequiredConfig = Required<OptionalConfig>;
/* 等价于:
{
    host: string;
    port: number;
    ssl: boolean;
    timeout: number;
}
*/

function createConnection(config: RequiredConfig) {
    console.log(`创建连接:${config.ssl ? 'https' : 'http'}://${config.host}:${config.port}`);
    console.log(`超时设置:${config.timeout}ms`);
    
    return {
        url: `${config.ssl ? 'https' : 'http'}://${config.host}:${config.port}`,
        timeout: config.timeout,
        connected: true
    };
}

const connection = createConnection({
    host: "api.example.com",
    port: 443,
    ssl: true,
    timeout: 5000
});
// "创建连接:https://api.example.com:443"
// "超时设置:5000ms"
console.log(connection.connected); // true

// 3. Readonly<T> - 将所有属性变为只读
type ReadonlyUser = Readonly<User>;

function createReadonlyUser(userData: User): ReadonlyUser {
    console.log(`创建只读用户:${userData.name}`);
    const readonlyUser = Object.freeze({ ...userData });
    console.log(`用户已设为只读模式`);
    return readonlyUser;
}

const originalUser: User = {
    id: 1,
    name: "张三",
    email: "zhangsan@example.com",
    age: 30,
    isActive: true
};

const readonlyUser = createReadonlyUser(originalUser);
// "创建只读用户:张三"
// "用户已设为只读模式"

console.log(readonlyUser.name); // "张三"
// readonlyUser.name = "李四"; // 错误!无法修改只读属性

🎨 自定义映射类型:打造专属工具

// 1. 生成Getter方法类型
type Getters<T> = {
    [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type UserGetters = Getters<User>;
/* 等价于:
{
    getId: () => number;
    getName: () => string;
    getEmail: () => string;
    getAge: () => number;
    getIsActive: () => boolean;
}
*/

// 实现Getter类
class UserWithGetters implements UserGetters {
    constructor(private user: User) {
        console.log(`创建带Getter的用户:${user.name}`);
    }
    
    getId(): number {
        console.log(`获取ID:${this.user.id}`);
        return this.user.id;
    }
    
    getName(): string {
        console.log(`获取姓名:${this.user.name}`);
        return this.user.name;
    }
    
    getEmail(): string {
        console.log(`获取邮箱:${this.user.email}`);
        return this.user.email;
    }
    
    getAge(): number {
        console.log(`获取年龄:${this.user.age}`);
        return this.user.age;
    }
    
    getIsActive(): boolean {
        console.log(`获取状态:${this.user.isActive ? "激活" : "禁用"}`);
        return this.user.isActive;
    }
}

const userWithGetters = new UserWithGetters(originalUser);
// "创建带Getter的用户:张三"

console.log(userWithGetters.getName()); 
// "获取姓名:张三"
// 返回:"张三"

console.log(userWithGetters.getAge()); 
// "获取年龄:30"
// 返回:30

// 2. 过滤特定类型的属性
type StringProperties<T> = {
    [K in keyof T as T[K] extends string ? K : never]: T[K];
};

type UserStringProps = StringProperties<User>;
/* 等价于:
{
    name: string;
    email: string;
}
*/

function extractStringProperties(user: User): UserStringProps {
    console.log(`提取用户的字符串属性`);
    const stringProps = {
        name: user.name,
        email: user.email
    };
    console.log(`提取的属性:`, stringProps);
    return stringProps;
}

const stringProps = extractStringProperties(originalUser);
// "提取用户的字符串属性"
// "提取的属性:" { name: "张三", email: "zhangsan@example.com" }
console.log(stringProps.name); // "张三"
console.log(stringProps.email); // "zhangsan@example.com"

// 3. 创建可空版本的类型
type Nullable<T> = {
    [K in keyof T]: T[K] | null;
};

type NullableUser = Nullable<User>;
/* 等价于:
{
    id: number | null;
    name: string | null;
    email: string | null;
    age: number | null;
    isActive: boolean | null;
}
*/

function createNullableUser(partial: Partial<User>): NullableUser {
    console.log(`创建可空用户,输入字段:`, Object.keys(partial));
    
    const nullableUser: NullableUser = {
        id: partial.id ?? null,
        name: partial.name ?? null,
        email: partial.email ?? null,
        age: partial.age ?? null,
        isActive: partial.isActive ?? null
    };
    
    console.log(`创建的可空用户:`, nullableUser);
    return nullableUser;
}

const nullableUser = createNullableUser({ name: "测试用户", age: 25 });
// "创建可空用户,输入字段:" ["name", "age"]
// "创建的可空用户:" { id: null, name: "测试用户", email: null, age: 25, isActive: null }

console.log(nullableUser.name); // "测试用户"
console.log(nullableUser.id); // null

🔧 高级映射类型:复杂转换

// 1. 深度只读类型
type DeepReadonly<T> = {
    readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

interface NestedUser {
    id: number;
    profile: {
        name: string;
        contact: {
            email: string;
            phone: string;
        };
    };
    preferences: {
        theme: string;
        language: string;
    };
}

type DeepReadonlyUser = DeepReadonly<NestedUser>;

function createDeepReadonlyUser(user: NestedUser): DeepReadonlyUser {
    console.log(`创建深度只读用户:${user.profile.name}`);
    
    // 深度冻结对象
    function deepFreeze<T>(obj: T): T {
        Object.getOwnPropertyNames(obj).forEach(prop => {
            const value = (obj as any)[prop];
            if (value && typeof value === 'object') {
                deepFreeze(value);
            }
        });
        return Object.freeze(obj);
    }
    
    const frozenUser = deepFreeze({ ...user });
    console.log(`用户已深度冻结`);
    return frozenUser as DeepReadonlyUser;
}

const nestedUser: NestedUser = {
    id: 1,
    profile: {
        name: "深度用户",
        contact: {
            email: "deep@example.com",
            phone: "123-456-7890"
        }
    },
    preferences: {
        theme: "dark",
        language: "zh-CN"
    }
};

const deepReadonlyUser = createDeepReadonlyUser(nestedUser);
// "创建深度只读用户:深度用户"
// "用户已深度冻结"

console.log(deepReadonlyUser.profile.name); // "深度用户"
console.log(deepReadonlyUser.profile.contact.email); // "deep@example.com"
// deepReadonlyUser.profile.name = "新名字"; // 错误!深度只读
// deepReadonlyUser.profile.contact.email = "new@example.com"; // 错误!深度只读

// 2. 条件映射类型
type ApiEndpoints<T> = {
    [K in keyof T as T[K] extends Function ? `api${Capitalize<string & K>}` : never]: T[K];
};

interface UserService {
    create: (user: User) => Promise<User>;
    update: (id: number, user: Partial<User>) => Promise<User>;
    delete: (id: number) => Promise<void>;
    name: string; // 这个不是函数,会被过滤掉
    version: number; // 这个也不是函数,会被过滤掉
}

type UserApiEndpoints = ApiEndpoints<UserService>;
/* 等价于:
{
    apiCreate: (user: User) => Promise<User>;
    apiUpdate: (id: number, user: Partial<User>) => Promise<User>;
    apiDelete: (id: number) => Promise<void>;
}
*/

class UserApi implements UserApiEndpoints {
    apiCreate(user: User): Promise<User> {
        console.log(`API: 创建用户 ${user.name}`);
        return Promise.resolve({ ...user, id: Date.now() });
    }
    
    apiUpdate(id: number, user: Partial<User>): Promise<User> {
        console.log(`API: 更新用户 ${id},字段:`, Object.keys(user));
        return Promise.resolve({ id, ...user } as User);
    }
    
    apiDelete(id: number): Promise<void> {
        console.log(`API: 删除用户 ${id}`);
        return Promise.resolve();
    }
}

const userApi = new UserApi();

// 异步测试
async function testUserApi() {
    const newUser = await userApi.apiCreate({
        id: 0, // 会被覆盖
        name: "API用户",
        email: "api@example.com",
        age: 28,
        isActive: true
    });
    // "API: 创建用户 API用户"
    console.log(`创建的用户ID:${newUser.id}`);
    
    await userApi.apiUpdate(newUser.id, { age: 29 });
    // "API: 更新用户 1704067200000,字段:" ["age"]
    
    await userApi.apiDelete(newUser.id);
    // "API: 删除用户 1704067200000"
}

testUserApi();

7.7 条件类型——类型系统的"智能决策器"

条件类型(Conditional Types)让类型也能拥有逻辑判断能力,就像给类型系统装上了"人工智能"。

🧠 基础条件类型:类型的三目运算符

// 基础语法:T extends U ? X : Y
type IsString<T> = T extends string ? true : false;
type IsNumber<T> = T extends number ? true : false;
type IsArray<T> = T extends any[] ? true : false;

// 测试类型判断
type Test1 = IsString<"hello">; // true
type Test2 = IsString<42>;      // false
type Test3 = IsNumber<100>;     // true
type Test4 = IsArray<string[]>; // true

// 实际使用示例
function processData<T>(data: T): T extends string ? string : T extends number ? number : unknown {
    console.log(`处理数据:${data},类型:${typeof data}`);
    
    if (typeof data === "string") {
        const result = data.toUpperCase();
        console.log(`字符串处理结果:${result}`);
        return result as any;
    } else if (typeof data === "number") {
        const result = data * 2;
        console.log(`数字处理结果:${result}`);
        return result as any;
    } else {
        console.log(`未知类型,原样返回`);
        return data as any;
    }
}

const stringResult = processData("hello");
// "处理数据:hello,类型:string"
// "字符串处理结果:HELLO"
console.log(stringResult); // "HELLO"

const numberResult = processData(42);
// "处理数据:42,类型:number"
// "数字处理结果:84"
console.log(numberResult); // 84

const unknownResult = processData(true);
// "处理数据:true,类型:boolean"
// "未知类型,原样返回"
console.log(unknownResult); // true

🔄 分布式条件类型:批量处理联合类型

// 分布式条件类型 - 自动分发到联合类型的每个成员
type ToArray<T> = T extends any ? T[] : never;

// 当T是联合类型时,条件类型会分布到每个成员
type StringOrNumberArray = ToArray<string | number>; // string[] | number[]
type BooleanArray = ToArray<boolean>; // boolean[]

// 实际应用:过滤联合类型
type NonNullable<T> = T extends null | undefined ? never : T;

type MaybeString = string | null | undefined;
type DefinitelyString = NonNullable<MaybeString>; // string

// 使用示例
function filterNullable<T>(value: T): NonNullable<T> | null {
    if (value === null || value === undefined) {
        console.log(`过滤掉空值:${value}`);
        return null;
    }
    console.log(`保留有效值:${value}`);
    return value as NonNullable<T>;
}

const result1 = filterNullable("hello");
// "保留有效值:hello"
console.log(result1); // "hello"

const result2 = filterNullable(null);
// "过滤掉空值:null"
console.log(result2); // null

const result3 = filterNullable(undefined);
// "过滤掉空值:undefined"
console.log(result3); // null

🔍 类型推断:infer 关键字的魔法

// 1. 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

type StringFunction = () => string;
type NumberFunction = (x: number) => number;
type VoidFunction = () => void;

type StringReturn = ReturnType<StringFunction>; // string
type NumberReturn = ReturnType<NumberFunction>; // number
type VoidReturn = ReturnType<VoidFunction>; // void

// 实际使用
function createTypedFunction<F extends (...args: any[]) => any>(
    fn: F
): (...args: Parameters<F>) => ReturnType<F> {
    return (...args) => {
        console.log(`调用函数,参数:`, args);
        const result = fn(...args);
        console.log(`函数返回:`, result);
        return result;
    };
}

const add = (a: number, b: number) => a + b;
const typedAdd = createTypedFunction(add);

const sum = typedAdd(5, 3);
// "调用函数,参数:" [5, 3]
// "函数返回:" 8
console.log(sum); // 8

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

type StringArrayElement = ArrayElement<string[]>; // string
type NumberArrayElement = ArrayElement<number[]>; // number
type MixedArrayElement = ArrayElement<(string | number)[]>; // string | number

// 实际应用:数组处理函数
function processArray<T extends any[]>(
    arr: T,
    processor: (item: ArrayElement<T>) => ArrayElement<T>
): T {
    console.log(`处理数组,长度:${arr.length}`);
    const result = arr.map(item => {
        const processed = processor(item);
        console.log(`处理项目:${item} -> ${processed}`);
        return processed;
    }) as T;
    console.log(`处理完成`);
    return result;
}

const numbers = [1, 2, 3, 4, 5];
const doubled = processArray(numbers, x => x * 2);
// "处理数组,长度:5"
// "处理项目:1 -> 2"
// "处理项目:2 -> 4"
// "处理项目:3 -> 6"
// "处理项目:4 -> 8"
// "处理项目:5 -> 10"
// "处理完成"
console.log(doubled); // [2, 4, 6, 8, 10]

const strings = ["hello", "world", "typescript"];
const uppercased = processArray(strings, s => s.toUpperCase());
// "处理数组,长度:3"
// "处理项目:hello -> HELLO"
// "处理项目:world -> WORLD"
// "处理项目:typescript -> TYPESCRIPT"
// "处理完成"
console.log(uppercased); // ["HELLO", "WORLD", "TYPESCRIPT"]

// 3. 提取Promise的值类型
type Awaited<T> = T extends Promise<infer U> ? U : T;

type StringPromise = Promise<string>;
type NumberPromise = Promise<number>;
type NestedPromise = Promise<Promise<boolean>>;

type StringValue = Awaited<StringPromise>; // string
type NumberValue = Awaited<NumberPromise>; // number
type BooleanValue = Awaited<NestedPromise>; // Promise<boolean> (只解包一层)

// 深度解包Promise
type DeepAwaited<T> = T extends Promise<infer U> ? DeepAwaited<U> : T;

type DeepBooleanValue = DeepAwaited<NestedPromise>; // boolean

// 实际应用:异步函数包装器
async function wrapAsync<T extends Promise<any>>(
    promise: T
): Promise<{ success: boolean; data: Awaited<T> | null; error: string | null }> {
    console.log(`包装异步操作`);
    try {
        const data = await promise;
        console.log(`异步操作成功:`, data);
        return { success: true, data, error: null };
    } catch (error) {
        console.log(`异步操作失败:`, error);
        return { success: false, data: null, error: String(error) };
    }
}

// 测试异步包装器
async function testAsyncWrapper() {
    const successPromise = Promise.resolve("成功数据");
    const failPromise = Promise.reject("失败原因");
    
    const result1 = await wrapAsync(successPromise);
    // "包装异步操作"
    // "异步操作成功:" "成功数据"
    console.log(result1); // { success: true, data: "成功数据", error: null }
    
    const result2 = await wrapAsync(failPromise);
    // "包装异步操作"
    // "异步操作失败:" "失败原因"
    console.log(result2); // { success: false, data: null, error: "失败原因" }
}

testAsyncWrapper();

🎯 实战应用:智能表单验证系统

// 字段类型定义
type FieldType = "text" | "number" | "email" | "date" | "boolean";

// 基础字段配置
interface BaseFieldConfig {
    name: string;
    type: FieldType;
    required?: boolean;
    label?: string;
}

// 根据字段类型确定值类型的条件类型
type FieldValue<T extends BaseFieldConfig> = 
    T extends { type: "number" } ? number :
    T extends { type: "date" } ? Date :
    T extends { type: "boolean" } ? boolean :
    string; // 默认为string (text, email)

// 根据required属性确定是否可选
type FieldValueWithRequired<T extends BaseFieldConfig> = 
    T extends { required: true } 
        ? FieldValue<T> 
        : FieldValue<T> | undefined;

// 表单值类型生成器
type FormValues<Fields extends readonly BaseFieldConfig[]> = {
    [K in Fields[number]["name"]]: FieldValueWithRequired<
        Extract<Fields[number], { name: K }>
    >;
};

// 验证规则类型
type ValidationRule<T> = {
    validate: (value: T) => boolean;
    message: string;
};

type FieldValidation<T extends BaseFieldConfig> = {
    [K in T["name"]]: ValidationRule<FieldValueWithRequired<T>>[];
};

// 表单配置示例
const userFormConfig = [
    { name: "username", type: "text", required: true, label: "用户名" },
    { name: "email", type: "email", required: true, label: "邮箱" },
    { name: "age", type: "number", required: false, label: "年龄" },
    { name: "birthdate", type: "date", required: false, label: "生日" },
    { name: "isActive", type: "boolean", required: true, label: "是否激活" }
] as const;

type UserFormValues = FormValues<typeof userFormConfig>;
/* 等价于:
{
    username: string;
    email: string;
    age: number | undefined;
    birthdate: Date | undefined;
    isActive: boolean;
}
*/

// 验证器实现
class FormValidator<T extends readonly BaseFieldConfig[]> {
    constructor(private config: T) {
        console.log(`创建表单验证器,字段数量:${config.length}`);
    }
    
    validate(values: FormValues<T>): { isValid: boolean; errors: string[] } {
        console.log(`开始验证表单`);
        const errors: string[] = [];
        
        for (const field of this.config) {
            const value = (values as any)[field.name];
            console.log(`验证字段 ${field.name}${value}`);
            
            // 检查必填字段
            if (field.required && (value === undefined || value === null || value === "")) {
                const error = `${field.label || field.name} 是必填字段`;
                console.log(`验证失败:${error}`);
                errors.push(error);
                continue;
            }
            
            // 类型特定验证
            if (value !== undefined && value !== null && value !== "") {
                switch (field.type) {
                    case "email":
                        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
                        if (!emailRegex.test(String(value))) {
                            const error = `${field.label || field.name} 格式不正确`;
                            console.log(`验证失败:${error}`);
                            errors.push(error);
                        }
                        break;
                    case "number":
                        if (typeof value !== "number" || isNaN(value)) {
                            const error = `${field.label || field.name} 必须是有效数字`;
                            console.log(`验证失败:${error}`);
                            errors.push(error);
                        }
                        break;
                    case "date":
                        if (!(value instanceof Date) || isNaN(value.getTime())) {
                            const error = `${field.label || field.name} 必须是有效日期`;
                            console.log(`验证失败:${error}`);
                            errors.push(error);
                        }
                        break;
                }
            }
            
            console.log(`字段 ${field.name} 验证通过`);
        }
        
        const isValid = errors.length === 0;
        console.log(`表单验证${isValid ? "成功" : "失败"},错误数量:${errors.length}`);
        return { isValid, errors };
    }
}

// 使用示例
const validator = new FormValidator(userFormConfig);
// "创建表单验证器,字段数量:5"

// 测试有效数据
const validData: UserFormValues = {
    username: "techuser",
    email: "tech@example.com",
    age: 25,
    birthdate: new Date("1998-01-01"),
    isActive: true
};

const result1 = validator.validate(validData);
// "开始验证表单"
// "验证字段 username:techuser"
// "字段 username 验证通过"
// "验证字段 email:tech@example.com"
// "字段 email 验证通过"
// "验证字段 age:25"
// "字段 age 验证通过"
// "验证字段 birthdate:Wed Jan 01 1998 00:00:00 GMT+0800"
// "字段 birthdate 验证通过"
// "验证字段 isActive:true"
// "字段 isActive 验证通过"
// "表单验证成功,错误数量:0"
console.log(result1); // { isValid: true, errors: [] }

// 测试无效数据
const invalidData: UserFormValues = {
    username: "", // 必填但为空
    email: "invalid-email", // 格式错误
    age: undefined, // 可选字段
    birthdate: undefined, // 可选字段
    isActive: true
};

const result2 = validator.validate(invalidData);
// "开始验证表单"
// "验证字段 username:"
// "验证失败:用户名 是必填字段"
// "验证字段 email:invalid-email"
// "验证失败:邮箱 格式不正确"
// "验证字段 age:undefined"
// "字段 age 验证通过"
// "验证字段 birthdate:undefined"
// "字段 birthdate 验证通过"
// "验证字段 isActive:true"
// "字段 isActive 验证通过"
// "表单验证失败,错误数量:2"
console.log(result2); // { isValid: false, errors: ["用户名 是必填字段", "邮箱 格式不正确"] }

🎯 高级类型最佳实践与设计模式

📋 类型设计原则对比表

原则 说明 好的例子 避免的例子
类型安全优先 优先保证类型安全,避免any type ID = string | number type ID = any
语义化命名 类型名称要有明确含义 type UserRole = "admin" | "user" type T1 = "a" | "b"
组合优于继承 使用联合类型和交叉类型 type Response<T> = Success<T> | Error 复杂的类继承链
渐进式增强 从简单类型开始,逐步增强 先定义基础类型,再扩展 一开始就定义复杂类型
可读性重要 复杂类型要有注释和示例 带有详细注释的类型定义 没有说明的复杂类型

🛠️ 实用工具类型库

// 1. 深度部分类型
type DeepPartial<T> = {
    [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

// 2. 深度必需类型
type DeepRequired<T> = {
    [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];
};

// 3. 选择性Pick
type PickByType<T, U> = {
    [K in keyof T as T[K] extends U ? K : never]: T[K];
};

// 4. 排除性Omit
type OmitByType<T, U> = {
    [K in keyof T as T[K] extends U ? never : K]: T[K];
};

// 5. 可空类型转换
type Nullish<T> = {
    [K in keyof T]: T[K] | null | undefined;
};

// 6. 函数参数提取
type Head<T extends any[]> = T extends [infer H, ...any[]] ? H : never;
type Tail<T extends any[]> = T extends [any, ...infer R] ? R : never;

// 使用示例
interface ComplexUser {
    id: number;
    profile: {
        name: string;
        settings: {
            theme: string;
            notifications: boolean;
        };
    };
    permissions: string[];
    createdAt: Date;
}

type PartialUser = DeepPartial<ComplexUser>;
type StringFields = PickByType<ComplexUser, string>;
type NonStringFields = OmitByType<ComplexUser, string>;

console.log("工具类型演示完成"); // "工具类型演示完成"

🎨 类型体操进阶挑战

// 挑战1:字符串操作类型
type Reverse<S extends string> = S extends `${infer First}${infer Rest}` 
    ? `${Reverse<Rest>}${First}` 
    : S;

type ReversedHello = Reverse<"hello">; // "olleh"

// 挑战2:数组长度计算
type Length<T extends readonly any[]> = T['length'];
type ArrayLength = Length<[1, 2, 3, 4, 5]>; // 5

// 挑战3:对象键值转换
type Flip<T extends Record<string, string>> = {
    [K in keyof T as T[K]]: K;
};

type Original = { a: "x", b: "y", c: "z" };
type Flipped = Flip<Original>; // { x: "a", y: "b", z: "c" }

// 挑战4:递归计数器
type Counter<N extends number, C extends any[] = []> = 
    C['length'] extends N ? C : Counter<N, [...C, any]>;

type FiveItems = Counter<5>; // [any, any, any, any, any]

console.log("类型体操挑战完成"); // "类型体操挑战完成"

📚 本章核心收获总结

🎯 掌握的核心技能

  1. 类型别名 🏷️

    • 简化复杂类型定义
    • 提高代码可读性和维护性
    • 支持泛型和递归定义
  2. 字符串字面量类型 🎯

    • 精确控制字符串值范围
    • 模板字面量类型的强大功能
    • 与Redux、状态管理的完美结合
  3. 元组类型 📏

    • 固定长度和类型的数组
    • 支持可选元素和剩余元素
    • React Hooks等场景的类型安全
  4. 枚举类型 🔢

    • 数字枚举和字符串枚举
    • 常量枚举的编译时优化
    • 现代联合类型替代方案
  5. 类型保护 🛡️

    • typeof、instanceof、in守卫
    • 自定义类型谓词函数
    • 运行时类型收窄机制
  6. 映射类型 🏭

    • 内置工具类型的使用
    • 自定义映射类型的创建
    • 批量类型转换的强大能力
  7. 条件类型 🧠

    • 类型系统的逻辑判断
    • infer关键字的类型推断
    • 分布式条件类型的特性

🚀 实战应用场景

  • API接口设计:使用映射类型生成请求/响应类型
  • 状态管理:字面量类型确保Action类型安全
  • 表单验证:条件类型实现智能表单系统
  • 工具函数:类型保护确保函数参数安全
  • 组件开发:元组类型支持Hooks等模式

💡 进阶学习建议

  1. 多练习类型体操:提升类型思维能力
  2. 阅读优秀库源码:学习实际应用模式
  3. 关注TypeScript更新:掌握最新特性
  4. 结合实际项目:在真实场景中应用
  5. 分享交流经验:与团队共同成长

🎉 恭喜你! 你已经掌握了TypeScript高级类型的核心技能,这些强大的类型工具将让你的代码更加安全、优雅和高效。接下来,我们将探索模块与命名空间的世界,学习如何组织大型TypeScript项目!

❌
❌