高级类型特性✅
本主题涵盖TypeScript的高级类型特性,包括泛型系统、条件类型、映射类型、工具类型等企业级开发必备技能。
TypeScript中的泛型是什么?如何使用?
答案
核心概念:
泛型是一种创建可重用组件的工具,能够支持多种类型数据而不失去类型检查的安全性。它使用类型变量来捕获用户提供的类型信息。
示例说明:
- 交互式演示
- 泛型概念
// TypeScript泛型系统核心概念
// 基础泛型函数
function identity<T>(arg: T): T {
return arg;
}
// 使用方式
let stringResult = identity<string>("hello"); // 明确指定类型
let numberResult = identity(42); // 类型推断为number
let booleanResult = identity(true); // 类型推断为boolean
// 泛型约束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在我们知道它有length属性
return arg;
}
// 使用示例
loggingIdentity("hello"); // ✅ string有length属性
loggingIdentity([1, 2, 3]); // ✅ Array有length属性
// loggingIdentity(123); // ❌ 错误:number没有length属性
// 使用keyof进行约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "Alice", age: 25, city: "New York" };
let name = getProperty(person, "name"); // string类型
let age = getProperty(person, "age"); // number类型
// let invalid = getProperty(person, "salary"); // ❌ 编译错误
// 泛型类
class GenericRepository<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
getById(predicate: (item: T) => boolean): T | undefined {
return this.items.find(predicate);
}
getAll(): T[] {
return [...this.items];
}
}
// 多个泛型参数
interface KeyValuePair<K, V> {
key: K;
value: V;
}
function createPair<T, U>(first: T, second: U): KeyValuePair<T, U> {
return { key: first, value: second };
}
// 默认泛型参数
interface APIResponse<T = any> {
data: T;
status: number;
message: string;
}
面试官视角:
泛型是TypeScript高级特性的基础,考察对类型系统的深度理解:
- 要点清单: 理解泛型的基本语法;掌握泛型约束;能使用keyof进行类型约束;理解泛型的类型推断
- 加分项: 解释泛型的编译时性质;掌握高阶泛型用法;理解泛型与函数重载的关系
- 常见失误: 过度使用泛型导致复杂化;约束条件设计不合理;不理解泛型的类型擦除
延伸阅读:
- TypeScript Handbook - Generics — 泛型官方完整指南
- Advanced TypeScript - Generic Types — 高级泛型用法
什么是条件类型?infer关键字如何使用?
答案
核心概念:
条件类型根据类型关系进行条件判断生成不同的类型,语法为T extends U ? X : Y。infer关键字用于在条件类型中推断待推断类型的部分。
示例说明:
- 基础条件类型
- infer基础用法
- 高级infer用法
- 递归条件类型
- 实际应用
// 基础条件类型
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";
// 使用示例
type T1 = TypeName<string>; // "string"
type T2 = TypeName<number>; // "number"
type T3 = TypeName<() => void>; // "function"
type T4 = TypeName<{ a: 1 }>; // "object"
// 分布式条件类型
type T5 = TypeName<string | number>; // "string" | "number"
// 实用条件类型
type NonNullable<T> = T extends null | undefined ? never : T;
type SafeString = NonNullable<string | null | undefined>; // string
// infer推断返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function greet(): string { return "hello"; }
function add(a: number, b: number): number { return a + b; }
async function fetchData(): Promise<User[]> { return []; }
type GreetReturn = ReturnType<typeof greet>; // string
type AddReturn = ReturnType<typeof add>; // number
type FetchReturn = ReturnType<typeof fetchData>; // Promise<User[]>
// infer推断参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
function createUser(name: string, age: number, active: boolean): User {
return { id: 1, name, email: `${name}@example.com` };
}
type CreateUserParams = Parameters<typeof createUser>; // [string, number, boolean]
// 推断数组元素类型
type ArrayElement<T> = T extends (infer U)[] ? U : T;
type Flatten<T> = T extends (infer U)[] ? U : T;
type StringElement = ArrayElement<string[]>; // string
type FlatUser = Flatten<User[]>; // User
// 推断Promise值类型
type PromiseValue<T> = T extends Promise<infer U> ? U : T;
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
type AsyncString = PromiseValue<Promise<string>>; // string
type NestedAsync = Awaited<Promise<Promise<number>>>; // number
// 复杂infer使用
type GetFirstArg<T> = T extends (first: infer F, ...args: any[]) => any ? F : never;
type GetLastArg<T> = T extends (...args: [...any[], infer L]) => any ? L : never;
function example(name: string, age: number, active: boolean): void {}
type FirstArgType = GetFirstArg<typeof example>; // string
type LastArgType = GetLastArg<typeof example>; // boolean
// 字符串操作条件类型
type StartsWith<T extends string, U extends string> =
T extends `${U}${infer _}` ? true : false;
type EndsWith<T extends string, U extends string> =
T extends `${infer _}${U}` ? true : false;
type StartsWithHello = StartsWith<"hello world", "hello">; // true
type EndsWithWorld = EndsWith<"hello world", "world">; // true
// 递归条件类型
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P]
: DeepReadonly<T[P]>
: T[P];
};
interface NestedUser {
id: number;
profile: {
name: string;
settings: {
theme: string;
notifications: boolean;
};
};
greet(): void;
}
type ReadonlyNestedUser = DeepReadonly<NestedUser>;
// 联合类型分解
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends
(k: infer I) => void ? I : never;
type Union = { a: string } | { b: number };
type Intersection = UnionToIntersection<Union>; // { a: string } & { b: number }
// 分布式 vs 非分布式
type ToArray<T> = T extends any ? T[] : never;
type StringOrNumberArray = ToArray<string | number>; // string[] | number[]
// 阻止分布式行为
type ToTuple<T> = [T] extends [any] ? T[] : never;
type Tuple = ToTuple<string | number>; // (string | number)[]
// API端点类型推断
type APIEndpoint<T extends string> = T extends `${infer Method} ${infer Path}`
? {
method: Method;
path: Path;
handler: Method extends 'GET'
? () => Promise<any>
: (data: any) => Promise<any>;
}
: never;
type UserEndpoint = APIEndpoint<'GET /users/:id'>;
type CreateUserEndpoint = APIEndpoint<'POST /users'>;
// 表单验证类型
type ValidationRule<T> = {
required?: boolean;
validator?: (value: T) => boolean;
message?: string;
};
type FormValidation<T> = {
[K in keyof T]: T[K] extends string
? ValidationRule<string>
: T[K] extends number
? ValidationRule<number>
: T[K] extends boolean
? ValidationRule<boolean>
: ValidationRule<T[K]>;
};
interface UserForm {
name: string;
age: number;
active: boolean;
}
type UserFormValidation = FormValidation<UserForm>;
// 实用函数
function createValidator<T>(rules: FormValidation<T>) {
return function validate(data: T): boolean {
return Object.entries(rules).every(([key, rule]) => {
const value = data[key as keyof T];
const validationRule = rule as ValidationRule<any>;
if (validationRule.required && (value === null || value === undefined)) {
return false;
}
return !validationRule.validator || validationRule.validator(value);
});
};
}
面试官视角:
条件类型是TypeScript最强大的类型操作工具:
- 要点清单: 理解条件类型语法;掌握infer关键字用法;理解分布式条件类型;能实现实用的条件类型工具
- 加分项: 掌握递归条件类型;理解条件类型的性能影响;能解决复杂的类型变换需求
- 常见失误: 不理解分布式行为;infer位置放置错误;递归类型导致编译器栈溢出
延伸阅读:
- TypeScript 2.8 - 条件类型 — 条件类型官方介绍
- TypeScript Handbook - 条件类型 — 条件类型详细文档
什么是映射类型?如何自定义映射类型?
答案
核心概念:
映射类型通过遍历现有类型的属性来创建新类型,使用[K in keyof T]语法。它是TypeScript内置工具类型的实现基础。
示例说明:
// 基础映射类型语法
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface User {
name: string;
age: number;
email: string;
}
type ReadonlyUser = Readonly<User>;
// { readonly name: string; readonly age: number; readonly email: string; }
type PartialUser = Partial<User>;
// { name?: string; age?: number; email?: string; }
// 键名转换
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserGetters = Getters<User>;
// {
// getName: () => string;
// getAge: () => number;
// getEmail: () => string;
// }
// 条件映射
type NonFunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
interface Example {
name: string;
age: number;
greet(): void;
calculate(x: number): number;
}
type ExampleData = NonFunctionProperties<Example>;
// { name: string; age: number; }
// 深度映射
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
interface NestedConfig {
database: {
host: string;
port: number;
credentials: {
username: string;
password: string;
};
};
cache: {
ttl: number;
};
}
type PartialConfig = DeepPartial<NestedConfig>;
// database?: {
// host?: string;
// port?: number;
// credentials?: {
// username?: string;
// password?: string;
// };
// };
// 类型值映射
type BooleanFlags<T> = {
[K in keyof T]: boolean;
};
type UserFlags = BooleanFlags<User>;
// { name: boolean; age: boolean; email: boolean; }
// 模板字面量键名映射
type EventHandlers<T> = {
[K in keyof T as `on${Capitalize<string & K>}Change`]: (value: T[K]) => void;
};
type UserEventHandlers = EventHandlers<User>;
// {
// onNameChange: (value: string) => void;
// onAgeChange: (value: number) => void;
// onEmailChange: (value: string) => void;
// }
// 过滤映射类型
type PickByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
interface Mixed {
name: string;
age: number;
active: boolean;
tags: string[];
onClick: () => void;
}
type StringProperties = PickByType<Mixed, string>;
// { name: string; }
type FunctionProperties = PickByType<Mixed, Function>;
// { onClick: () => void; }
// 组合映射类型
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
type Required<T> = {
[P in keyof T]-?: T[P];
};
type NullableRequired<T> = Required<Nullable<T>>;
type UserNullableRequired = NullableRequired<Partial<User>>;
// { name: string | null; age: number | null; email: string | null; }
// 索引签名映射
type Proxify<T> = {
[P in keyof T]: { get(): T[P]; set(value: T[P]): void; };
};
type ProxifiedUser = Proxify<User>;
// {
// name: { get(): string; set(value: string): void; };
// age: { get(): number; set(value: number): void; };
// email: { get(): string; set(value: string): void; };
// }
// 高级映射类型实用工具
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
type OptionalKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];
type RequiredKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];
interface PartialRequired {
name?: string;
age: number;
email?: string;
}
type Optional = OptionalKeys<PartialRequired>; // "name" | "email"
type Required = RequiredKeys<PartialRequired>; // "age"
面试官视角:
映射类型是TypeScript元编程的核心工具:
- 要点清单: 理解映射类型语法;掌握键名转换技巧;能实现深度映射;理解修饰符操作(readonly、?)
- 加分项: 实现复杂的类型过滤和变换;掌握模板字面量类型结合;理解映射类型的性能特征
- 常见失误: 键名转换语法错误;不理解修饰符的添加和移除;递归映射类型设计不当
延伸阅读:
- TypeScript Handbook - 映射类型 — 映射类型官方文档
- Template Literal Types — 模板字面量类型
TypeScript内置工具类型有哪些?如何使用?
答案
核心概念:
TypeScript提供了丰富的内置工具类型,用于常见的类型变换操作。主要分为属性修饰、键值操作、函数相关和条件过滤四大类。
示例说明:
// 1. 属性修饰类型
interface User {
readonly id: number;
name: string;
email?: string;
phone?: string;
}
// Partial<T> - 所有属性变可选
type PartialUser = Partial<User>;
// { readonly id?: number; name?: string; email?: string; phone?: string; }
// Required<T> - 所有属性变必需
type RequiredUser = Required<User>;
// { readonly id: number; name: string; email: string; phone: string; }
// Readonly<T> - 所有属性变只读
type ReadonlyUser = Readonly<User>;
// { readonly id: number; readonly name: string; readonly email?: string; readonly phone?: string; }
// 2. 键值选择类型
// Pick<T, K> - 选择指定属性
type UserBasicInfo = Pick<User, 'name' | 'email'>;
// { name: string; email?: string; }
// Omit<T, K> - 排除指定属性
type UserWithoutId = Omit<User, 'id'>;
// { name: string; email?: string; phone?: string; }
// Record<K, T> - 构造键值对象类型
type UserRoles = Record<'admin' | 'user' | 'guest', boolean>;
// { admin: boolean; user: boolean; guest: boolean; }
type HttpStatus = Record<number, string>;
const statusMessages: HttpStatus = {
200: 'OK',
404: 'Not Found',
500: 'Internal Server Error'
};
// 3. 联合类型操作
type Status = 'loading' | 'success' | 'error' | 'idle';
type Theme = 'light' | 'dark' | 'auto';
// Exclude<T, U> - 从T中排除U
type ActiveStatus = Exclude<Status, 'idle'>; // 'loading' | 'success' | 'error'
// Extract<T, U> - 从T中提取U
type LoadingStates = Extract<Status, 'loading' | 'idle'>; // 'loading' | 'idle'
// NonNullable<T> - 排除null和undefined
type ValidTheme = NonNullable<Theme | null | undefined>; // 'light' | 'dark' | 'auto'
// 4. 函数相关类型
function createUser(name: string, email: string, active: boolean): User {
return { id: 1, name, email, phone: undefined };
}
// ReturnType<T> - 获取函数返回类型
type CreateUserReturn = ReturnType<typeof createUser>; // User
// Parameters<T> - 获取函数参数类型
type CreateUserParams = Parameters<typeof createUser>; // [string, string, boolean]
// ConstructorParameters<T> - 获取构造函数参数类型
class UserService {
constructor(private apiUrl: string, private timeout: number) {}
}
type UserServiceParams = ConstructorParameters<typeof UserService>; // [string, number]
// InstanceType<T> - 获取构造函数实例类型
type UserServiceInstance = InstanceType<typeof UserService>; // UserService
// 5. 高级应用示例
// 深度Partial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
interface AppConfig {
database: {
host: string;
port: number;
ssl: boolean;
};
cache: {
redis: {
host: string;
port: number;
};
};
}
type PartialConfig = DeepPartial<AppConfig>;
// 条件工具类型组合
type APIResponse<T> = {
data: T;
status: number;
message: string;
};
type APIUserResponse = APIResponse<User>;
type APIUserListResponse = APIResponse<User[]>;
// 提取可选属性键名
type OptionalKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];
type UserOptionalKeys = OptionalKeys<User>; // "email" | "phone"
// 提取必需属性键名
type RequiredKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];
type UserRequiredKeys = RequiredKeys<User>; // "id" | "name"
// 分离可选和必需属性
type GetOptional<T> = Pick<T, OptionalKeys<T>>;
type GetRequired<T> = Pick<T, RequiredKeys<T>>;
type UserOptional = GetOptional<User>; // { email?: string; phone?: string; }
type UserRequired = GetRequired<User>; // { readonly id: number; name: string; }
// 实用工具类型
type Awaited<T> = T extends PromiseLike<infer U> ? U : T;
type AsyncUser = Awaited<Promise<User>>; // User
type SyncUser = Awaited<User>; // User
// 函数参数优化
type FirstParameter<T> = T extends (first: infer F, ...args: any[]) => any ? F : never;
type LastParameter<T> = T extends (...args: any[]) => infer R ? R : never;
function processData(id: number, data: string, options: { validate: boolean }): boolean {
return true;
}
type FirstParam = FirstParameter<typeof processData>; // number
type ReturnValue = LastParameter<typeof processData>; // boolean
面试官视角:
工具类型是TypeScript实用性的重要体现:
- 要点清单: 熟悉常用工具类型(Partial、Pick、Omit、Record等);理解函数相关类型;能组合使用多个工具类型
- 加分项: 实现自定义工具类型;理解工具类型的实现原理;掌握深度操作的技巧
- 常见失误: 混淆Pick和Extract的使用场景;不理解Record的键类型约束;过度复杂的工具类型组合
延伸阅读:
- TypeScript Handbook - 工具类型 — 官方工具类型完整列表
- TypeScript Deep Dive - 工具类型 — 工具类型深入解析
extends关键字在TypeScript中有哪些用法?
答案
核心概念:
extends关键字在TypeScript中有三种主要用法:类继承、接口继承和条件类型判断。每种用法都体现了类型关系的不同方面。
示例说明:
// 1. 类继承
class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number): void {
console.log(`${this.name} moved ${distance} meters`);
}
}
class Dog extends Animal {
private breed: string;
constructor(name: string, breed: string) {
super(name);
this.breed = breed;
}
bark(): void {
console.log(`${this.name} (${this.breed}) barks!`);
}
// 重写父类方法
move(distance: number): void {
console.log(`${this.name} runs ${distance} meters`);
}
}
// 2. 接口继承
interface Flyable {
fly(): void;
altitude: number;
}
interface Swimmable {
swim(): void;
depth: number;
}
// 单一接口继承
interface Bird extends Flyable {
species: string;
wingspan: number;
}
// 多接口继承
interface Duck extends Flyable, Swimmable {
quack(): void;
}
class Mallard implements Duck {
species = "Mallard";
altitude = 0;
depth = 0;
fly(): void {
this.altitude = 100;
console.log("Flying at altitude:", this.altitude);
}
swim(): void {
this.depth = 2;
console.log("Swimming at depth:", this.depth);
}
quack(): void {
console.log("Quack!");
}
}
// 3. 泛型约束中的extends
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength("hello"); // ✅ string有length属性
logLength([1, 2, 3]); // ✅ Array有length属性
// logLength(123); // ❌ number没有length属性
// 使用keyof约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "Alice", age: 30, city: "New York" };
const name = getProperty(person, "name"); // string
const age = getProperty(person, "age"); // number
// const invalid = getProperty(person, "job"); // ❌ 'job'不存在于person中
// 4. 条件类型中的extends
type IsString<T> = T extends string ? true : false;
type Test1 = IsString<string>; // true
type Test2 = IsString<number>; // false
type Test3 = IsString<string | number>; // boolean (分布式)
// 复杂条件类型
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";
type T1 = TypeName<string>; // "string"
type T2 = TypeName<() => void>; // "function"
type T3 = TypeName<{ name: string }>; // "object"
// 5. 条件类型中的infer配合extends
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
function example(name: string, age: number): { name: string; age: number } {
return { name, age };
}
type ExampleReturn = ReturnType<typeof example>; // { name: string; age: number }
type ExampleParams = Parameters<typeof example>; // [string, number]
// 6. 高级extends用法
// 排除某些类型
type NonNullable<T> = T extends null | undefined ? never : T;
type SafeString = NonNullable<string | null | undefined>; // string
// 提取数组元素类型
type ArrayElement<T> = T extends (infer U)[] ? U : T;
type StringElement = ArrayElement<string[]>; // string
type NumberElement = ArrayElement<number>; // number
// 深度类型检查
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P]
: DeepReadonly<T[P]>
: T[P];
};
interface User {
name: string;
profile: {
email: string;
settings: {
theme: string;
notifications: boolean;
};
};
greet(): void;
}
type ReadonlyUser = DeepReadonly<User>;
// 所有嵌套属性变为readonly,但函数保持不变
// 7. 分布式条件类型
type ToArray<T> = T extends any ? T[] : never;
type StringOrNumberArray = ToArray<string | number>; // string[] | number[]
// 阻止分布式行为
type ToTuple<T> = [T] extends [any] ? T[] : never;
type Tuple = ToTuple<string | number>; // (string | number)[]
// 8. 递归extends
type Flatten<T> = T extends (infer U)[]
? U extends (infer V)[]
? Flatten<V>[]
: U
: T;
type NestedArray = number[][][];
type FlattenedArray = Flatten<NestedArray>; // number
// 9. 实际应用:API响应类型推断
type APIResponse<T> = {
success: boolean;
data: T;
message: string;
};
type ExtractAPIData<T> = T extends APIResponse<infer U> ? U : never;
type UserAPI = APIResponse<{ id: number; name: string }>;
type UserData = ExtractAPIData<UserAPI>; // { id: number; name: string }
// 10. 函数重载类型提取
type OverloadReturnType<T> = T extends {
(...args: any[]): infer R;
(...args: any[]): any;
} ? R : T extends (...args: any[]) => infer R ? R : any;
面试官视角:
extends关键字体现了TypeScript类型系统的核心机制:
- 要点清单: 理解三种extends用法的区别;掌握泛型约束语法;能使用条件类型进行类型判断;理解分布式条件类型
- 加分项: 实现复杂的递归条件类型;理解extends在类型推断中的作用;掌握分布式行为的控制
- 常见失误: 混淆继承和条件判断的用法;不理解泛型约束的作用域;分布式条件类型行为理解错误
延伸阅读:
- TypeScript Handbook - Conditional Types — 条件类型官方文档
- TypeScript Handbook - Generic Constraints — 泛型约束详解
什么是模板字面量类型?如何使用?
答案
核心概念:
模板字面量类型使用JavaScript模板字符串语法在类型系统中操作字符串,支持字符串拼接、模式匹配和类型推断,常用于API路径、事件名称等字符串类型的精确控制。
示例说明:
// 基础模板字面量类型
type Greeting<T extends string> = `Hello, ${T}!`;
type WelcomeMessage = Greeting<"World">; // "Hello, World!"
type PersonalGreeting = Greeting<"Alice">; // "Hello, Alice!"
// 联合类型的字符串组合
type EventType = 'click' | 'hover' | 'focus';
type Element = 'button' | 'input' | 'div';
type ElementEvent = `${Element}-${EventType}`;
// "button-click" | "button-hover" | "button-focus" |
// "input-click" | "input-hover" | "input-focus" |
// "div-click" | "div-hover" | "div-focus"
// HTTP方法和路径组合
type Method = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Route = '/users' | '/posts' | '/comments';
type APIEndpoint = `${Method} ${Route}`;
// "GET /users" | "POST /users" | "PUT /users" | "DELETE /users" | ...
// 内置字符串操作类型
type UppercaseGreeting = Uppercase<"hello world">; // "HELLO WORLD"
type LowercaseTitle = Lowercase<"TYPESCRIPT">; // "typescript"
type CapitalizedName = Capitalize<"alice">; // "Alice"
type UncapitalizedTitle = Uncapitalize<"Hello">; // "hello"
// 实际应用:CSS属性类型
type CSSUnit = 'px' | 'rem' | 'em' | '%' | 'vh' | 'vw';
type CSSSize = `${number}${CSSUnit}`;
// 使用示例(实际使用时需要类型断言)
const width: CSSSize = "100px" as CSSSize;
const height: CSSSize = "50rem" as CSSSize;
// 对象属性名生成
type User = {
name: string;
age: number;
email: string;
};
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type Setters<T> = {
[K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};
type UserGetters = Getters<User>;
// {
// getName: () => string;
// getAge: () => number;
// getEmail: () => string;
// }
type UserSetters = Setters<User>;
// {
// setName: (value: string) => void;
// setAge: (value: number) => void;
// setEmail: (value: string) => void;
// }
// 模式匹配和解构
type ExtractRouteParams<T extends string> = T extends `${string}/:${infer Param}/${infer Rest}`
? Param | ExtractRouteParams<`/${Rest}`>
: T extends `${string}/:${infer Param}`
? Param
: never;
type UserRoute = "/users/:id/posts/:postId";
type RouteParams = ExtractRouteParams<UserRoute>; // "id" | "postId"
// SQL查询类型安全
type SQLOperation = 'SELECT' | 'INSERT' | 'UPDATE' | 'DELETE';
type TableName = 'users' | 'posts' | 'comments';
type SQLQuery<Op extends SQLOperation, Table extends TableName> =
Op extends 'SELECT' ? `${Op} * FROM ${Table}` :
Op extends 'INSERT' ? `${Op} INTO ${Table}` :
Op extends 'UPDATE' ? `${Op} ${Table} SET` :
Op extends 'DELETE' ? `${Op} FROM ${Table}` :
never;
type SelectUsers = SQLQuery<'SELECT', 'users'>; // "SELECT * FROM users"
type InsertPost = SQLQuery<'INSERT', 'posts'>; // "INSERT INTO posts"
// 事件处理器类型
type DOMEvent = 'click' | 'keydown' | 'input' | 'change';
type EventHandler<E extends DOMEvent> = `on${Capitalize<E>}`;
type ClickHandler = EventHandler<'click'>; // "onClick"
type KeyDownHandler = EventHandler<'keydown'>; // "onKeydown"
// React组件props
type EventHandlers = {
[E in DOMEvent as EventHandler<E>]?: (event: Event) => void;
};
// {
// onClick?: (event: Event) => void;
// onKeydown?: (event: Event) => void;
// onInput?: (event: Event) => void;
// onChange?: (event: Event) => void;
// }
// 环境变量类型
type Environment = 'development' | 'production' | 'test';
type ConfigKey = 'API_URL' | 'DATABASE_URL' | 'SECRET_KEY';
type EnvironmentConfig = {
[K in ConfigKey as `${K}_${Uppercase<Environment>}`]?: string;
};
// {
// API_URL_DEVELOPMENT?: string;
// API_URL_PRODUCTION?: string;
// API_URL_TEST?: string;
// DATABASE_URL_DEVELOPMENT?: string;
// // ... 其他组合
// }
// 版本号类型
type SemVerPart = `${number}` | `${number}.${number}` | `${number}.${number}.${number}`;
type Version = `v${SemVerPart}`;
type ValidVersions = Version; // "v1" | "v1.0" | "v1.0.0" 等
// URL路径类型
type APIVersion = 'v1' | 'v2';
type ResourceType = 'users' | 'posts' | 'comments';
type APIPath = `/${APIVersion}/${ResourceType}` | `/${APIVersion}/${ResourceType}/:id`;
type UserPaths = APIPath;
// "/v1/users" | "/v1/posts" | "/v1/comments" |
// "/v2/users" | "/v2/posts" | "/v2/comments" |
// "/v1/users/:id" | "/v1/posts/:id" | "/v1/comments/:id" |
// "/v2/users/:id" | "/v2/posts/:id" | "/v2/comments/:id"
// 高级模式匹配
type ParseURL<T extends string> = T extends `https://${infer Domain}/${infer Path}`
? { protocol: 'https'; domain: Domain; path: Path }
: T extends `http://${infer Domain}/${infer Path}`
? { protocol: 'http'; domain: Domain; path: Path }
: T extends `${infer Protocol}://${infer Domain}`
? { protocol: Protocol; domain: Domain; path: '' }
: never;
type ParsedURL = ParseURL<"https://example.com/api/users">;
// { protocol: "https"; domain: "example.com"; path: "api/users" }
// 递归字符串操作
type Join<T extends string[], Delimiter extends string = ',') =
T extends readonly [infer First, ...infer Rest]
? First extends string
? Rest extends string[]
? Rest['length'] extends 0
? First
: `${First}${Delimiter}${Join<Rest, Delimiter>}`
: never
: never
: '';
type JoinedString = Join<["a", "b", "c"], "-">; // "a-b-c"
面试官视角:
模板字面量类型是TypeScript 4.1+的强大特性:
- 要点清单: 理解模板字面量语法;掌握内置字符串操作类型;能实现字符串模式匹配;会在映射类型中使用
- 加分项: 实现复杂的字符串解析;掌握递归字符串操作;理解模板字面量类型的性能影响
- 常见失误: 过度复杂的字符串类型导致编译缓慢;模式匹配边界情况处理不当;字符串类型推断错误
延伸阅读:
- TypeScript 4.1 - 模板字面量类型 — 模板字面量类型官方介绍
- TypeScript Handbook - 模板字面量类型 — 官方详细文档
如何实现一个深度递归的类型工具?
答案
核心概念:
深度递归类型工具通过条件类型和映射类型的组合,实现对嵌套对象结构的深层处理。需要注意递归深度限制和性能优化。
示例说明:
// 1. 深度只读类型
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P] // 函数保持不变
: DeepReadonly<T[P]> // 递归处理对象
: T[P]; // 原始类型保持不变
};
interface NestedUser {
id: number;
name: string;
profile: {
email: string;
settings: {
theme: 'light' | 'dark';
notifications: {
email: boolean;
push: boolean;
};
};
};
greet(): void;
}
type ReadonlyNestedUser = DeepReadonly<NestedUser>;
// 所有嵌套属性都变为readonly,但greet函数保持不变
// 2. 深度可选类型
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object
? T[P] extends (infer U)[]
? DeepPartial<U>[] // 数组处理
: T[P] extends Function
? T[P] // 函数保持不变
: DeepPartial<T[P]> // 递归处理对象
: T[P];
};
type PartialNestedUser = DeepPartial<NestedUser>;
// 所有嵌套属性都变为可选
// 3. 深度必需类型
type DeepRequired<T> = {
[P in keyof T]-?: T[P] extends object
? T[P] extends (infer U)[]
? DeepRequired<U>[]
: T[P] extends Function
? T[P]
: DeepRequired<T[P]>
: T[P];
};
// 4. 深度路径类型
type DeepKeyOf<T> = {
[K in keyof T & (string | number)]: T[K] extends object
? T[K] extends (infer U)[]
? K | `${K}.${number}` | `${K}.${number}.${DeepKeyOf<U>}`
: K | `${K}.${DeepKeyOf<T[K]>}`
: K;
}[keyof T & (string | number)];
type UserPaths = DeepKeyOf<NestedUser>;
// "id" | "name" | "profile" | "profile.email" |
// "profile.settings" | "profile.settings.theme" |
// "profile.settings.notifications" | ...
// 5. 深度获取类型
type DeepGet<T, P> = P extends `${infer K}.${infer Rest}`
? K extends keyof T
? DeepGet<T[K], Rest>
: never
: P extends keyof T
? T[P]
: never;
type ProfileEmail = DeepGet<NestedUser, 'profile.email'>; // string
type Theme = DeepGet<NestedUser, 'profile.settings.theme'>; // 'light' | 'dark'
// 6. 深度设置类型
type DeepSet<T, P, V> = P extends `${infer K}.${infer Rest}`
? K extends keyof T
? {
[Q in keyof T]: Q extends K ? DeepSet<T[Q], Rest, V> : T[Q];
}
: never
: P extends keyof T
? {
[Q in keyof T]: Q extends P ? V : T[Q];
}
: never;
type UpdatedUser = DeepSet<NestedUser, 'profile.settings.theme', 'auto'>;
// theme类型从 'light' | 'dark' 变为 'auto'
// 7. 深度扁平化类型
type DeepFlatten<T, Prefix extends string = ''> = {
[K in keyof T as T[K] extends object
? T[K] extends Function
? `${Prefix}${string & K}`
: never
: `${Prefix}${string & K}`]: T[K] extends object
? T[K] extends Function
? T[K]
: never
: T[K];
} & {
[K in keyof T as T[K] extends object
? T[K] extends Function
? never
: keyof DeepFlatten<T[K], `${Prefix}${string & K}.`>
: never]: T[K] extends object
? T[K] extends Function
? never
: DeepFlatten<T[K], `${Prefix}${string & K}.`>[keyof DeepFlatten<T[K], `${Prefix}${string & K}.`>]
: never;
};
// 8. 深度过滤类型
type DeepPickByType<T, U> = {
[K in keyof T]: T[K] extends U
? T[K]
: T[K] extends object
? T[K] extends Function
? never
: DeepPickByType<T[K], U> extends never
? never
: DeepPickByType<T[K], U>
: never;
};
type OnlyStrings = DeepPickByType<NestedUser, string>;
// 只保留string类型的属性
// 9. 数组处理的递归类型
type DeepArrayToTuple<T> = T extends readonly (infer U)[]
? U extends object
? DeepArrayToTuple<U>[]
: U[]
: T extends object
? { [K in keyof T]: DeepArrayToTuple<T[K]> }
: T;
interface WithArray {
items: { id: number; tags: string[] }[];
metadata: string;
}
type TupledArray = DeepArrayToTuple<WithArray>;
// 10. 性能优化的递归类型
// 使用尾递归优化避免栈溢出
type DeepKeys<T, Acc extends string[] = []> = {
[K in keyof T]: T[K] extends object
? T[K] extends Function
? [...Acc, string & K]
: DeepKeys<T[K], [...Acc, string & K]>
: [...Acc, string & K];
}[keyof T];
// 11. 条件递归深度限制
type RecursionLimit = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
type SafeDeepReadonly<T, Depth extends keyof RecursionLimit = 10> =
[Depth] extends [never]
? never
: {
readonly [P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P]
: SafeDeepReadonly<T[P], RecursionLimit[Depth]>
: T[P];
};
// 12. 实用的深度工具类型
type DeepNonNullable<T> = {
[P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P]
: DeepNonNullable<NonNullable<T[P]>>
: NonNullable<T[P]>;
};
type DeepMutable<T> = {
-readonly [P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P]
: DeepMutable<T[P]>
: T[P];
};
// 使用示例
interface APIConfig {
readonly endpoints: {
readonly users: string;
readonly posts: {
readonly list: string;
readonly detail: string;
};
};
readonly timeout?: number;
}
type MutableConfig = DeepMutable<APIConfig>;
// 所有readonly修饰符都被移除
type RequiredConfig = DeepRequired<APIConfig>;
// timeout变为必需属性
// 13. 复杂应用:表单验证类型
type ValidationRule<T> = {
required?: boolean;
validate?: (value: T) => boolean;
};
type DeepValidation<T> = {
[K in keyof T]: T[K] extends object
? T[K] extends Function
? never
: DeepValidation<T[K]>
: ValidationRule<T[K]>;
};
type UserValidation = DeepValidation<NestedUser>;
// 为每个非函数属性生成验证规则类型
面试官视角:
深度递归类型是TypeScript高级类型编程的顶峰:
- 要点清单: 理解递归类型的实现原理;掌握条件类型和映射类型组合;了解递归深度限制;能处理特殊情况(数组、函数)
- 加分项: 实现性能优化的递归类型;理解TypeScript编译器的递归限制;能设计实用的深度工具类型
- 常见失误: 递归深度过深导致编译器报错;未处理函数和数组的特殊情况;递归条件设计不当导致无限递归
延伸阅读:
- TypeScript Performance — TypeScript性能优化指南
- Recursive Types — 官方递归类型示例