跳到主要内容

高级类型特性✅

本主题涵盖TypeScript的高级类型特性,包括泛型系统、条件类型、映射类型、工具类型等企业级开发必备技能。

TypeScript中的泛型是什么?如何使用?

答案

核心概念:

泛型是一种创建可重用组件的工具,能够支持多种类型数据而不失去类型检查的安全性。它使用类型变量来捕获用户提供的类型信息。

示例说明:

// TypeScript泛型系统comprehensive示例

console.log('🔧 TypeScript泛型系统全面演示');

// 1. 基础泛型函数演示
const demoBasicGenerics = () => {
  console.log('\n=== 基础泛型函数 ===');
  
  // 模拟泛型identity函数的效果
  const identity = (arg) => arg;
  
  console.log('identity("hello"):', identity("hello"));
  console.log('identity(42):', identity(42));
  console.log('identity(true):', identity(true));
  console.log('identity([1,2,3]):', identity([1, 2, 3]));
  
  // 泛型函数的类型推断效果
  const numbers = [1, 2, 3, 4, 5];
  const strings = ["a", "b", "c"];
  
  const firstNumber = identity(numbers[0]);
  const firstString = identity(strings[0]);
  
  console.log('firstNumber:', firstNumber, typeof firstNumber);
  console.log('firstString:', firstString, typeof firstString);
};

// 2. 泛型约束演示
const demoGenericConstraints = () => {
  console.log('\n=== 泛型约束演示 ===');
  
  // 模拟Lengthwise约束的效果
  const loggingIdentity = (arg) => {
    if (arg && typeof arg.length === 'number') {
      console.log(`参数长度: ${arg.length}, 值:`, arg);
      return arg;
    } else {
      console.log('参数没有length属性:', arg);
      return arg;
    }
  };
  
  // 有length属性的类型
  console.log('字符串测试:');
  loggingIdentity("hello world");
  
  console.log('数组测试:');
  loggingIdentity([1, 2, 3, 4, 5]);
  
  console.log('类数组对象测试:');
  loggingIdentity({ 0: 'a', 1: 'b', 2: 'c', length: 3 });
  
  // 没有length属性的类型
  console.log('数字测试(没有length):');
  loggingIdentity(123);
  
  console.log('对象测试(没有length):');
  loggingIdentity({ name: 'Alice', age: 25 });
};

// 3. keyof约束演示
const demoKeyofConstraint = () => {
  console.log('\n=== keyof约束演示 ===');
  
  // 模拟getProperty函数的效果
  const getProperty = (obj, key) => {
    if (key in obj) {
      console.log(`获取属性 ${key}:`, obj[key]);
      return obj[key];
    } else {
      console.log(`属性 ${key} 不存在于对象中`);
      return undefined;
    }
  };
  
  const person = {
    name: "Alice",
    age: 25,
    city: "New York",
    email: "alice@example.com"
  };
  
  console.log('person对象:', person);
  console.log('有效属性访问:');
  getProperty(person, "name");
  getProperty(person, "age");
  getProperty(person, "city");
  
  console.log('无效属性访问:');
  getProperty(person, "salary");
  getProperty(person, "phone");
};

// 4. 泛型类演示
const demoGenericClass = () => {
  console.log('\n=== 泛型类演示 ===');
  
  // 模拟泛型Repository类
  class GenericRepository {
    constructor() {
      this.items = [];
    }
    
    add(item) {
      this.items.push(item);
      console.log('添加项目:', item);
    }
    
    getById(predicate) {
      const found = this.items.find(predicate);
      console.log('查找结果:', found);
      return found;
    }
    
    getAll() {
      console.log('所有项目:', this.items);
      return [...this.items];
    }
    
    update(predicate, newItem) {
      const index = this.items.findIndex(predicate);
      if (index >= 0) {
        console.log(`更新项目 ${index}:`, this.items[index], '->', newItem);
        this.items[index] = newItem;
        return true;
      } else {
        console.log('未找到要更新的项目');
        return false;
      }
    }
    
    delete(predicate) {
      const index = this.items.findIndex(predicate);
      if (index >= 0) {
        const deleted = this.items.splice(index, 1)[0];
        console.log('删除项目:', deleted);
        return true;
      } else {
        console.log('未找到要删除的项目');
        return false;
      }
    }
  }
  
  // 用户存储库
  console.log('=== 用户存储库演示 ===');
  const userRepo = new GenericRepository();
  
  userRepo.add({ id: 1, name: 'Alice', email: 'alice@example.com' });
  userRepo.add({ id: 2, name: 'Bob', email: 'bob@example.com' });
  userRepo.add({ id: 3, name: 'Charlie', email: 'charlie@example.com' });
  
  userRepo.getAll();
  userRepo.getById(user => user.name === 'Alice');
  userRepo.update(user => user.id === 2, { id: 2, name: 'Bob Smith', email: 'bob.smith@example.com' });
  userRepo.delete(user => user.id === 3);
  userRepo.getAll();
  
  // 产品存储库
  console.log('\n=== 产品存储库演示 ===');
  const productRepo = new GenericRepository();
  
  productRepo.add({ id: 'p1', name: 'Laptop', price: 999.99 });
  productRepo.add({ id: 'p2', name: 'Mouse', price: 25.99 });
  productRepo.add({ id: 'p3', name: 'Keyboard', price: 79.99 });
  
  productRepo.getById(product => product.price < 30);
  productRepo.getAll();
};

// 5. 多个泛型参数演示
const demoMultipleTypeParams = () => {
  console.log('\n=== 多个泛型参数演示 ===');
  
  // 模拟KeyValuePair
  const createPair = (key, value) => {
    const pair = { key, value };
    console.log(`创建键值对: ${typeof key} -> ${typeof value}`, pair);
    return pair;
  };
  
  console.log('不同类型的键值对:');
  createPair("name", "Alice");
  createPair("age", 25);
  createPair(1, "first");
  createPair(true, ["a", "b", "c"]);
  createPair("config", { mode: "development", debug: true });
};

// 6. 泛型工具函数演示
const demoGenericUtils = () => {
  console.log('\n=== 泛型工具函数演示 ===');
  
  // 模拟泛型工具类
  const GenericUtils = {
    map: (array, transform) => {
      console.log('映射前:', array);
      const result = array.map(transform);
      console.log('映射后:', result);
      return result;
    },
    
    filter: (array, predicate) => {
      console.log('过滤前:', array);
      const result = array.filter(predicate);
      console.log('过滤后:', result);
      return result;
    },
    
    reduce: (array, reducer, initialValue) => {
      console.log('归约输入:', array, '初始值:', initialValue);
      const result = array.reduce(reducer, initialValue);
      console.log('归约结果:', result);
      return result;
    },
    
    groupBy: (array, keySelector) => {
      console.log('分组输入:', array);
      const groups = array.reduce((acc, item) => {
        const key = keySelector(item);
        if (!acc[key]) {
          acc[key] = [];
        }
        acc[key].push(item);
        return acc;
      }, {});
      console.log('分组结果:', groups);
      return groups;
    }
  };
  
  const users = [
    { id: 1, name: 'Alice', department: 'Engineering', age: 25 },
    { id: 2, name: 'Bob', department: 'Sales', age: 30 },
    { id: 3, name: 'Charlie', department: 'Engineering', age: 28 },
    { id: 4, name: 'Diana', department: 'Marketing', age: 26 },
    { id: 5, name: 'Eve', department: 'Sales', age: 32 }
  ];
  
  console.log('原始用户数据:', users);
  
  // 提取用户名
  GenericUtils.map(users, user => user.name);
  
  // 过滤工程师
  GenericUtils.filter(users, user => user.department === 'Engineering');
  
  // 计算平均年龄
  GenericUtils.reduce(users, (sum, user) => sum + user.age, 0) / users.length;
  
  // 按部门分组
  GenericUtils.groupBy(users, user => user.department);
};

// 7. 异步结果处理演示
const demoAsyncResult = () => {
  console.log('\n=== 异步结果处理演示 ===');
  
  class AsyncResult {
    constructor(value, error) {
      this.value = value;
      this.error = error;
    }
    
    static success(value) {
      console.log('创建成功结果:', value);
      return new AsyncResult(value, null);
    }
    
    static failure(error) {
      console.log('创建失败结果:', error);
      return new AsyncResult(null, error);
    }
    
    isSuccess() {
      return this.error === null;
    }
    
    getValue() {
      return this.value;
    }
    
    getError() {
      return this.error;
    }
    
    map(transform) {
      if (this.isSuccess() && this.value !== null) {
        const transformed = transform(this.value);
        console.log('转换成功:', this.value, '->', transformed);
        return AsyncResult.success(transformed);
      }
      console.log('转换跳过(因为是错误结果)');
      return AsyncResult.failure(this.error);
    }
    
    flatMap(transform) {
      if (this.isSuccess() && this.value !== null) {
        return transform(this.value);
      }
      return AsyncResult.failure(this.error);
    }
  }
  
  // 模拟异步操作
  const fetchUser = (id) => {
    console.log(`模拟获取用户 ${id}`);
    if (id > 0 && id <= 3) {
      return AsyncResult.success({ id, name: `User${id}`, email: `user${id}@example.com` });
    } else {
      return AsyncResult.failure(new Error(`User ${id} not found`));
    }
  };
  
  const processUser = (user) => {
    console.log('处理用户:', user);
    return AsyncResult.success(`Processed: ${user.name}`);
  };
  
  // 测试成功情况
  console.log('=== 成功情况 ===');
  const result1 = fetchUser(1);
  const processed1 = result1.flatMap(processUser);
  console.log('最终结果:', processed1.getValue());
  
  // 测试失败情况
  console.log('\n=== 失败情况 ===');
  const result2 = fetchUser(999);
  const processed2 = result2.flatMap(processUser);
  console.log('错误信息:', processed2.getError()?.message);
  
  // 测试链式调用
  console.log('\n=== 链式转换 ===');
  const result3 = fetchUser(2)
    .map(user => user.name.toUpperCase())
    .map(name => `Welcome, ${name}!`);
  
  if (result3.isSuccess()) {
    console.log('链式转换结果:', result3.getValue());
  }
};

// 执行所有示例
demoBasicGenerics();
demoGenericConstraints();
demoKeyofConstraint();
demoGenericClass();
demoMultipleTypeParams();
demoGenericUtils();
demoAsyncResult();

console.log('\n🎉 泛型系统演示完成!');

面试官视角:

泛型是TypeScript高级特性的基础,考察对类型系统的深度理解:

  • 要点清单: 理解泛型的基本语法;掌握泛型约束;能使用keyof进行类型约束;理解泛型的类型推断
  • 加分项: 解释泛型的编译时性质;掌握高阶泛型用法;理解泛型与函数重载的关系
  • 常见失误: 过度使用泛型导致复杂化;约束条件设计不合理;不理解泛型的类型擦除

延伸阅读:

什么是条件类型?infer关键字如何使用?

答案

核心概念:

条件类型根据类型关系进行条件判断生成不同的类型,语法为T extends U ? X : Yinfer关键字用于在条件类型中推断待推断类型的部分。

示例说明:

answers/P1-conditional-types/conditional-examples.ts
// 基础条件类型
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

面试官视角:

条件类型是TypeScript最强大的类型操作工具:

  • 要点清单: 理解条件类型语法;掌握infer关键字用法;理解分布式条件类型;能实现实用的条件类型工具
  • 加分项: 掌握递归条件类型;理解条件类型的性能影响;能解决复杂的类型变换需求
  • 常见失误: 不理解分布式行为;infer位置放置错误;递归类型导致编译器栈溢出

延伸阅读:

什么是映射类型?如何自定义映射类型?

答案

核心概念:

映射类型通过遍历现有类型的属性来创建新类型,使用[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内置工具类型有哪些?如何使用?

答案

核心概念:

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的键类型约束;过度复杂的工具类型组合

延伸阅读:

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在类型推断中的作用;掌握分布式行为的控制
  • 常见失误: 混淆继承和条件判断的用法;不理解泛型约束的作用域;分布式条件类型行为理解错误

延伸阅读:

什么是模板字面量类型?如何使用?

答案

核心概念:

模板字面量类型使用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+的强大特性:

  • 要点清单: 理解模板字面量语法;掌握内置字符串操作类型;能实现字符串模式匹配;会在映射类型中使用
  • 加分项: 实现复杂的字符串解析;掌握递归字符串操作;理解模板字面量类型的性能影响
  • 常见失误: 过度复杂的字符串类型导致编译缓慢;模式匹配边界情况处理不当;字符串类型推断错误

延伸阅读:

如何实现一个深度递归的类型工具?

答案

核心概念:

深度递归类型工具通过条件类型和映射类型的组合,实现对嵌套对象结构的深层处理。需要注意递归深度限制和性能优化。

示例说明:

// 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编译器的递归限制;能设计实用的深度工具类型
  • 常见失误: 递归深度过深导致编译器报错;未处理函数和数组的特殊情况;递归条件设计不当导致无限递归

延伸阅读: