第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("类型体操挑战完成"); // "类型体操挑战完成"
📚 本章核心收获总结
🎯 掌握的核心技能
-
类型别名 🏷️
- 简化复杂类型定义
- 提高代码可读性和维护性
- 支持泛型和递归定义
-
字符串字面量类型 🎯
- 精确控制字符串值范围
- 模板字面量类型的强大功能
- 与Redux、状态管理的完美结合
-
元组类型 📏
- 固定长度和类型的数组
- 支持可选元素和剩余元素
- React Hooks等场景的类型安全
-
枚举类型 🔢
- 数字枚举和字符串枚举
- 常量枚举的编译时优化
- 现代联合类型替代方案
-
类型保护 🛡️
- typeof、instanceof、in守卫
- 自定义类型谓词函数
- 运行时类型收窄机制
-
映射类型 🏭
- 内置工具类型的使用
- 自定义映射类型的创建
- 批量类型转换的强大能力
-
条件类型 🧠
- 类型系统的逻辑判断
- infer关键字的类型推断
- 分布式条件类型的特性
🚀 实战应用场景
-
API接口设计:使用映射类型生成请求/响应类型
-
状态管理:字面量类型确保Action类型安全
-
表单验证:条件类型实现智能表单系统
-
工具函数:类型保护确保函数参数安全
-
组件开发:元组类型支持Hooks等模式
💡 进阶学习建议
-
多练习类型体操:提升类型思维能力
-
阅读优秀库源码:学习实际应用模式
-
关注TypeScript更新:掌握最新特性
-
结合实际项目:在真实场景中应用
-
分享交流经验:与团队共同成长
🎉 恭喜你! 你已经掌握了TypeScript高级类型的核心技能,这些强大的类型工具将让你的代码更加安全、优雅和高效。接下来,我们将探索模块与命名空间的世界,学习如何组织大型TypeScript项目!