TypeScript 和 JavaScript 的区别是什么?
What are the differences between TypeScript and JavaScript?
- *考察点:对 TypeScript 基本概念和语言特性的理解。*
共 36 道题目
What are the differences between TypeScript and JavaScript?
What are the differences between TypeScript and JavaScript?
答案:
TypeScript 是 JavaScript 的超集,在 JavaScript 基础上增加了静态类型检查系统。主要区别体现在类型系统、编译时检查、开发体验和代码质量保证等方面。
主要区别:
类型系统:
// JavaScript - 动态类型,运行时确定
let name = "张三";
name = 123; // 运行时才发现问题
// TypeScript - 静态类型,编译时检查
let name: string = "张三";
name = 123; // 编译时就会报错
编译过程:
错误检测:
// TypeScript 能在编译时发现错误
function greet(person: { name: string, age: number }) {
return `Hello ${person.name}`;
}
greet({ name: "李四" }); // Error: Property 'age' is missing
开发优势:
What are the basic data types in TypeScript?
What are the basic data types in TypeScript?
答案:
TypeScript 的基本数据类型包括 JavaScript 原有类型和 TypeScript 特有类型,提供了完整的类型约束能力。
基本类型:
原始类型:
// 数字类型
let age: number = 25;
let price: number = 99.99;
// 字符串类型
let name: string = "张三";
let template: string = `Hello, ${name}`;
// 布尔类型
let isActive: boolean = true;
let isCompleted: boolean = false;
空值类型:
// undefined 和 null
let u: undefined = undefined;
let n: null = null;
// void - 通常用于函数无返回值
function logMessage(): void {
console.log("Hello");
}
TypeScript 特有类型:
// any - 任意类型,失去类型检查
let anything: any = 42;
anything = "hello";
anything = true;
// unknown - 安全的 any
let userInput: unknown;
if (typeof userInput === "string") {
console.log(userInput.toUpperCase()); // 需要类型检查
}
// never - 永不存在的值的类型
function error(): never {
throw new Error("Something went wrong");
}
复合类型:
// 数组
let numbers: number[] = [1, 2, 3];
let strings: Array<string> = ["a", "b", "c"];
// 元组
let tuple: [string, number] = ["hello", 42];
// 对象
let user: { name: string; age: number } = {
name: "李四",
age: 30
};
What are type annotations? How to use them?
What are type annotations? How to use them?
答案:
类型注解是 TypeScript 中显式指定变量、函数参数、返回值等的类型的语法。通过在变量名后使用冒号 : 加类型名的方式来声明类型。
基本语法:
变量类型注解:
// 基本变量类型注解
let userName: string = "张三";
let userAge: number = 25;
let isActive: boolean = true;
// 可以省略初始值
let email: string;
email = "[email protected]";
函数类型注解:
// 参数和返回值类型注解
function add(a: number, b: number): number {
return a + b;
}
// 箭头函数类型注解
const multiply = (x: number, y: number): number => x * y;
// 可选参数
function greet(name: string, title?: string): string {
return title ? `Hello, ${title} ${name}` : `Hello, ${name}`;
}
对象类型注解:
// 对象类型注解
let user: {
id: number;
name: string;
email?: string; // 可选属性
} = {
id: 1,
name: "李四"
};
// 函数作为对象属性
let calculator: {
add: (a: number, b: number) => number;
subtract: (a: number, b: number) => number;
} = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
使用场景:
What is type inference?
What is type inference?
答案:
类型推断是 TypeScript 编译器根据代码上下文自动推断变量类型的能力,无需显式类型注解就能确定类型。这让代码更简洁的同时保持类型安全。
推断规则:
初始化推断:
// TypeScript 自动推断类型
let name = "张三"; // 推断为 string
let age = 25; // 推断为 number
let isActive = true; // 推断为 boolean
// 等价于显式注解
let name: string = "张三";
let age: number = 25;
let isActive: boolean = true;
函数返回值推断:
// 返回值类型自动推断
function add(a: number, b: number) {
return a + b; // 推断返回值为 number
}
function getUser() {
return {
id: 1,
name: "李四",
age: 30
}; // 推断返回值为 { id: number; name: string; age: number }
}
数组类型推断:
// 数组元素类型推断
let numbers = [1, 2, 3]; // 推断为 number[]
let mixed = [1, "hello", true]; // 推断为 (string | number | boolean)[]
let users = [
{ name: "张三", age: 25 },
{ name: "李四", age: 30 }
]; // 推断为 { name: string; age: number }[]
上下文推断:
// 基于上下文推断参数类型
const numbers = [1, 2, 3];
numbers.map(num => num.toString()); // num 推断为 number
// 事件处理中的推断
button.addEventListener('click', (event) => {
// event 推断为 MouseEvent
console.log(event.clientX);
});
推断优势:
What is an interface? How to define and use it?
What is an interface? How to define and use it?
答案:
接口(interface)是 TypeScript 中定义对象结构的方式,用于约束对象的形状。它只描述类型结构,不包含实现,是实现类型契约和代码规范的重要工具。
基本定义与使用:
基础接口:
// 定义用户接口
interface User {
id: number;
name: string;
email: string;
}
// 使用接口约束对象
const user: User = {
id: 1,
name: "张三",
email: "[email protected]"
};
可选属性和只读属性:
interface Product {
readonly id: number; // 只读属性
name: string;
price: number;
description?: string; // 可选属性
}
const product: Product = {
id: 1,
name: "笔记本电脑",
price: 5999
// description 可选,可以不提供
};
// product.id = 2; // 错误:无法修改只读属性
函数接口:
// 定义函数类型接口
interface Calculator {
(a: number, b: number): number;
}
const add: Calculator = (x, y) => x + y;
const multiply: Calculator = (x, y) => x * y;
// 包含方法的接口
interface MathOperations {
add(a: number, b: number): number;
subtract(a: number, b: number): number;
}
接口继承:
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
bark(): void;
}
const myDog: Dog = {
name: "旺财",
age: 3,
breed: "金毛",
bark() {
console.log("汪汪汪!");
}
};
应用场景:
How to use optional chaining operator (?.) and nullish coalescing operator (??) in TypeScript?
How to use optional chaining operator (?.) and nullish coalescing operator (??) in TypeScript?
答案:
可选链操作符(?.)和空值合并操作符(??)是 TypeScript 提供的安全访问语法,用于处理可能为 null 或 undefined 的值,避免运行时错误。
可选链操作符(?.):
对象属性访问:
interface User {
name: string;
profile?: {
avatar?: string;
address?: {
city: string;
street?: string;
};
};
}
const user: User = { name: "张三" };
// 传统方式 - 容易出错
// const avatar = user.profile.avatar; // 运行时错误
// 使用可选链 - 安全访问
const avatar = user.profile?.avatar; // undefined
const city = user.profile?.address?.city; // undefined
const street = user.profile?.address?.street; // undefined
方法调用:
interface Api {
getData?: () => Promise<any>;
utils?: {
format?: (data: any) => string;
};
}
const api: Api = {};
// 安全的方法调用
const data = await api.getData?.(); // undefined
const formatted = api.utils?.format?.(data); // undefined
数组访问:
const users: User[] | undefined = getUsers();
// 安全的数组访问
const firstUser = users?.[0];
const firstName = users?.[0]?.name;
空值合并操作符(??):
基本用法:
// 只有当左侧为 null 或 undefined 时才使用右侧值
const username = user.name ?? "匿名用户";
const port = process.env.PORT ?? 3000;
const config = savedConfig ?? defaultConfig;
// 与 || 的区别
const count1 = 0 || 10; // 10 (0 被认为是假值)
const count2 = 0 ?? 10; // 0 (0 不是 null/undefined)
结合使用:
// 可选链 + 空值合并的组合
const userCity = user.profile?.address?.city ?? "未知城市";
const avatar = user.profile?.avatar ?? "/default-avatar.png";
// 复杂对象的安全访问和默认值
const theme = config?.ui?.theme ?? "light";
const apiUrl = settings?.api?.baseUrl ?? "https://api.example.com";
实际应用场景:
What is enum in TypeScript?
What is enum in TypeScript?
答案:
枚举(enum)是 TypeScript 提供的一种数据类型,用于定义一组命名的常量。它让代码更具可读性和维护性,常用于表示状态、类型、配置等固定的值集合。
枚举类型:
数字枚举:
// 默认从 0 开始递增
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// 自定义起始值
enum Status {
Pending = 1, // 1
Processing, // 2
Completed, // 3
Failed // 4
}
// 使用枚举
const currentDirection = Direction.Up;
const orderStatus = Status.Processing;
console.log(currentDirection); // 0
console.log(Status.Completed); // 3
字符串枚举:
enum Color {
Red = "red",
Green = "green",
Blue = "blue"
}
enum ApiEndpoint {
Users = "/api/users",
Products = "/api/products",
Orders = "/api/orders"
}
// 使用字符串枚举
const userColor = Color.Red; // "red"
const endpoint = ApiEndpoint.Users; // "/api/users"
异构枚举(混合类型):
enum Mixed {
No = 0,
Yes = "YES"
}
// 不推荐使用,容易混淆
常量枚举(const enum):
const enum Theme {
Light = "light",
Dark = "dark"
}
// 编译时会被内联,不会生成额外代码
const currentTheme = Theme.Dark; // 编译后直接是 "dark"
实际应用:
// HTTP 状态码
enum HttpStatus {
OK = 200,
NotFound = 404,
InternalServerError = 500
}
// 用户角色
enum UserRole {
Admin = "admin",
User = "user",
Guest = "guest"
}
// 在函数中使用
function handleResponse(status: HttpStatus) {
switch (status) {
case HttpStatus.OK:
return "请求成功";
case HttpStatus.NotFound:
return "资源未找到";
default:
return "未知错误";
}
}
// 检查用户权限
function checkPermission(role: UserRole): boolean {
return role === UserRole.Admin;
}
使用场景:
How does TypeScript define arrays and tuples?
How does TypeScript define arrays and tuples?
答案:
TypeScript 提供了数组(Array)和元组(Tuple)两种集合类型。数组是相同类型元素的集合,元组是固定长度和类型的有序列表。
数组定义:
基础数组类型:
// 两种声明方式
let numbers1: number[] = [1, 2, 3, 4, 5];
let numbers2: Array<number> = [1, 2, 3, 4, 5];
let names: string[] = ["张三", "李四", "王五"];
let flags: boolean[] = [true, false, true];
// 混合类型数组(联合类型)
let mixed: (string | number)[] = [1, "hello", 2, "world"];
对象数组:
interface User {
id: number;
name: string;
}
let users: User[] = [
{ id: 1, name: "张三" },
{ id: 2, name: "李四" }
];
// 或使用 Array<T> 语法
let products: Array<{ name: string; price: number }> = [
{ name: "笔记本", price: 5999 },
{ name: "手机", price: 3999 }
];
元组定义:
基础元组:
// 固定长度和类型的数组
let point: [number, number] = [10, 20];
let user: [string, number, boolean] = ["张三", 25, true];
// 访问元组元素
console.log(point[0]); // 10 (number)
console.log(user[1]); // 25 (number)
// 解构赋值
const [x, y] = point;
const [name, age, isActive] = user;
可选元素和剩余元素:
// 可选元素
let optional: [string, number?] = ["hello"]; // 第二个元素可选
let optional2: [string, number?] = ["hello", 42];
// 剩余元素
let rest: [string, ...number[]] = ["prefix", 1, 2, 3, 4];
// 命名元组(TypeScript 4.0+)
type Point3D = [x: number, y: number, z: number];
let position: Point3D = [10, 20, 30];
只读元组:
// 只读元组
let readonlyTuple: readonly [number, string] = [1, "hello"];
// readonlyTuple[0] = 2; // 错误:无法修改只读元组
// 只读数组
let readonlyArray: readonly number[] = [1, 2, 3];
// readonlyArray.push(4); // 错误:只读数组不能修改
实际应用场景:
// 坐标点
type Coordinate = [number, number];
const origin: Coordinate = [0, 0];
// 函数返回多个值
function getNameAndAge(): [string, number] {
return ["张三", 25];
}
const [userName, userAge] = getNameAndAge();
// 状态管理(类似 React useState)
type State<T> = [T, (value: T) => void];
function useState<T>(initial: T): State<T> {
let state = initial;
const setState = (value: T) => { state = value; };
return [state, setState];
}
// 键值对
type KeyValue = [string, any];
const config: KeyValue[] = [
["theme", "dark"],
["language", "zh-CN"],
["debug", true]
];
数组 vs 元组的区别:
What are Union Types and Intersection Types?
What are Union Types and Intersection Types?
答案:
联合类型和交叉类型是 TypeScript 中组合多个类型的方式。联合类型表示"或"的关系(A 或 B),交叉类型表示"和"的关系(A 和 B)。
联合类型(Union Types):
基础联合类型:
// 使用 | 操作符定义联合类型
let value: string | number;
value = "hello"; // 有效
value = 42; // 有效
// value = true; // 错误:boolean 不在联合类型中
// 函数参数的联合类型
function formatValue(input: string | number): string {
if (typeof input === "string") {
return input.toUpperCase();
}
return input.toString();
}
console.log(formatValue("hello")); // "HELLO"
console.log(formatValue(123)); // "123"
对象联合类型:
interface Cat {
type: "cat";
meow(): void;
}
interface Dog {
type: "dog";
bark(): void;
}
type Pet = Cat | Dog;
function handlePet(pet: Pet) {
// 使用判别联合类型
switch (pet.type) {
case "cat":
pet.meow(); // TypeScript 知道这是 Cat
break;
case "dog":
pet.bark(); // TypeScript 知道这是 Dog
break;
}
}
字面量联合类型:
// 字符串字面量联合
type Theme = "light" | "dark" | "auto";
type Size = "small" | "medium" | "large";
let currentTheme: Theme = "dark"; // 有效
// let invalidTheme: Theme = "blue"; // 错误
// 数字字面量联合
type HttpStatus = 200 | 404 | 500;
function handleStatus(status: HttpStatus) {
switch (status) {
case 200:
return "成功";
case 404:
return "未找到";
case 500:
return "服务器错误";
}
}
交叉类型(Intersection Types):
基础交叉类型:
// 使用 & 操作符定义交叉类型
interface Person {
name: string;
age: number;
}
interface Employee {
employeeId: string;
department: string;
}
// 交叉类型包含两个接口的所有属性
type PersonEmployee = Person & Employee;
const worker: PersonEmployee = {
name: "张三",
age: 30,
employeeId: "EMP001",
department: "开发部"
};
Mixin 模式:
interface Timestamped {
timestamp: Date;
}
interface Tagged {
tags: string[];
}
// 组合多个能力
type BlogPost = {
title: string;
content: string;
} & Timestamped & Tagged;
const post: BlogPost = {
title: "TypeScript 学习",
content: "今天学习了联合类型和交叉类型",
timestamp: new Date(),
tags: ["TypeScript", "编程"]
};
函数类型交叉:
type Logger = {
log: (message: string) => void;
};
type Formatter = {
format: (data: any) => string;
};
type LoggerFormatter = Logger & Formatter;
const utility: LoggerFormatter = {
log: (message: string) => console.log(message),
format: (data: any) => JSON.stringify(data)
};
实际应用场景:
// API 响应类型
type ApiResponse<T> =
| { success: true; data: T }
| { success: false; error: string };
// 事件类型
type MouseEvent = {
type: "click" | "hover";
target: Element;
} & ({ type: "click"; button: number } | { type: "hover" });
// 配置对象
type Config = BaseConfig & (DevConfig | ProdConfig);
interface BaseConfig {
appName: string;
version: string;
}
interface DevConfig {
env: "development";
debug: true;
}
interface ProdConfig {
env: "production";
debug: false;
apiUrl: string;
}
使用区别:
What is type alias in TypeScript?
What is type alias in TypeScript?
- 考察点:类型别名概念与应用场景。
答案:
类型别名(type alias)是使用 type 关键字为现有类型创建新名称的方式。它不创建新类型,而是为类型提供一个更具描述性或更简洁的名称。
基本语法:
基础类型别名:
// 为基本类型创建别名
type ID = string | number;
type Name = string;
type Age = number;
// 使用类型别名
let userId: ID = "user_123";
let userName: Name = "张三";
let userAge: Age = 25;
// 函数参数使用别名
function getUser(id: ID): { name: Name; age: Age } {
return { name: "张三", age: 25 };
}
对象类型别名:
// 复杂对象类型别名
type User = {
id: number;
name: string;
email: string;
profile?: {
avatar: string;
bio: string;
};
};
type Product = {
id: number;
name: string;
price: number;
category: string;
};
// 使用类型别名
const user: User = {
id: 1,
name: "李四",
email: "[email protected]"
};
函数类型别名:
// 函数类型别名
type EventHandler = (event: Event) => void;
type Validator<T> = (value: T) => boolean;
type Mapper<T, R> = (input: T) => R;
// 使用函数类型别名
const clickHandler: EventHandler = (e) => console.log("clicked");
const isValidEmail: Validator<string> = (email) => email.includes("@");
const toString: Mapper<number, string> = (num) => num.toString();
泛型类型别名:
// 泛型类型别名
type ApiResponse<T> = {
success: boolean;
data: T;
message?: string;
};
type Optional<T> = T | undefined;
type Nullable<T> = T | null;
// 使用泛型别名
type UserResponse = ApiResponse<User>;
type OptionalString = Optional<string>;
const response: UserResponse = {
success: true,
data: { id: 1, name: "王五", email: "[email protected]" }
};
type vs interface 的区别:
语法差异:
// type - 使用 = 赋值
type Point = {
x: number;
y: number;
};
// interface - 直接定义
interface Point2 {
x: number;
y: number;
}
扩展方式:
// type 使用交叉类型扩展
type Animal = {
name: string;
};
type Dog = Animal & {
breed: string;
};
// interface 使用 extends 扩展
interface Animal2 {
name: string;
}
interface Dog2 extends Animal2 {
breed: string;
}
能力差异:
// type 可以表示联合类型、基本类型等
type Status = "loading" | "success" | "error";
type StringOrNumber = string | number;
// interface 不能表示联合类型或基本类型
// interface Status = "loading" | "success" | "error"; // 错误
实际应用场景:
// 状态管理
type AppState = {
user: User | null;
theme: "light" | "dark";
loading: boolean;
};
// 事件系统
type EventType = "click" | "scroll" | "resize";
type EventListener<T = any> = (data: T) => void;
// API 类型定义
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type RequestConfig = {
method: HttpMethod;
url: string;
headers?: Record<string, string>;
body?: any;
};
// 工具类型组合
type PartialUser = Partial<User>; // 所有属性可选
type RequiredUser = Required<User>; // 所有属性必需
type UserKeys = keyof User; // 获取所有属性名
使用建议:
interface 定义对象结构type 定义联合类型、基本类型别名type 进行复杂类型组合How to define function types in TypeScript?
How to define function types in TypeScript?
- 考察点:函数类型声明方式与应用。
答案:
TypeScript 提供了多种方式来定义函数类型,包括函数声明、函数表达式、箭头函数和函数类型别名等,可以精确控制参数类型和返回值类型。
函数类型定义方式:
函数声明:
// 基础函数声明
function add(a: number, b: number): number {
return a + b;
}
// 可选参数
function greet(name: string, title?: string): string {
return title ? `Hello, ${title} ${name}` : `Hello, ${name}`;
}
// 默认参数
function createUser(name: string, age: number = 18): User {
return { id: Date.now(), name, age };
}
// 剩余参数
function sum(...numbers: number[]): number {
return numbers.reduce((total, num) => total + num, 0);
}
函数表达式:
// 完整的函数类型注解
const multiply: (a: number, b: number) => number = function(a, b) {
return a * b;
};
// 箭头函数
const divide: (a: number, b: number) => number = (a, b) => {
if (b === 0) throw new Error("Division by zero");
return a / b;
};
// 简化写法(类型推断)
const subtract = (a: number, b: number): number => a - b;
函数类型别名:
// 定义函数类型别名
type Calculator = (a: number, b: number) => number;
type Predicate<T> = (item: T) => boolean;
type EventHandler = (event: Event) => void;
// 使用类型别名
const add: Calculator = (a, b) => a + b;
const isEven: Predicate<number> = (num) => num % 2 === 0;
const handleClick: EventHandler = (e) => console.log("clicked");
接口定义函数类型:
// 使用接口定义函数类型
interface SearchFunction {
(source: string, subString: string): boolean;
}
interface MathOperations {
add: (a: number, b: number) => number;
subtract: (a: number, b: number) => number;
multiply?: (a: number, b: number) => number; // 可选方法
}
// 实现接口
const search: SearchFunction = (src, sub) => src.indexOf(sub) > -1;
const math: MathOperations = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
高级函数类型:
泛型函数:
// 泛型函数定义
function identity<T>(arg: T): T {
return arg;
}
// 泛型函数类型别名
type GenericFunction<T, R> = (arg: T) => R;
type ArrayMapper<T, R> = (arr: T[]) => R[];
// 使用泛型函数
const stringIdentity = identity<string>("hello");
const numberIdentity = identity<number>(42);
// 类型推断
const autoIdentity = identity("world"); // 推断为 string
函数重载:
// 函数重载签名
function format(value: string): string;
function format(value: number): string;
function format(value: boolean): string;
// 实现签名
function format(value: string | number | boolean): string {
return String(value);
}
// 使用时有准确的类型检查
const str1 = format("hello"); // string
const str2 = format(42); // string
const str3 = format(true); // string
高阶函数:
// 接受函数作为参数
function withLogging<T extends (...args: any[]) => any>(
fn: T
): (...args: Parameters<T>) => ReturnType<T> {
return (...args) => {
console.log("Function called with:", args);
const result = fn(...args);
console.log("Function returned:", result);
return result;
};
}
// 返回函数的函数
function createValidator<T>(
validate: (value: T) => boolean
): (value: T) => { isValid: boolean; value: T } {
return (value: T) => ({
isValid: validate(value),
value
});
}
// 使用高阶函数
const loggedAdd = withLogging(add);
const emailValidator = createValidator<string>(email => email.includes("@"));
实际应用场景:
// 异步函数
async function fetchUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
// 事件处理器
type ButtonClickHandler = (event: MouseEvent) => void;
// 中间件模式
type Middleware<T> = (
data: T,
next: (data: T) => void
) => void;
// 回调函数
function processData<T, R>(
data: T[],
processor: (item: T) => R,
onComplete: (results: R[]) => void
): void {
const results = data.map(processor);
onComplete(results);
}
最佳实践:
any 类型How does TypeScript handle JavaScript files? What is the role of allowJs configuration?
How does TypeScript handle JavaScript files? What is the role of allowJs configuration?
- 考察点:TS 与 JS 混合开发理解。
答案:
TypeScript 可以与现有的 JavaScript 代码无缝集成,allowJs 配置选项控制是否允许在 TypeScript 项目中包含和编译 JavaScript 文件,支持渐进式迁移。
allowJs 配置详解:
基本配置:
// tsconfig.json
{
"compilerOptions": {
"allowJs": true, // 允许编译 JavaScript 文件
"checkJs": false, // 是否检查 JavaScript 文件中的错误
"outDir": "./dist", // 输出目录
"rootDir": "./src" // 源代码目录
},
"include": [
"src/**/*.ts",
"src/**/*.js" // 包含 JavaScript 文件
]
}
JavaScript 类型检查:
// 启用 JavaScript 类型检查
{
"compilerOptions": {
"allowJs": true,
"checkJs": true, // 对 JS 文件进行类型检查
"noImplicitAny": false, // JS 文件中允许隐式 any
"strict": false // 对 JS 文件不启用严格模式
}
}
JavaScript 文件处理方式:
JSDoc 类型注解:
// user.js - 使用 JSDoc 提供类型信息
/**
* @typedef {Object} User
* @property {number} id - 用户ID
* @property {string} name - 用户名
* @property {string} email - 邮箱
*/
/**
* 获取用户信息
* @param {number} id - 用户ID
* @returns {Promise<User>} 用户信息
*/
async function getUser(id) {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
/**
* @param {User} user - 用户对象
* @param {string} [title] - 可选标题
* @returns {string}
*/
function formatUser(user, title) {
return title ? `${title}: ${user.name}` : user.name;
}
module.exports = { getUser, formatUser };
TypeScript 中导入 JavaScript:
// types.ts - 定义类型
export interface User {
id: number;
name: string;
email: string;
}
// main.ts - 导入 JavaScript 模块
import { getUser, formatUser } from './user.js';
import type { User } from './types';
async function main() {
const user: User = await getUser(1);
const formatted: string = formatUser(user, "管理员");
console.log(formatted);
}
声明文件支持:
// user.d.ts - 为 JavaScript 文件提供类型声明
export interface User {
id: number;
name: string;
email: string;
}
export function getUser(id: number): Promise<User>;
export function formatUser(user: User, title?: string): string;
渐进式迁移策略:
项目结构:
src/
├── legacy/ # 现有 JavaScript 代码
│ ├── utils.js
│ ├── api.js
│ └── components/
├── new/ # 新的 TypeScript 代码
│ ├── services.ts
│ ├── types.ts
│ └── components/
└── types/ # 类型声明文件
├── legacy.d.ts
└── global.d.ts
迁移配置:
// tsconfig.json - 渐进式迁移配置
{
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"strict": false, // 对 JS 文件宽松检查
"noImplicitReturns": false,
"noImplicitAny": false
},
"include": ["src/**/*"],
"exclude": [
"src/legacy/old-components/**/*.js" // 暂时排除难以迁移的文件
]
}
文件级别控制:
// legacy-module.js
// @ts-check // 启用类型检查
// @ts-nocheck // 跳过类型检查
// @ts-ignore // 忽略下一行错误
/**
* @param {string} message
*/
function log(message) {
// @ts-ignore - 故意使用错误的类型用于演示
console.log(message.toUpperCase());
}
实际应用场景:
现有项目迁移:
// 步骤1:启用 allowJs
// 步骤2:逐个文件重命名 .js -> .ts
// 步骤3:添加类型注解
// 步骤4:解决类型错误
// 原 JavaScript 文件
// utils.js -> utils.ts
export function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
// TypeScript 版本
export function debounce<T extends (...args: any[]) => any>(
func: T,
delay: number
): (...args: Parameters<T>) => void {
let timeoutId: NodeJS.Timeout;
return function(...args: Parameters<T>) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
第三方库集成:
// 对于没有类型定义的 JavaScript 库
declare module 'legacy-library' {
export function doSomething(param: string): number;
export const config: {
version: string;
debug: boolean;
};
}
// 使用
import { doSomething, config } from 'legacy-library';
const result: number = doSomething("test");
console.log(config.version);
配置建议:
allowJs: falseallowJs: true,配合 checkJs 逐步检查.d.ts 声明文件描述 JavaScript 模块What is Type Assertion? What's the difference from type conversion?
What is Type Assertion? What’s the difference from type conversion?
答案:
类型断言是 TypeScript 提供的一种告诉编译器"相信我,我知道这是什么类型"的语法。它只在编译时起作用,不进行实际的类型转换或运行时检查。
类型断言语法:
两种语法形式:
// 尖括号语法(在 JSX 中不推荐)
let someValue: unknown = "hello world";
let strLength1: number = (<string>someValue).length;
// as 语法(推荐)
let strLength2: number = (someValue as string).length;
// DOM 元素断言
const inputElement = document.getElementById("username") as HTMLInputElement;
inputElement.value = "张三"; // 现在可以安全访问 value 属性
常见使用场景:
// API 响应处理
interface User {
id: number;
name: string;
email: string;
}
async function fetchUser(): Promise<User> {
const response = await fetch("/api/user");
const data = await response.json();
// 类型断言 - 假设 API 返回正确格式
return data as User;
}
// 联合类型缩窄
function processInput(input: string | number) {
if (typeof input === "string") {
// 这里不需要断言,TypeScript 已经知道是 string
return input.toUpperCase();
}
// 明确断言为 number(实际上这里也不需要)
return (input as number) * 2;
}
双重断言:
// 有时需要先断言为 unknown 再断言为目标类型
const input = "123";
// 直接断言可能报错
// const num = input as number; // Error: 不能直接从 string 断言为 number
// 双重断言(不推荐,但有时必要)
const num = (input as unknown) as number;
// 更好的方式是实际转换
const actualNum = parseInt(input, 10);
类型断言 vs 类型转换:
类型断言(编译时):
// 类型断言 - 仅告诉 TypeScript 编译器类型
let value: unknown = "42";
let num1 = value as number;
console.log(typeof num1); // "string" - 运行时仍是字符串
console.log(num1.toFixed(2)); // 运行时错误!
// 类型断言不会改变运行时的值
interface Cat {
meow(): void;
}
let pet: any = { bark: () => console.log("woof") };
let cat = pet as Cat;
// cat.meow(); // 运行时错误 - 对象实际上没有 meow 方法
类型转换(运行时):
// 实际的类型转换 - 改变运行时的值
let str = "42";
let num2 = Number(str); // 实际转换
let num3 = parseInt(str, 10); // 实际转换
let num4 = +str; // 实际转换
console.log(typeof num2); // "number" - 运行时确实是数字
console.log(num2.toFixed(2)); // "42.00" - 正常工作
// 对象转换
class Animal {
constructor(public name: string) {}
}
class Dog extends Animal {
bark() { console.log("woof"); }
}
let animal: Animal = new Dog("旺财");
// 实际检查类型后转换
if (animal instanceof Dog) {
animal.bark(); // 安全,因为进行了运行时检查
}
安全的类型断言实践:
类型守卫结合断言:
// 自定义类型守卫
function isUser(obj: any): obj is User {
return obj &&
typeof obj.id === 'number' &&
typeof obj.name === 'string' &&
typeof obj.email === 'string';
}
// 安全使用
async function safeGetUser(): Promise<User | null> {
const response = await fetch("/api/user");
const data = await response.json();
if (isUser(data)) {
return data; // TypeScript 知道这是 User 类型
}
return null; // 数据格式不正确
}
非空断言(!):
// 非空断言操作符
function processElement(id: string) {
// 明确知道元素存在时使用
const element = document.getElementById(id)!; // 断言不为 null
element.style.display = "none";
}
// 可选链更安全
function safeProcessElement(id: string) {
const element = document.getElementById(id);
if (element) {
element.style.display = "none";
}
}
使用建议:
as 语法而不是尖括号语法How to use optional properties and readonly properties in TypeScript?
How to use optional properties and readonly properties in TypeScript?
答案:
可选属性(Optional Properties)和只读属性(Readonly Properties)是 TypeScript 中控制对象属性访问和修改的重要特性,提供了更精确的类型约束和数据保护。
可选属性(?):
基础语法:
interface User {
id: number; // 必需属性
name: string; // 必需属性
email?: string; // 可选属性
phone?: string; // 可选属性
profile?: { // 嵌套可选属性
avatar?: string;
bio?: string;
};
}
// 有效的对象创建
const user1: User = {
id: 1,
name: "张三"
// email 和 phone 可以省略
};
const user2: User = {
id: 2,
name: "李四",
email: "[email protected]",
profile: {
avatar: "/avatar.jpg"
// bio 可以省略
}
};
函数参数中的可选属性:
// 配置对象模式
interface ApiConfig {
baseUrl: string;
timeout?: number;
retries?: number;
headers?: Record<string, string>;
}
function createApiClient(config: ApiConfig) {
const {
baseUrl,
timeout = 5000, // 默认值
retries = 3, // 默认值
headers = {} // 默认值
} = config;
return {
baseUrl,
timeout,
retries,
headers
};
}
// 使用时只需提供必要属性
const api = createApiClient({
baseUrl: "https://api.example.com"
});
只读属性(readonly):
接口中的只读属性:
interface Point {
readonly x: number;
readonly y: number;
}
interface Config {
readonly apiKey: string;
readonly debug: boolean;
features?: readonly string[]; // 只读数组
}
const point: Point = { x: 10, y: 20 };
// point.x = 30; // 错误:无法修改只读属性
const config: Config = {
apiKey: "secret-key",
debug: true,
features: ["auth", "cache"]
};
// config.apiKey = "new-key"; // 错误:无法修改只读属性
类中的只读属性:
class Circle {
readonly pi: number = 3.14159;
readonly radius: number;
constructor(radius: number) {
this.radius = radius; // 构造函数中可以赋值
}
get area(): number {
return this.pi * this.radius ** 2;
}
// setRadius(newRadius: number) {
// this.radius = newRadius; // 错误:无法修改只读属性
// }
}
const circle = new Circle(5);
console.log(circle.area);
// circle.pi = 3.14; // 错误:无法修改只读属性
组合使用:
可选且只读的属性:
interface UserPreferences {
readonly id: number;
readonly userId: number;
theme?: readonly ("light" | "dark");
readonly createdAt?: Date; // 可选的只读属性
settings?: {
readonly notifications: boolean;
language?: string;
};
}
const preferences: UserPreferences = {
id: 1,
userId: 123,
theme: "dark",
settings: {
notifications: true,
language: "zh-CN"
}
};
// preferences.id = 2; // 错误:只读
// preferences.theme = "light"; // 错误:只读
// preferences.settings.notifications = false; // 错误:只读
preferences.settings.language = "en"; // 正确:language 不是只读
工具类型的应用:
// 使用内置工具类型
interface Product {
id: number;
name: string;
price: number;
description: string;
}
// 全部属性可选
type PartialProduct = Partial<Product>;
/*
{
id?: number;
name?: string;
price?: number;
description?: string;
}
*/
// 全部属性只读
type ReadonlyProduct = Readonly<Product>;
/*
{
readonly id: number;
readonly name: string;
readonly price: number;
readonly description: string;
}
*/
// 选择特定属性为可选
type ProductUpdate = Partial<Pick<Product, 'name' | 'price' | 'description'>> &
Pick<Product, 'id'>;
/*
{
id: number;
name?: string;
price?: number;
description?: string;
}
*/
实际应用场景:
状态管理:
interface AppState {
readonly user: User | null;
readonly isLoading: boolean;
readonly error?: string;
preferences?: UserPreferences;
}
// 状态更新函数
function updateState(
currentState: AppState,
updates: Partial<AppState>
): AppState {
return {
...currentState,
...updates
};
}
API 响应类型:
interface ApiResponse<T> {
readonly success: boolean;
readonly data?: T;
readonly error?: {
readonly code: string;
readonly message: string;
};
readonly timestamp: Date;
}
interface CreateUserRequest {
name: string;
email: string;
phone?: string; // 可选字段
}
interface UserResponse {
readonly id: number; // 服务器生成,只读
readonly name: string;
readonly email: string;
readonly phone?: string;
readonly createdAt: Date; // 服务器时间戳,只读
}
配置对象:
interface DatabaseConfig {
readonly host: string;
readonly port: number;
readonly database: string;
readonly ssl?: boolean;
readonly timeout?: number;
readonly pool?: {
readonly min: number;
readonly max: number;
readonly idleTimeout?: number;
};
}
// 运行时不可修改的配置
const dbConfig: DatabaseConfig = {
host: "localhost",
port: 5432,
database: "myapp",
ssl: true,
pool: {
min: 2,
max: 10
}
};
最佳实践:
readonly? 可选标记What are Generics? How to use generic functions and generic interfaces?
What are Generics? How to use generic functions and generic interfaces?
答案:
泛型(Generics)是 TypeScript 中实现代码重用和类型安全的重要特性,允许在定义函数、接口或类时使用类型参数,在使用时再指定具体类型。
泛型基础概念:
泛型函数:
// 基础泛型函数
function identity<T>(arg: T): T {
return arg;
}
// 使用时指定类型
const numberResult = identity<number>(42); // T = number
const stringResult = identity<string>("hello"); // T = string
// 类型推断
const autoResult = identity("world"); // 自动推断 T = string
// 多个类型参数
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
const coords = pair<number, number>(10, 20); // [number, number]
const userInfo = pair("张三", 25); // [string, number] - 推断
泛型接口:
// 基础泛型接口
interface Container<T> {
value: T;
getValue(): T;
setValue(value: T): void;
}
// 实现泛型接口
class NumberContainer implements Container<number> {
constructor(public value: number) {}
getValue(): number {
return this.value;
}
setValue(value: number): void {
this.value = value;
}
}
class StringContainer implements Container<string> {
constructor(public value: string) {}
getValue(): string {
return this.value;
}
setValue(value: string): void {
this.value = value;
}
}
复杂泛型应用:
API 响应泛型:
// 通用 API 响应接口
interface ApiResponse<T> {
success: boolean;
data: T;
message?: string;
errors?: string[];
}
interface User {
id: number;
name: string;
email: string;
}
interface Product {
id: number;
name: string;
price: number;
}
// 使用泛型接口
type UserResponse = ApiResponse<User>;
type ProductListResponse = ApiResponse<Product[]>;
type LoginResponse = ApiResponse<{ token: string; user: User }>;
// 泛型 API 函数
async function apiCall<T>(url: string): Promise<ApiResponse<T>> {
const response = await fetch(url);
return response.json() as ApiResponse<T>;
}
// 使用
const userResponse = await apiCall<User>("/api/users/1");
const products = await apiCall<Product[]>("/api/products");
数据操作泛型:
// 泛型数组操作
interface Repository<T> {
items: T[];
add(item: T): void;
remove(id: string | number): boolean;
find(predicate: (item: T) => boolean): T | undefined;
filter(predicate: (item: T) => boolean): T[];
map<R>(mapper: (item: T) => R): R[];
}
class InMemoryRepository<T extends { id: string | number }> implements Repository<T> {
public items: T[] = [];
add(item: T): void {
this.items.push(item);
}
remove(id: string | number): boolean {
const index = this.items.findIndex(item => item.id === id);
if (index > -1) {
this.items.splice(index, 1);
return true;
}
return false;
}
find(predicate: (item: T) => boolean): T | undefined {
return this.items.find(predicate);
}
filter(predicate: (item: T) => boolean): T[] {
return this.items.filter(predicate);
}
map<R>(mapper: (item: T) => R): R[] {
return this.items.map(mapper);
}
}
// 使用
const userRepository = new InMemoryRepository<User>();
userRepository.add({ id: 1, name: "张三", email: "[email protected]" });
const productRepository = new InMemoryRepository<Product>();
productRepository.add({ id: 1, name: "笔记本", price: 5999 });
高级泛型特性:
泛型约束:
// 约束泛型必须有特定属性
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): T {
console.log(`Length: ${arg.length}`);
return arg;
}
logLength("hello"); // 正确:string 有 length
logLength([1, 2, 3]); // 正确:array 有 length
// logLength(123); // 错误:number 没有 length
// 键约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { id: 1, name: "张三", age: 25 };
const userName = getProperty(user, "name"); // string
const userId = getProperty(user, "id"); // number
// const invalid = getProperty(user, "salary"); // 错误:'salary' 不存在
条件类型与泛型:
// 条件类型
type NonNull<T> = T extends null | undefined ? never : T;
type ArrayElement<T> = T extends (infer U)[] ? U : never;
// 实用工具类型
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
type RequiredFields<T, K extends keyof T> = T & Required<Pick<T, K>>;
// 使用示例
interface CreateUserRequest {
name: string;
email: string;
age?: number;
phone?: string;
}
// 使age字段必需,其他保持原样
type CreateUserWithAge = RequiredFields<CreateUserRequest, 'age'>;
实际项目应用:
状态管理:
// 泛型状态管理
interface State<T> {
data: T | null;
loading: boolean;
error: string | null;
}
interface Actions<T> {
setLoading(): void;
setData(data: T): void;
setError(error: string): void;
reset(): void;
}
function createStore<T>(): [State<T>, Actions<T>] {
let state: State<T> = {
data: null,
loading: false,
error: null
};
const actions: Actions<T> = {
setLoading: () => { state.loading = true; },
setData: (data: T) => {
state.data = data;
state.loading = false;
state.error = null;
},
setError: (error: string) => {
state.error = error;
state.loading = false;
},
reset: () => {
state = { data: null, loading: false, error: null };
}
};
return [state, actions];
}
// 使用
const [userState, userActions] = createStore<User>();
const [productState, productActions] = createStore<Product[]>();
表单处理:
// 泛型表单处理
interface FormField<T> {
value: T;
error?: string;
touched: boolean;
validate?: (value: T) => string | undefined;
}
type FormState<T> = {
[K in keyof T]: FormField<T[K]>;
};
interface LoginForm {
email: string;
password: string;
}
const loginFormState: FormState<LoginForm> = {
email: {
value: "",
touched: false,
validate: (email) => email.includes("@") ? undefined : "Invalid email"
},
password: {
value: "",
touched: false,
validate: (pwd) => pwd.length >= 6 ? undefined : "Password too short"
}
};
最佳实践:
What are Generic Constraints? How to use the extends keyword?
What are Generic Constraints? How to use the extends keyword?
答案:
泛型约束(Generic Constraints)通过 extends 关键字限制泛型参数必须符合特定条件,提供了更精确的类型控制和更好的类型安全性。
基础泛型约束:
接口约束:
// 约束泛型必须具有特定属性
interface HasLength {
length: number;
}
function logWithLength<T extends HasLength>(arg: T): T {
console.log(`Item length: ${arg.length}`);
return arg;
}
// 有效调用
logWithLength("hello"); // string 有 length 属性
logWithLength([1, 2, 3]); // array 有 length 属性
logWithLength({ length: 5 }); // 对象有 length 属性
// 无效调用
// logWithLength(123); // 错误:number 没有 length 属性
// logWithLength({ name: "test" }); // 错误:对象没有 length 属性
keyof 约束:
// 约束泛型 K 必须是 T 的属性键
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = {
id: 1,
name: "张三",
age: 25,
email: "[email protected]"
};
// 类型安全的属性访问
const name = getProperty(person, "name"); // string
const age = getProperty(person, "age"); // number
const id = getProperty(person, "id"); // number
// 编译时错误防护
// const invalid = getProperty(person, "salary"); // 错误:'salary' 不存在于类型中
// 批量获取属性
function pick<T, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K> {
const result = {} as Pick<T, K>;
keys.forEach(key => {
result[key] = obj[key];
});
return result;
}
const userInfo = pick(person, "name", "email"); // { name: string; email: string }
高级约束模式:
条件约束:
// 约束 T 必须是数组类型
type ArrayElement<T extends readonly unknown[]> = T extends readonly (infer U)[] ? U : never;
type StringArray = ArrayElement<string[]>; // string
type NumberArray = ArrayElement<number[]>; // number
// type Invalid = ArrayElement<string>; // 错误:string 不是数组
// 函数参数约束
function processArray<T extends readonly unknown[]>(
arr: T,
processor: (item: ArrayElement<T>) => void
): void {
arr.forEach(processor);
}
processArray([1, 2, 3], (num) => console.log(num * 2)); // num: number
processArray(["a", "b"], (str) => console.log(str.toUpperCase())); // str: string
多重约束:
// 同时满足多个约束
interface Serializable {
serialize(): string;
}
interface Timestamped {
timestamp: Date;
}
function processData<T extends Serializable & Timestamped>(data: T): string {
const serialized = data.serialize();
const time = data.timestamp.toISOString();
return `${time}: ${serialized}`;
}
class LogEntry implements Serializable, Timestamped {
constructor(
public message: string,
public timestamp: Date = new Date()
) {}
serialize(): string {
return JSON.stringify({
message: this.message,
timestamp: this.timestamp
});
}
}
const entry = new LogEntry("用户登录");
const result = processData(entry); // 正确:LogEntry 满足所有约束
实际应用场景:
数据库操作约束:
// 约束实体必须有 id 属性
interface Entity {
id: string | number;
}
interface Repository<T extends Entity> {
save(entity: T): Promise<T>;
findById(id: T['id']): Promise<T | null>;
delete(id: T['id']): Promise<boolean>;
update(id: T['id'], updates: Partial<T>): Promise<T>;
}
interface User extends Entity {
id: number;
name: string;
email: string;
}
interface Product extends Entity {
id: string;
name: string;
price: number;
}
class DatabaseRepository<T extends Entity> implements Repository<T> {
async save(entity: T): Promise<T> {
// 数据库保存逻辑
console.log(`Saving entity with id: ${entity.id}`);
return entity;
}
async findById(id: T['id']): Promise<T | null> {
// 数据库查询逻辑
console.log(`Finding entity with id: ${id}`);
return null;
}
async delete(id: T['id']): Promise<boolean> {
console.log(`Deleting entity with id: ${id}`);
return true;
}
async update(id: T['id'], updates: Partial<T>): Promise<T> {
console.log(`Updating entity ${id} with:`, updates);
return {} as T;
}
}
const userRepo = new DatabaseRepository<User>();
const productRepo = new DatabaseRepository<Product>();
事件系统约束:
// 约束事件类型
interface BaseEvent {
type: string;
timestamp: Date;
}
interface UserEvent extends BaseEvent {
userId: string;
}
interface SystemEvent extends BaseEvent {
level: 'info' | 'warning' | 'error';
}
// 约束事件处理器
type EventHandler<T extends BaseEvent> = (event: T) => void;
class EventManager<T extends BaseEvent> {
private handlers: Map<string, EventHandler<T>[]> = new Map();
on(eventType: T['type'], handler: EventHandler<T>): void {
if (!this.handlers.has(eventType)) {
this.handlers.set(eventType, []);
}
this.handlers.get(eventType)!.push(handler);
}
emit(event: T): void {
const handlers = this.handlers.get(event.type) || [];
handlers.forEach(handler => handler(event));
}
}
// 使用
const userEventManager = new EventManager<UserEvent>();
userEventManager.on('user.login', (event) => {
console.log(`User ${event.userId} logged in at ${event.timestamp}`);
});
API 客户端约束:
// 约束 API 请求和响应类型
interface ApiEndpoint {
path: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
}
interface GetEndpoint extends ApiEndpoint {
method: 'GET';
}
interface PostEndpoint extends ApiEndpoint {
method: 'POST';
}
// 根据请求方法约束参数
type RequestConfig<T extends ApiEndpoint> = T extends GetEndpoint
? { params?: Record<string, any> }
: T extends PostEndpoint
? { body: any; params?: Record<string, any> }
: { params?: Record<string, any>; body?: any };
async function apiRequest<
TEndpoint extends ApiEndpoint,
TResponse
>(
endpoint: TEndpoint,
config: RequestConfig<TEndpoint>
): Promise<TResponse> {
// API 请求逻辑
console.log(`${endpoint.method} ${endpoint.path}`, config);
return {} as TResponse;
}
// 类型安全的 API 调用
const getUsers = await apiRequest<GetEndpoint, User[]>(
{ path: '/users', method: 'GET' },
{ params: { page: 1 } } // 只能传 params
);
const createUser = await apiRequest<PostEndpoint, User>(
{ path: '/users', method: 'POST' },
{ body: { name: '张三', email: '[email protected]' } } // 必须传 body
);
约束的嵌套和组合:
// 复杂约束组合
interface Comparable<T> {
compareTo(other: T): number;
}
interface Cloneable<T> {
clone(): T;
}
// 多重约束
function sortAndClone<T extends Comparable<T> & Cloneable<T>>(
items: T[]
): T[] {
return items
.map(item => item.clone()) // 必须实现 Cloneable
.sort((a, b) => a.compareTo(b)); // 必须实现 Comparable
}
// 条件约束
type NonNullable<T> = T extends null | undefined ? never : T;
type RequiredKeys<T> = {
[K in keyof T]-?: T[K] extends undefined ? never : K;
}[keyof T];
// 实用工具类型
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
最佳实践:
How does TypeScript implement class inheritance and interface implementation?
How does TypeScript implement class inheritance and interface implementation?
答案:
TypeScript 支持完整的面向对象编程,提供了类继承(extends)和接口实现(implements)机制,实现代码复用和类型约束。
类继承(extends):
// 基础类
class Animal {
protected name: string;
protected age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
public speak(): void {
console.log(`${this.name} makes a sound`);
}
protected getInfo(): string {
return `${this.name} is ${this.age} years old`;
}
}
// 继承类
class Dog extends Animal {
private breed: string;
constructor(name: string, age: number, breed: string) {
super(name, age); // 调用父类构造函数
this.breed = breed;
}
// 重写父类方法
public speak(): void {
console.log(`${this.name} barks loudly!`);
}
// 新增方法
public wagTail(): void {
console.log(`${this.name} wags tail happily`);
}
// 访问受保护的父类方法
public showInfo(): void {
console.log(this.getInfo() + `, breed: ${this.breed}`);
}
}
const dog = new Dog("旺财", 3, "金毛");
dog.speak(); // "旺财 barks loudly!"
dog.wagTail(); // "旺财 wags tail happily"
dog.showInfo(); // "旺财 is 3 years old, breed: 金毛"
接口实现(implements):
// 定义接口
interface Flyable {
fly(): void;
altitude: number;
}
interface Swimmable {
swim(): void;
depth: number;
}
// 实现接口
class Bird implements Flyable {
altitude: number = 0;
fly(): void {
this.altitude = 100;
console.log(`Flying at ${this.altitude} meters`);
}
}
// 多接口实现
class Duck extends Animal implements Flyable, Swimmable {
altitude: number = 0;
depth: number = 0;
fly(): void {
this.altitude = 50;
console.log(`${this.name} flies at ${this.altitude}m`);
}
swim(): void {
this.depth = 2;
console.log(`${this.name} swims at ${this.depth}m depth`);
}
speak(): void {
console.log(`${this.name} quacks`);
}
}
const duck = new Duck("小黄", 2);
duck.speak(); // "小黄 quacks"
duck.fly(); // "小黄 flies at 50m"
duck.swim(); // "小黄 swims at 2m depth"
复杂继承场景:
// 抽象基类
abstract class Shape {
protected x: number;
protected y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
// 抽象方法 - 子类必须实现
abstract calculateArea(): number;
abstract draw(): void;
// 具体方法 - 子类可以使用
public move(dx: number, dy: number): void {
this.x += dx;
this.y += dy;
console.log(`Moved to (${this.x}, ${this.y})`);
}
public getPosition(): [number, number] {
return [this.x, this.y];
}
}
// 继承抽象类
class Circle extends Shape {
constructor(x: number, y: number, private radius: number) {
super(x, y);
}
calculateArea(): number {
return Math.PI * this.radius ** 2;
}
draw(): void {
console.log(`Drawing circle at (${this.x}, ${this.y}) with radius ${this.radius}`);
}
}
class Rectangle extends Shape {
constructor(
x: number,
y: number,
private width: number,
private height: number
) {
super(x, y);
}
calculateArea(): number {
return this.width * this.height;
}
draw(): void {
console.log(`Drawing rectangle at (${this.x}, ${this.y}) ${this.width}x${this.height}`);
}
}
接口与类的组合应用:
// Mixin 接口
interface Timestamped {
timestamp: Date;
updateTimestamp(): void;
}
interface Loggable {
log(message: string): void;
}
// Mixin 实现函数
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
if (name !== 'constructor') {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(baseCtor.prototype, name) || Object.create(null)
);
}
});
});
}
// Mixin 类
class TimestampedMixin implements Timestamped {
timestamp: Date = new Date();
updateTimestamp(): void {
this.timestamp = new Date();
}
}
class LoggableMixin implements Loggable {
log(message: string): void {
console.log(`[${new Date().toISOString()}] ${message}`);
}
}
// 使用 Mixin 的类
class User implements Timestamped, Loggable {
constructor(public name: string, public email: string) {}
// 这些方法会通过 applyMixins 自动添加
timestamp!: Date;
updateTimestamp!: () => void;
log!: (message: string) => void;
}
// 应用 Mixins
applyMixins(User, [TimestampedMixin, LoggableMixin]);
实际应用场景:
// 基础组件接口
interface Component {
render(): string;
mount(element: HTMLElement): void;
unmount(): void;
}
interface EventEmitter {
on(event: string, callback: Function): void;
emit(event: string, ...args: any[]): void;
}
// 基础组件类
abstract class BaseComponent implements Component, EventEmitter {
protected element?: HTMLElement;
private events: Map<string, Function[]> = new Map();
abstract render(): string;
mount(element: HTMLElement): void {
this.element = element;
element.innerHTML = this.render();
this.onMounted();
}
unmount(): void {
if (this.element) {
this.element.innerHTML = '';
this.element = undefined;
}
this.onUnmounted();
}
on(event: string, callback: Function): void {
if (!this.events.has(event)) {
this.events.set(event, []);
}
this.events.get(event)!.push(callback);
}
emit(event: string, ...args: any[]): void {
const callbacks = this.events.get(event) || [];
callbacks.forEach(callback => callback(...args));
}
protected onMounted(): void {}
protected onUnmounted(): void {}
}
// 具体组件
class Button extends BaseComponent {
constructor(private text: string, private onClick?: () => void) {
super();
}
render(): string {
return `<button id="btn">${this.text}</button>`;
}
protected onMounted(): void {
const button = this.element?.querySelector('#btn');
if (button && this.onClick) {
button.addEventListener('click', this.onClick);
}
}
}
最佳实践:
What are namespaces and modules?
What are namespaces and modules?
答案:
命名空间和模块是 TypeScript 中两种不同的代码组织方式。模块是现代 JavaScript 的标准,而命名空间是 TypeScript 早期的内部模块系统。
命名空间(namespace):
基础命名空间:
// 命名空间定义
namespace Geometry {
export interface Point {
x: number;
y: number;
}
export class Circle {
constructor(public center: Point, public radius: number) {}
area(): number {
return Math.PI * this.radius ** 2;
}
}
export function distance(p1: Point, p2: Point): number {
return Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
}
}
// 使用命名空间
const point1: Geometry.Point = { x: 0, y: 0 };
const circle = new Geometry.Circle(point1, 5);
命名空间合并:
// 跨文件命名空间合并
namespace App {
export interface User {
id: number;
name: string;
}
}
namespace App {
export class UserService {
getUser(id: number): User {
return { id, name: "用户" + id };
}
}
}
// 合并后可以一起使用
const service = new App.UserService();
const user: App.User = service.getUser(1);
模块(ES6 Modules):
模块导出:
// geometry.ts
export interface Point {
x: number;
y: number;
}
export class Circle {
constructor(public center: Point, public radius: number) {}
area(): number {
return Math.PI * this.radius ** 2;
}
}
export function distance(p1: Point, p2: Point): number {
return Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
}
// 默认导出
export default class Rectangle {
constructor(public width: number, public height: number) {}
}
模块导入:
// main.ts
import Rectangle, { Point, Circle, distance } from './geometry';
import * as Geo from './geometry'; // 命名空间导入
const point1: Point = { x: 0, y: 0 };
const circle = new Circle(point1, 5);
const rect = new Rectangle(10, 20);
主要区别:
What are decorators in TypeScript? What are the common types of decorators?
What are decorators in TypeScript? What are the common types of decorators?
答案:
装饰器(Decorator)是 TypeScript 中用于修改类、方法、属性或参数行为的特殊声明,提供了声明式编程和元编程能力,需要在 tsconfig.json 中启用 experimentalDecorators。
装饰器配置:
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
装饰器类型:
类装饰器(Class Decorator):
// 基础类装饰器
function Sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@Sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return `Hello, ${this.greeting}`;
}
}
// 带参数的类装饰器工厂
function Entity(tableName: string) {
return function<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
tableName = tableName;
save() {
console.log(`Saving to ${tableName} table`);
}
};
};
}
@Entity('users')
class User {
constructor(public name: string) {}
}
const user = new User("张三");
(user as any).save(); // "Saving to users table"
方法装饰器(Method Decorator):
// 方法装饰器
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with arguments:`, args);
const result = originalMethod.apply(this, args);
console.log(`Method ${propertyKey} returned:`, result);
return result;
};
}
// 性能监控装饰器
function Performance(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
const start = Date.now();
const result = originalMethod.apply(this, args);
const end = Date.now();
console.log(`${propertyKey} executed in ${end - start}ms`);
return result;
};
}
class Calculator {
@Log
@Performance
add(a: number, b: number): number {
return a + b;
}
@Log
multiply(a: number, b: number): number {
return a * b;
}
}
const calc = new Calculator();
calc.add(5, 3); // 会输出日志和性能信息
属性装饰器(Property Decorator):
// 属性验证装饰器
function Required(target: any, propertyKey: string) {
let value: any;
const getter = () => value;
const setter = (newVal: any) => {
if (newVal === null || newVal === undefined || newVal === '') {
throw new Error(`${propertyKey} is required`);
}
value = newVal;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
// 格式化装饰器
function Format(formatter: (value: any) => any) {
return function(target: any, propertyKey: string) {
let value: any;
const getter = () => value;
const setter = (newVal: any) => {
value = formatter(newVal);
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
}
class UserProfile {
@Required
public name: string = '';
@Format(email => email.toLowerCase().trim())
public email: string = '';
constructor(name: string, email: string) {
this.name = name;
this.email = email;
}
}
const profile = new UserProfile("张三", " [email protected] ");
console.log(profile.email); // "[email protected]"
参数装饰器(Parameter Decorator):
// 参数验证装饰器
function Validate(target: any, methodName: string, parameterIndex: number) {
const existingRequiredParameters: number[] =
Reflect.getOwnMetadata('required_parameters', target, methodName) || [];
existingRequiredParameters.push(parameterIndex);
Reflect.defineMetadata('required_parameters', existingRequiredParameters, target, methodName);
}
// 方法装饰器配合参数装饰器
function ValidateMethod(target: any, methodName: string, descriptor: PropertyDescriptor) {
const method = descriptor.value;
descriptor.value = function(...args: any[]) {
const requiredParameters: number[] =
Reflect.getOwnMetadata('required_parameters', target, methodName) || [];
for (const parameterIndex of requiredParameters) {
if (args[parameterIndex] === null || args[parameterIndex] === undefined) {
throw new Error(`Parameter at index ${parameterIndex} is required`);
}
}
return method.apply(this, args);
};
}
class UserService {
@ValidateMethod
createUser(@Validate name: string, @Validate email: string, age?: number) {
return { name, email, age };
}
}
实际应用场景:
API 路由装饰器:
// HTTP 方法装饰器
function Get(path: string) {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
Reflect.defineMetadata('http:method', 'GET', target, propertyKey);
Reflect.defineMetadata('http:path', path, target, propertyKey);
};
}
function Post(path: string) {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
Reflect.defineMetadata('http:method', 'POST', target, propertyKey);
Reflect.defineMetadata('http:path', path, target, propertyKey);
};
}
// 控制器装饰器
function Controller(basePath: string) {
return function<T extends { new(...args: any[]): {} }>(constructor: T) {
Reflect.defineMetadata('controller:basePath', basePath, constructor);
return constructor;
};
}
@Controller('/api/users')
class UserController {
@Get('/')
getAllUsers() {
return { users: [] };
}
@Post('/')
createUser() {
return { message: 'User created' };
}
@Get('/:id')
getUser() {
return { user: {} };
}
}
依赖注入装饰器:
// 注入装饰器
const INJECTION_TOKENS = new Map<string, any>();
function Injectable<T extends { new(...args: any[]): {} }>(constructor: T) {
INJECTION_TOKENS.set(constructor.name, constructor);
return constructor;
}
function Inject(token: string) {
return function(target: any, propertyKey: string | symbol | undefined, parameterIndex: number) {
const existingTokens = Reflect.getOwnMetadata('inject:tokens', target) || [];
existingTokens[parameterIndex] = token;
Reflect.defineMetadata('inject:tokens', existingTokens, target);
};
}
@Injectable
class DatabaseService {
connect() {
console.log('Connected to database');
}
}
@Injectable
class LoggerService {
log(message: string) {
console.log(`[LOG] ${message}`);
}
}
@Injectable
class UserService {
constructor(
@Inject('DatabaseService') private db: DatabaseService,
@Inject('LoggerService') private logger: LoggerService
) {}
createUser(name: string) {
this.logger.log(`Creating user: ${name}`);
this.db.connect();
}
}
缓存装饰器:
// 缓存装饰器
function Cache(expireTime: number = 60000) {
const cache = new Map<string, { value: any; expiry: number }>();
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
const cacheKey = `${propertyKey}_${JSON.stringify(args)}`;
const cached = cache.get(cacheKey);
if (cached && Date.now() < cached.expiry) {
console.log(`Cache hit for ${propertyKey}`);
return cached.value;
}
const result = originalMethod.apply(this, args);
cache.set(cacheKey, {
value: result,
expiry: Date.now() + expireTime
});
console.log(`Cache miss for ${propertyKey}, result cached`);
return result;
};
};
}
class ApiService {
@Cache(30000) // 缓存30秒
async fetchUserData(userId: number) {
console.log(`Fetching data for user ${userId}`);
// 模拟 API 调用
return { id: userId, name: `User ${userId}` };
}
}
装饰器组合与执行顺序:
// 装饰器执行顺序示例
function First() {
console.log("First(): factory evaluated");
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("First(): called");
};
}
function Second() {
console.log("Second(): factory evaluated");
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("Second(): called");
};
}
class ExampleClass {
@First()
@Second()
method() {}
}
// 输出顺序:
// First(): factory evaluated
// Second(): factory evaluated
// Second(): called
// First(): called
最佳实践:
experimentalDecorators 和 emitDecoratorMetadataWhat are Type Guards? What are the implementation methods?
What are Type Guards? What are the implementation methods?
答案:
类型守卫(Type Guard)是 TypeScript 中在运行时检查类型并缩窄类型范围的机制,让编译器能够在特定代码块中推断出更精确的类型。
内置类型守卫:
typeof 类型守卫:
function processValue(value: string | number) {
if (typeof value === "string") {
// 在这个块中,value 被缩窄为 string 类型
return value.toUpperCase();
}
// 这里 value 自动被缩窄为 number 类型
return value.toFixed(2);
}
// 多类型检查
function formatValue(input: string | number | boolean) {
if (typeof input === "string") {
return `"${input}"`; // input: string
} else if (typeof input === "number") {
return input.toString(); // input: number
} else {
return input ? "true" : "false"; // input: boolean
}
}
instanceof 类型守卫:
class Dog {
bark() { console.log("Woof!"); }
}
class Cat {
meow() { console.log("Meow!"); }
}
function handlePet(pet: Dog | Cat) {
if (pet instanceof Dog) {
pet.bark(); // pet: Dog
} else {
pet.meow(); // pet: Cat
}
}
// 错误处理中的应用
function handleError(error: unknown) {
if (error instanceof Error) {
console.log(error.message); // error: Error
} else if (typeof error === "string") {
console.log(error); // error: string
} else {
console.log("Unknown error occurred");
}
}
自定义类型守卫:
用户定义的类型守卫函数:
// 使用 is 关键字
interface Fish {
swim(): void;
}
interface Bird {
fly(): void;
}
// 自定义类型守卫函数
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function handleAnimal(animal: Fish | Bird) {
if (isFish(animal)) {
animal.swim(); // animal: Fish
} else {
animal.fly(); // animal: Bird
}
}
// API 响应类型守卫
interface User {
id: number;
name: string;
email: string;
}
function isUser(obj: any): obj is User {
return obj &&
typeof obj.id === 'number' &&
typeof obj.name === 'string' &&
typeof obj.email === 'string';
}
async function fetchUser(): Promise<User | null> {
const response = await fetch('/api/user');
const data = await response.json();
if (isUser(data)) {
return data; // data: User
}
return null; // 数据格式不正确
}
in 操作符类型守卫:
interface Car {
drive(): void;
wheels: number;
}
interface Boat {
sail(): void;
hull: string;
}
function operateVehicle(vehicle: Car | Boat) {
if ('drive' in vehicle) {
vehicle.drive(); // vehicle: Car
console.log(`Car has ${vehicle.wheels} wheels`);
} else {
vehicle.sail(); // vehicle: Boat
console.log(`Boat has ${vehicle.hull} hull`);
}
}
// 复杂对象检查
interface ApiSuccess {
success: true;
data: any;
}
interface ApiError {
success: false;
error: string;
}
type ApiResponse = ApiSuccess | ApiError;
function handleApiResponse(response: ApiResponse) {
if ('data' in response) {
// response: ApiSuccess
console.log("Success:", response.data);
} else {
// response: ApiError
console.log("Error:", response.error);
}
}
discriminated union 类型守卫:
// 判别联合类型
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
type Shape = Circle | Square | Rectangle;
function calculateArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
// shape: Circle
return Math.PI * shape.radius ** 2;
case "square":
// shape: Square
return shape.size ** 2;
case "rectangle":
// shape: Rectangle
return shape.width * shape.height;
default:
// 确保所有情况都被处理
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
// 事件系统应用
interface ClickEvent {
type: "click";
target: HTMLElement;
clientX: number;
clientY: number;
}
interface KeyboardEvent {
type: "keydown" | "keyup";
target: HTMLElement;
key: string;
ctrlKey: boolean;
}
type UIEvent = ClickEvent | KeyboardEvent;
function handleUIEvent(event: UIEvent) {
switch (event.type) {
case "click":
// event: ClickEvent
console.log(`Clicked at (${event.clientX}, ${event.clientY})`);
break;
case "keydown":
case "keyup":
// event: KeyboardEvent
console.log(`Key ${event.key} ${event.type}`);
if (event.ctrlKey) {
console.log("Ctrl modifier was pressed");
}
break;
}
}
高级类型守卫应用:
// 泛型类型守卫
function isArrayOf<T>(
arr: unknown,
guard: (item: unknown) => item is T
): arr is T[] {
return Array.isArray(arr) && arr.every(guard);
}
function isNumber(value: unknown): value is number {
return typeof value === 'number';
}
function isString(value: unknown): value is string {
return typeof value === 'string';
}
// 使用
const unknownData: unknown = [1, 2, 3, 4];
if (isArrayOf(unknownData, isNumber)) {
// unknownData: number[]
const sum = unknownData.reduce((a, b) => a + b, 0);
}
// 条件类型与类型守卫结合
type NonNullable<T> = T extends null | undefined ? never : T;
function isNotNull<T>(value: T): value is NonNullable<T> {
return value !== null && value !== undefined;
}
// 使用示例
function processUsers(users: (User | null)[]) {
const validUsers = users.filter(isNotNull); // validUsers: User[]
validUsers.forEach(user => {
console.log(user.name); // 安全访问,user 不可能是 null
});
}
// 复杂类型检查
interface LoadingState {
status: 'loading';
}
interface SuccessState {
status: 'success';
data: any;
}
interface ErrorState {
status: 'error';
error: string;
}
type AsyncState = LoadingState | SuccessState | ErrorState;
function isSuccessState(state: AsyncState): state is SuccessState {
return state.status === 'success';
}
function isErrorState(state: AsyncState): state is ErrorState {
return state.status === 'error';
}
function handleAsyncState(state: AsyncState) {
if (isSuccessState(state)) {
console.log("Data:", state.data); // state: SuccessState
} else if (isErrorState(state)) {
console.log("Error:", state.error); // state: ErrorState
} else {
console.log("Loading..."); // state: LoadingState
}
}
最佳实践:
How does TypeScript integrate with third-party JavaScript libraries?
How does TypeScript integrate with third-party JavaScript libraries?
答案:
TypeScript 通过类型声明文件与第三方 JavaScript 库集成,提供类型安全和开发体验,有多种集成方式。
集成方式:
使用 @types 包:
# 安装 JavaScript 库和对应的类型定义
npm install lodash
npm install @types/lodash --save-dev
npm install express
npm install @types/express --save-dev
// 使用有类型支持的第三方库
import _ from 'lodash';
import express from 'express';
const numbers = [1, 2, 3, 4, 5];
const doubled = _.map(numbers, n => n * 2); // 类型安全
const app = express();
app.get('/users', (req, res) => {
// req 和 res 都有完整的类型信息
res.json({ users: [] });
});
库自带类型定义:
// 现代库通常自带 TypeScript 声明
import axios from 'axios'; // axios 包含类型定义
import React from 'react'; // React 包含类型定义
interface User {
id: number;
name: string;
email: string;
}
// 使用泛型获得类型安全的 API 调用
const response = await axios.get<User[]>('/api/users');
const users: User[] = response.data; // 类型安全
// React 组件也有完整类型支持
const UserComponent: React.FC<{ user: User }> = ({ user }) => (
<div>{user.name}</div>
);
手动创建声明文件:
// 为没有类型的库创建声明文件
// types/legacy-library.d.ts
declare module 'legacy-library' {
export interface Config {
apiUrl: string;
debug: boolean;
}
export class ApiClient {
constructor(config: Config);
get<T>(path: string): Promise<T>;
post<T>(path: string, data: any): Promise<T>;
}
export function initialize(config: Config): void;
export const version: string;
}
// 使用自定义声明
import { ApiClient, initialize } from 'legacy-library';
initialize({ apiUrl: 'https://api.example.com', debug: true });
const client = new ApiClient({ apiUrl: 'https://api.example.com', debug: false });
const users = await client.get<User[]>('/users');
高级集成技巧:
全局变量声明:
// types/global.d.ts
declare global {
// jQuery 全局变量
const $: {
(selector: string): {
addClass: (className: string) => void;
removeClass: (className: string) => void;
on: (event: string, handler: (e: Event) => void) => void;
};
ajax: (options: {
url: string;
method?: 'GET' | 'POST';
data?: any;
success?: (data: any) => void;
}) => void;
};
// 环境变量
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
API_URL: string;
SECRET_KEY: string;
}
}
// Window 对象扩展
interface Window {
gtag: (command: string, ...args: any[]) => void;
dataLayer: any[];
}
}
// 使用全局声明
$('.button').addClass('active');
$.ajax({
url: '/api/data',
method: 'GET',
success: (data) => console.log(data)
});
const apiUrl = process.env.API_URL; // 有类型提示
window.gtag('event', 'click', { button: 'header' });
模块增强(Module Augmentation):
// 扩展现有模块的类型
import 'express';
declare module 'express' {
interface Request {
user?: {
id: number;
name: string;
role: 'admin' | 'user';
};
}
}
// 现在可以使用扩展的类型
app.use((req, res, next) => {
req.user = { id: 1, name: '张三', role: 'admin' }; // 类型安全
next();
});
app.get('/profile', (req, res) => {
if (req.user) {
res.json({ name: req.user.name }); // 类型安全访问
}
});
条件类型集成:
// 为动态库创建类型适配器
type LibraryMethod<T> = T extends 'get'
? <R>(path: string) => Promise<R>
: T extends 'post'
? <R>(path: string, data: any) => Promise<R>
: T extends 'delete'
? (path: string) => Promise<void>
: never;
interface DynamicApiClient {
request<T extends 'get' | 'post' | 'delete'>(
method: T,
path: string,
data?: any
): ReturnType<LibraryMethod<T>>;
}
declare const apiClient: DynamicApiClient;
// 类型安全的动态调用
const users = await apiClient.request('get', '/users'); // 返回 Promise<unknown>
const newUser = await apiClient.request('post', '/users', userData); // 返回 Promise<unknown>
await apiClient.request('delete', '/users/1'); // 返回 Promise<void>
实际项目集成示例:
React + UI 库集成:
// 集成 Ant Design
import { Button, Form, Input } from 'antd';
import type { FormInstance } from 'antd/es/form';
interface LoginFormData {
username: string;
password: string;
}
const LoginForm: React.FC = () => {
const [form] = Form.useForm<LoginFormData>();
const onFinish = (values: LoginFormData) => {
console.log('登录数据:', values); // 完整类型支持
};
return (
<Form form={form} onFinish={onFinish}>
<Form.Item name="username" rules={[{ required: true }]}>
<Input placeholder="用户名" />
</Form.Item>
<Form.Item name="password" rules={[{ required: true }]}>
<Input.Password placeholder="密码" />
</Form.Item>
<Button type="primary" htmlType="submit">
登录
</Button>
</Form>
);
};
数据库 ORM 集成:
// 集成 Prisma
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// Prisma 自动生成类型
async function getUserWithPosts(userId: number) {
const user = await prisma.user.findUnique({
where: { id: userId },
include: { posts: true } // 自动类型推断
});
return user; // 返回类型自动推断包含 posts 关联
}
// 使用生成的类型
type UserWithPosts = Awaited<ReturnType<typeof getUserWithPosts>>;
最佳实践:
What are declaration files (.d.ts)? How to write them?
What are declaration files (.d.ts)? How to write them?
- 考察点:类型声明文件理解与编写能力。
答案:
声明文件(.d.ts)是 TypeScript 中专门用于类型声明的文件,只包含类型信息而不包含具体实现,为 JavaScript 代码提供类型定义。
声明文件的作用:
为 JavaScript 代码提供类型信息:
// utils.js (JavaScript 文件)
function formatDate(date) {
return date.toISOString().split('T')[0];
}
function calculateAge(birthYear) {
return new Date().getFullYear() - birthYear;
}
const API_BASE_URL = 'https://api.example.com';
module.exports = {
formatDate,
calculateAge,
API_BASE_URL
};
// utils.d.ts (对应的声明文件)
declare function formatDate(date: Date): string;
declare function calculateAge(birthYear: number): number;
declare const API_BASE_URL: string;
export { formatDate, calculateAge, API_BASE_URL };
描述全局变量和环境:
// global.d.ts
declare global {
// 全局变量
const __VERSION__: string;
const __DEV__: boolean;
// 全局函数
function gtag(command: string, targetId: string, config?: any): void;
// 扩展 Window 对象
interface Window {
dataLayer: any[];
customAnalytics: {
track: (event: string, properties?: Record<string, any>) => void;
identify: (userId: string, traits?: Record<string, any>) => void;
};
}
// 扩展 NodeJS 环境
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
DATABASE_URL: string;
JWT_SECRET: string;
REDIS_URL?: string;
}
}
}
export {}; // 确保文件被当作模块处理
编写声明文件的方法:
基本类型声明:
// types.d.ts
// 函数声明
declare function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number,
immediate?: boolean
): T;
// 变量声明
declare const VERSION: string;
declare let isProduction: boolean;
// 类声明
declare class EventEmitter {
constructor();
on(event: string, listener: (...args: any[]) => void): this;
off(event: string, listener: (...args: any[]) => void): this;
emit(event: string, ...args: any[]): boolean;
}
// 接口声明
interface ApiResponse<T = any> {
success: boolean;
data: T;
message?: string;
timestamp: number;
}
// 类型别名声明
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
type ApiEndpoint = `/api/${string}`;
模块声明:
// modules.d.ts
// 为文件扩展名声明模块
declare module '*.css' {
const content: { [className: string]: string };
export default content;
}
declare module '*.scss' {
const content: { [className: string]: string };
export default content;
}
declare module '*.png' {
const src: string;
export default src;
}
declare module '*.jpg' {
const src: string;
export default src;
}
declare module '*.svg' {
import React from 'react';
const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
export { ReactComponent };
const src: string;
export default src;
}
// 为第三方模块声明类型
declare module 'custom-library' {
interface Config {
apiUrl: string;
timeout?: number;
retries?: number;
}
interface User {
id: string;
name: string;
email: string;
}
class ApiClient {
constructor(config: Config);
getUser(id: string): Promise<User>;
updateUser(id: string, data: Partial<User>): Promise<User>;
}
export { Config, User, ApiClient };
export default ApiClient;
}
命名空间声明:
// vendor.d.ts
// jQuery 声明
declare namespace $ {
interface JQueryStatic {
(selector: string): JQuery;
(element: Element): JQuery;
(callback: () => void): void;
ajax(settings: AjaxSettings): JQueryXHR;
get(url: string, data?: any, success?: (data: any) => void): JQueryXHR;
post(url: string, data?: any, success?: (data: any) => void): JQueryXHR;
extend<T>(target: T, ...sources: any[]): T;
each<T>(array: T[], callback: (index: number, value: T) => void): void;
}
interface JQuery {
addClass(className: string): JQuery;
removeClass(className: string): JQuery;
toggleClass(className: string, state?: boolean): JQuery;
on(events: string, handler: (eventObject: JQueryEventObject) => any): JQuery;
off(events?: string, handler?: (eventObject: JQueryEventObject) => any): JQuery;
text(): string;
text(text: string): JQuery;
html(): string;
html(htmlString: string): JQuery;
find(selector: string): JQuery;
parent(selector?: string): JQuery;
children(selector?: string): JQuery;
each(callback: (index: number, element: Element) => void): JQuery;
map<T>(callback: (index: number, element: Element) => T): JQuery;
}
interface AjaxSettings {
url: string;
type?: string;
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
data?: any;
success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => void;
error?: (jqXHR: JQueryXHR, textStatus: string, errorThrown: string) => void;
contentType?: string;
dataType?: 'json' | 'xml' | 'html' | 'text' | 'script';
timeout?: number;
}
interface JQueryEventObject extends Event {
data?: any;
namespace?: string;
result?: any;
timeStamp: number;
currentTarget: Element;
delegateTarget: Element;
}
interface JQueryXHR {
readyState: number;
status: number;
statusText: string;
responseText: string;
responseJSON?: any;
done(callback: (data: any, textStatus: string, jqXHR: JQueryXHR) => void): JQueryXHR;
fail(callback: (jqXHR: JQueryXHR, textStatus: string, errorThrown: string) => void): JQueryXHR;
always(callback: (data?: any, textStatus?: string, jqXHR?: JQueryXHR) => void): JQueryXHR;
}
}
declare const $: $.JQueryStatic;
高级声明技巧:
// advanced.d.ts
// 条件类型声明
type ApiResult<T extends 'success' | 'error'> = T extends 'success'
? { status: 'success'; data: any; timestamp: number }
: { status: 'error'; message: string; code: number };
// 泛型约束声明
interface Repository<T extends { id: string | number }> {
findById(id: T['id']): Promise<T | null>;
create(data: Omit<T, 'id'>): Promise<T>;
update(id: T['id'], data: Partial<Omit<T, 'id'>>): Promise<T>;
delete(id: T['id']): Promise<void>;
findAll(query?: QueryOptions<T>): Promise<T[]>;
}
interface QueryOptions<T> {
where?: Partial<T>;
orderBy?: keyof T;
limit?: number;
offset?: number;
}
// 映射类型声明
type ValidationRules<T> = {
[K in keyof T]?: {
required?: boolean;
minLength?: number;
maxLength?: number;
pattern?: RegExp;
custom?: (value: T[K]) => boolean | string;
};
};
// 函数重载声明
declare function createElement(tag: 'div'): HTMLDivElement;
declare function createElement(tag: 'span'): HTMLSpanElement;
declare function createElement(tag: 'input'): HTMLInputElement;
declare function createElement(tag: 'button'): HTMLButtonElement;
declare function createElement(tag: string): HTMLElement;
// 模板字面量类型声明
type HttpStatusCode = `${2 | 3 | 4 | 5}${number}${number}`;
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
type ApiEndpoint<T extends string> = `/api/${T}`;
declare function fetchApi<T extends string>(
method: HttpMethod,
endpoint: ApiEndpoint<T>
): Promise<ApiResponse>;
实际项目中的声明文件组织:
// 项目结构
src/
├── types/
│ ├── global.d.ts // 全局类型声明
│ ├── modules.d.ts // 模块声明
│ ├── api.d.ts // API 相关类型
│ ├── components.d.ts // 组件类型
│ └── vendor/ // 第三方库声明
│ ├── jquery.d.ts
│ └── lodash.d.ts
├── components/
└── utils/
// tsconfig.json 配置
{
"compilerOptions": {
"typeRoots": ["./src/types", "./node_modules/@types"],
"types": ["node", "jest"]
},
"include": [
"src/**/*"
]
}
具体编写示例:
// src/types/api.d.ts
declare namespace API {
// 基础响应类型
interface BaseResponse {
success: boolean;
message?: string;
timestamp: number;
}
interface SuccessResponse<T = any> extends BaseResponse {
success: true;
data: T;
}
interface ErrorResponse extends BaseResponse {
success: false;
error: {
code: number;
message: string;
details?: any;
};
}
type Response<T = any> = SuccessResponse<T> | ErrorResponse;
// 用户相关类型
namespace User {
interface Profile {
id: number;
username: string;
email: string;
avatar?: string;
role: 'admin' | 'user' | 'moderator';
createdAt: string;
updatedAt: string;
}
interface CreateRequest {
username: string;
email: string;
password: string;
}
interface UpdateRequest {
username?: string;
email?: string;
avatar?: string;
}
interface LoginRequest {
email: string;
password: string;
}
interface LoginResponse {
user: Profile;
token: string;
refreshToken: string;
expiresIn: number;
}
}
// 文章相关类型
namespace Article {
interface Item {
id: number;
title: string;
content: string;
summary: string;
author: User.Profile;
tags: string[];
publishedAt: string;
updatedAt: string;
viewCount: number;
likeCount: number;
}
interface CreateRequest {
title: string;
content: string;
summary?: string;
tags?: string[];
}
interface UpdateRequest extends Partial<CreateRequest> {}
interface ListQuery {
page?: number;
limit?: number;
author?: number;
tag?: string;
keyword?: string;
sortBy?: 'createdAt' | 'updatedAt' | 'viewCount' | 'likeCount';
order?: 'asc' | 'desc';
}
interface ListResponse {
items: Item[];
total: number;
page: number;
limit: number;
totalPages: number;
}
}
}
// 使用声明的类型
declare function apiRequest<T = any>(
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
url: string,
data?: any
): Promise<API.Response<T>>;
// 导出供其他文件使用
export = API;
export as namespace API;
最佳实践:
declare 关键字进行类型声明What are the common configurations in TypeScript compilation options (tsconfig.json)?
What are the common configurations in TypeScript compilation options (tsconfig.json)?
答案:
tsconfig.json 是 TypeScript 项目的配置文件,用于指定编译选项、文件包含规则和项目设置,是 TypeScript 项目的核心配置。
基础编译选项:
{
"compilerOptions": {
// 目标 JavaScript 版本
"target": "ES2020", // 输出 JavaScript 版本
"module": "ESNext", // 模块系统
"lib": ["ES2020", "DOM", "DOM.Iterable"], // 包含的库文件
// 输出配置
"outDir": "./dist", // 输出目录
"rootDir": "./src", // 输入根目录
"removeComments": true, // 移除注释
"downlevelIteration": true, // 为迭代器提供完整支持
// 模块解析
"moduleResolution": "node", // 模块解析策略
"esModuleInterop": true, // ES 模块互操作性
"allowSyntheticDefaultImports": true, // 允许合成默认导入
"forceConsistentCasingInFileNames": true, // 强制文件名大小写一致
// 类型检查
"strict": true, // 启用所有严格类型检查
"noImplicitAny": true, // 禁止隐式 any 类型
"strictNullChecks": true, // 严格 null 检查
"strictFunctionTypes": true, // 严格函数类型检查
"noImplicitReturns": true, // 禁止函数没有返回值
"noUnusedLocals": true, // 检查未使用的局部变量
"noUnusedParameters": true, // 检查未使用的参数
// 实验性功能
"experimentalDecorators": true, // 启用装饰器
"emitDecoratorMetadata": true, // 为装饰器生成元数据
// 源码映射
"sourceMap": true, // 生成源码映射
"inlineSourceMap": false, // 内联源码映射
// 其他
"skipLibCheck": true, // 跳过库文件检查
"allowJs": true, // 允许 JavaScript 文件
"checkJs": false, // 检查 JavaScript 文件
"declaration": true, // 生成声明文件
"declarationMap": true, // 为声明文件生成映射
"incremental": true, // 启用增量编译
"tsBuildInfoFile": "./dist/.tsbuildinfo" // 构建信息文件位置
}
}
文件包含和排除:
{
"compilerOptions": {
// ... 编译选项
},
// 包含的文件
"include": [
"src/**/*", // 包含 src 目录下所有文件
"types/**/*", // 包含类型声明文件
"tests/**/*.test.ts" // 包含测试文件
],
// 排除的文件
"exclude": [
"node_modules", // 排除 node_modules
"dist", // 排除输出目录
"coverage", // 排除覆盖率报告
"**/*.spec.ts", // 排除特定的测试文件
"build", // 排除构建目录
"*.config.js" // 排除配置文件
],
// 直接指定文件(很少使用)
"files": [
"src/index.ts",
"src/types/global.d.ts"
]
}
不同环境的配置示例:
开发环境配置:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
// 开发友好配置
"sourceMap": true,
"inlineSources": true,
"removeComments": false,
"preserveConstEnums": true,
// 严格类型检查
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
// 模块解析
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
// 路径映射
"baseUrl": "./",
"paths": {
"@/*": ["src/*"],
"@/components/*": ["src/components/*"],
"@/utils/*": ["src/utils/*"],
"@/types/*": ["src/types/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
生产环境配置:
{
"compilerOptions": {
"target": "ES2018",
"module": "CommonJS",
"lib": ["ES2018", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
// 生产优化
"removeComments": true,
"noEmitOnError": true,
"declaration": true,
"declarationMap": false,
"sourceMap": false,
// 严格检查
"strict": true,
"noUnusedLocals": false, // 生产环境可能需要保留未使用代码
"noUnusedParameters": false,
// 兼容性
"downlevelIteration": true,
"importHelpers": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": ["src/**/*.test.ts", "src/**/*.spec.ts", "node_modules"]
}
Node.js 项目配置:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
// Node.js 特定
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
// 类型
"types": ["node"],
// 严格模式
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
// 输出
"sourceMap": true,
"declaration": true,
"removeComments": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "tests"]
}
React 项目配置:
{
"compilerOptions": {
"target": "ES2020",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
// React 特定
"jsx": "react-jsx",
"jsxImportSource": "react",
// 路径映射
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@/components/*": ["src/components/*"],
"@/hooks/*": ["src/hooks/*"],
"@/utils/*": ["src/utils/*"]
}
},
"include": [
"src"
],
"exclude": [
"node_modules"
]
}
高级配置选项:
{
"compilerOptions": {
// 项目引用
"composite": true, // 启用项目引用
"incremental": true, // 增量编译
"tsBuildInfoFile": "./.tsbuildinfo", // 构建信息文件
// 模块解析高级选项
"baseUrl": "./", // 基础URL
"paths": { // 路径映射
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"],
"#types/*": ["types/*"]
},
"rootDirs": ["src", "generated"], // 虚拟根目录
// 类型相关
"typeRoots": ["./types", "./node_modules/@types"], // 类型根目录
"types": ["node", "jest", "webpack-env"], // 包含的类型包
// 输出控制
"declarationDir": "./types", // 声明文件输出目录
"outFile": "./dist/bundle.js", // 单文件输出(仅限AMD和System)
// JSX 选项
"jsx": "react-jsx", // JSX 编译方式
"jsxFactory": "React.createElement", // JSX 工厂函数
"jsxFragmentFactory": "React.Fragment", // JSX Fragment 工厂
"jsxImportSource": "react", // JSX 导入源
// 高级类型检查
"exactOptionalPropertyTypes": true, // 精确可选属性类型
"noImplicitOverride": true, // 需要显式 override
"noPropertyAccessFromIndexSignature": true, // 索引签名属性访问限制
"noUncheckedIndexedAccess": true, // 未检查的索引访问
// 实验性功能
"useDefineForClassFields": true, // 使用 define 语义定义类字段
"importsNotUsedAsValues": "remove", // 未使用导入的处理方式
"preserveValueImports": false, // 保留值导入
// 性能
"assumeChangesOnlyAffectDirectDependencies": true, // 假设更改只影响直接依赖
"skipDefaultLibCheck": true, // 跳过默认库检查
// 调试
"generateCpuProfile": "profile.cpuprofile", // 生成 CPU 性能分析
"explainFiles": true, // 解释文件包含原因
"extendedDiagnostics": true // 扩展诊断信息
},
// 项目引用
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/utils" }
],
// 监听选项
"watchOptions": {
"watchFile": "useFsEvents",
"watchDirectory": "useFsEvents",
"fallbackPolling": "dynamicPriority",
"synchronousWatchDirectory": true,
"excludeDirectories": ["**/node_modules", "_build"]
},
// 类型获取选项
"typeAcquisition": {
"enable": false,
"include": ["jquery"],
"exclude": ["jest", "mocha"]
}
}
分环境配置管理:
// tsconfig.json (基础配置)
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
// tsconfig.dev.json (开发环境)
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": true,
"removeComments": false,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": ["src/**/*", "tests/**/*"]
}
// tsconfig.prod.json (生产环境)
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"removeComments": true,
"declaration": true,
"noUnusedLocals": false,
"noUnusedParameters": false
},
"exclude": ["src/**/*.test.ts", "src/**/*.spec.ts", "tests", "node_modules"]
}
// tsconfig.build.json (构建配置)
{
"extends": "./tsconfig.prod.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"noEmit": false
}
}
命令行使用:
# 使用不同配置文件
tsc --project tsconfig.dev.json
tsc --project tsconfig.prod.json
tsc -p tsconfig.build.json
# 覆盖配置选项
tsc --target ES2018 --outDir ./build
tsc --strict --noImplicitAny
# 监听模式
tsc --watch --project tsconfig.dev.json
最佳实践:
extends 继承基础配置避免重复include 和 exclude 控制编译范围What are TypeScript's module resolution strategies? How to configure path mapping?
What are TypeScript’s module resolution strategies? How to configure path mapping?
- 考察点:模块系统与路径映射配置。
答案:
TypeScript 提供了两种主要的模块解析策略,并支持路径映射功能来简化模块导入,提高代码可维护性。
模块解析策略:
Node.js 解析策略(推荐):
// tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}
解析顺序:
// 导入 './utils/helper'
// 查找顺序:
// 1. ./utils/helper.ts
// 2. ./utils/helper.tsx
// 3. ./utils/helper.d.ts
// 4. ./utils/helper/package.json (查看 "types" 字段)
// 5. ./utils/helper/index.ts
// 6. ./utils/helper/index.tsx
// 7. ./utils/helper/index.d.ts
// 导入 'lodash'
// 查找顺序:
// 1. node_modules/lodash.ts
// 2. node_modules/lodash.tsx
// 3. node_modules/lodash.d.ts
// 4. node_modules/lodash/package.json ("types" 或 "typings" 字段)
// 5. node_modules/lodash/index.ts
// 6. node_modules/@types/lodash/package.json
// 7. node_modules/@types/lodash/index.d.ts
Classic 解析策略(已弃用):
// tsconfig.json
{
"compilerOptions": {
"moduleResolution": "classic"
}
}
解析顺序:
// 相对导入 './helper'
// 查找:
// 1. ./helper.ts
// 2. ./helper.d.ts
// 非相对导入 'moduleA'
// 从当前目录开始向上查找:
// 1. /current/directory/moduleA.ts
// 2. /current/directory/moduleA.d.ts
// 3. /current/moduleA.ts
// 4. /current/moduleA.d.ts
// 5. /moduleA.ts
// 6. /moduleA.d.ts
路径映射配置:
基础路径映射:
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
// 简化导入路径
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"],
"@types/*": ["src/types/*"],
"@assets/*": ["src/assets/*"],
"@api/*": ["src/api/*"],
// 多个可能的位置
"@lib/*": ["src/lib/*", "lib/*"],
// 精确匹配
"config": ["src/config/index.ts"],
"constants": ["src/constants/index.ts"]
}
}
}
// 使用路径映射前
import { Button } from '../../../components/ui/Button';
import { formatDate } from '../../../utils/date';
import { ApiClient } from '../../../api/client';
// 使用路径映射后
import { Button } from '@components/ui/Button';
import { formatDate } from '@utils/date';
import { ApiClient } from '@api/client';
复杂路径映射示例:
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
// 根据环境映射不同实现
"config/*": ["src/config/production/*", "src/config/development/*"],
// 版本化的库
"ui-lib/*": ["src/ui/v2/*", "src/ui/v1/*"],
// 平台特定的实现
"platform/*": [
"src/platforms/web/*",
"src/platforms/mobile/*",
"src/platforms/common/*"
],
// 外部库的本地版本
"react": ["node_modules/react"],
"react-dom": ["node_modules/react-dom"],
// 工具库的别名
"utils": ["src/utils/index.ts"],
"helpers": ["src/utils/helpers/index.ts"],
// 样式文件
"@styles/*": ["src/styles/*"],
"theme": ["src/styles/theme/index.ts"],
// 测试工具
"@test-utils": ["tests/utils/index.ts"],
"@mocks/*": ["tests/mocks/*"]
}
}
}
实际项目配置示例:
React 项目路径映射:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@pages/*": ["src/pages/*"],
"@hooks/*": ["src/hooks/*"],
"@context/*": ["src/context/*"],
"@utils/*": ["src/utils/*"],
"@api/*": ["src/api/*"],
"@types/*": ["src/types/*"],
"@assets/*": ["src/assets/*"],
"@styles/*": ["src/styles/*"],
"@constants/*": ["src/constants/*"]
}
}
}
// 组件中使用
import React from 'react';
import { Button } from '@components/ui/Button';
import { useAuth } from '@hooks/useAuth';
import { AuthContext } from '@context/AuthContext';
import { formatCurrency } from '@utils/format';
import { API_ENDPOINTS } from '@constants/api';
import type { User } from '@types/user';
const UserProfile: React.FC = () => {
const { user } = useAuth();
return (
<div>
<h1>{user?.name}</h1>
<Button onClick={() => console.log('Edit profile')}>
Edit Profile
</Button>
</div>
);
};
Node.js 项目路径映射:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@controllers/*": ["controllers/*"],
"@services/*": ["services/*"],
"@models/*": ["models/*"],
"@middleware/*": ["middleware/*"],
"@utils/*": ["utils/*"],
"@config/*": ["config/*"],
"@types/*": ["types/*"],
"@db/*": ["database/*"],
"@routes/*": ["routes/*"]
}
}
}
// Express 应用中使用
import express from 'express';
import { UserController } from '@controllers/UserController';
import { AuthMiddleware } from '@middleware/AuthMiddleware';
import { UserService } from '@services/UserService';
import { DatabaseConfig } from '@config/database';
import type { AuthRequest } from '@types/request';
const router = express.Router();
router.get('/users',
AuthMiddleware.verify,
UserController.getUsers
);
高级配置技巧:
条件路径映射:
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
// 根据构建目标选择不同实现
"logger": [
"src/utils/logger.development.ts",
"src/utils/logger.production.ts"
],
// 功能特性开关
"feature/*": [
"src/features/enabled/*",
"src/features/disabled/*"
]
}
}
}
Webpack 集成配置:
// webpack.config.js
const path = require('path');
module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
'@types': path.resolve(__dirname, 'src/types'),
'@assets': path.resolve(__dirname, 'src/assets')
},
extensions: ['.ts', '.tsx', '.js', '.jsx']
}
};
Vite 集成配置:
// vite.config.ts
import { defineConfig } from 'vite';
import path from 'path';
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
'@types': path.resolve(__dirname, 'src/types')
}
}
});
调试模块解析:
使用 TypeScript 编译器调试:
# 显示模块解析过程
tsc --traceResolution
# 显示包含的文件
tsc --listFiles
# 显示详细信息
tsc --explainFiles
在 tsconfig.json 中启用调试:
{
"compilerOptions": {
"traceResolution": true,
"listFiles": true,
"explainFiles": true
}
}
常见问题和解决方案:
路径映射不工作:
// 问题:路径映射不生效
// 解决方案:检查 baseUrl 设置
{
"compilerOptions": {
"baseUrl": "./", // 必须设置 baseUrl
"paths": {
"@/*": ["src/*"]
}
}
}
编译器和构建工具不一致:
// 确保 webpack/vite 的别名与 TypeScript 路径映射一致
// webpack.config.js
module.exports = {
resolve: {
alias: {
// 与 tsconfig.json 中的 paths 保持一致
'@': path.resolve(__dirname, 'src')
}
}
};
测试环境配置:
// jest.config.js
{
"moduleNameMapping": {
"^@/(.*)$": "<rootDir>/src/$1",
"^@components/(.*)$": "<rootDir>/src/components/$1"
}
}
最佳实践:
What are Conditional Types? Please give examples.
What are Conditional Types? Please give examples.
答案:
条件类型(Conditional Types)是 TypeScript 中基于条件判断来选择类型的高级类型特性,语法为 T extends U ? X : Y,类似于三元运算符但作用于类型层面。
基础语法:
简单条件类型:
// 基础条件类型语法
type IsString<T> = T extends string ? true : false;
type Test1 = IsString<string>; // true
type Test2 = IsString<number>; // false
type Test3 = IsString<"hello">; // true
// 实用的条件类型
type NonNull<T> = T extends null | undefined ? never : T;
type ValidString = NonNull<string>; // string
type ValidNumber = NonNull<number | null>; // number
type NeverType = NonNull<null>; // never
内置条件类型:
// TypeScript 内置的条件类型
type MyNonNullable<T> = T extends null | undefined ? never : T;
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type MyParameters<T> = T extends (...args: infer P) => any ? P : never;
// 使用示例
function add(a: number, b: number): number {
return a + b;
}
type AddParams = MyParameters<typeof add>; // [number, number]
type AddReturn = MyReturnType<typeof add>; // number
infer 关键字:
类型推断:
// 推断返回值类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 推断参数类型
type FirstParameter<T> = T extends (first: infer P, ...args: any[]) => any ? P : never;
// 推断数组元素类型
type ArrayElement<T> = T extends (infer U)[] ? U : never;
type StringArray = ArrayElement<string[]>; // string
type NumberElement = ArrayElement<number[]>; // number
// 推断 Promise 内容类型
type Awaited<T> = T extends Promise<infer U> ? U : T;
type PromiseString = Awaited<Promise<string>>; // string
type DirectString = Awaited<string>; // string
复杂类型推断:
// 深层嵌套推断
type DeepArray<T> = T extends (infer U)[]
? U extends (infer V)[]
? DeepArray<V>
: U
: T;
type Deep1 = DeepArray<string[][][]>; // string
type Deep2 = DeepArray<number[]>; // number
// 对象属性推断
type GetProperty<T, K> = T extends { [P in keyof T]: T[P] }
? K extends keyof T
? T[K]
: never
: never;
interface User {
id: number;
name: string;
email: string;
}
type UserName = GetProperty<User, 'name'>; // string
type UserId = GetProperty<User, 'id'>; // number
实际应用场景:
API 响应类型处理:
// 根据请求方法确定响应类型
type ApiMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type ApiResponse<M extends ApiMethod, T> = M extends 'GET'
? { data: T }
: M extends 'POST'
? { data: T; id: string }
: M extends 'PUT'
? { data: T; updated: Date }
: M extends 'DELETE'
? { success: boolean }
: never;
// 使用
type GetUserResponse = ApiResponse<'GET', User>; // { data: User }
type CreateUserResponse = ApiResponse<'POST', User>; // { data: User; id: string }
type DeleteUserResponse = ApiResponse<'DELETE', User>; // { success: boolean }
// 函数重载辅助
function apiCall<M extends ApiMethod, T>(
method: M,
data?: T
): Promise<ApiResponse<M, T>>;
function apiCall(method: ApiMethod, data?: any): Promise<any> {
// 实现逻辑
return Promise.resolve({});
}
表单验证类型:
// 条件类型实现表单验证
type ValidationRule<T> = T extends string
? { required?: boolean; minLength?: number; maxLength?: number; pattern?: RegExp }
: T extends number
? { required?: boolean; min?: number; max?: number }
: T extends boolean
? { required?: boolean }
: { required?: boolean };
type FormValidation<T> = {
[K in keyof T]: ValidationRule<T[K]>;
};
interface LoginForm {
username: string;
password: string;
age: number;
rememberMe: boolean;
}
type LoginValidation = FormValidation<LoginForm>;
/*
{
username: { required?: boolean; minLength?: number; maxLength?: number; pattern?: RegExp };
password: { required?: boolean; minLength?: number; maxLength?: number; pattern?: RegExp };
age: { required?: boolean; min?: number; max?: number };
rememberMe: { required?: boolean };
}
*/
高级类型工具:
// 递归条件类型
type DeepReadonly<T> = T extends object
? { readonly [P in keyof T]: DeepReadonly<T[P]> }
: T;
type DeepPartial<T> = T extends object
? { [P in keyof T]?: DeepPartial<T[P]> }
: T;
// 使用示例
interface NestedObject {
user: {
profile: {
name: string;
settings: {
theme: string;
notifications: boolean;
};
};
};
}
type ReadonlyNested = DeepReadonly<NestedObject>;
type PartialNested = DeepPartial<NestedObject>;
// 联合类型分发
type ToArray<T> = T extends any ? T[] : never;
type StringOrNumber = string | number;
type ArrayUnion = ToArray<StringOrNumber>; // string[] | number[]
// 排除特定类型
type NonFunction<T> = T extends Function ? never : T;
interface MixedObject {
name: string;
age: number;
getName: () => string;
setAge: (age: number) => void;
}
type OnlyData = {
[K in keyof MixedObject]: NonFunction<MixedObject[K]>;
};
// { name: string; age: number; getName: never; setAge: never; }
状态机类型:
// 状态机条件类型
type StateTransition<
CurrentState extends string,
Action extends string
> = CurrentState extends 'idle'
? Action extends 'start' ? 'loading' : 'idle'
: CurrentState extends 'loading'
? Action extends 'success' ? 'success'
: Action extends 'error' ? 'error'
: Action extends 'cancel' ? 'idle'
: 'loading'
: CurrentState extends 'success' | 'error'
? Action extends 'reset' ? 'idle' : CurrentState
: never;
type IdleToLoading = StateTransition<'idle', 'start'>; // 'loading'
type LoadingToSuccess = StateTransition<'loading', 'success'>; // 'success'
type SuccessToIdle = StateTransition<'success', 'reset'>; // 'idle'
分布式条件类型:
// 联合类型的分布式特性
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
type Example = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'
type Example2 = Extract<'a' | 'b' | 'c', 'a' | 'b'>; // 'a' | 'b'
// 防止分布式行为
type NoDistribute<T, U> = [T] extends [U] ? true : false;
type Test1 = NoDistribute<'a' | 'b', string>; // true
type Test2 = ('a' | 'b') extends string ? true : false; // true(分布式)
最佳实践:
What are Mapped Types? What are the common built-in mapped types?
What are Mapped Types? What are the common built-in mapped types?
答案:
映射类型(Mapped Types)是 TypeScript 中基于现有类型创建新类型的方式,通过遍历类型的属性并应用转换规则来生成新的类型结构。
基础映射类型语法:
// 基础映射类型语法
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Required<T> = {
[P in keyof T]-?: T[P]; // 移除可选性
};
// 使用示例
interface User {
id: number;
name: string;
email?: string;
}
type ReadonlyUser = Readonly<User>;
/*
{
readonly id: number;
readonly name: string;
readonly email?: string;
}
*/
type PartialUser = Partial<User>;
/*
{
id?: number;
name?: string;
email?: string;
}
*/
type RequiredUser = Required<User>;
/*
{
id: number;
name: string;
email: string; // 不再可选
}
*/
常见内置映射类型:
基础工具类型:
interface Product {
id: number;
name: string;
price: number;
description?: string;
category: string;
}
// Pick - 选择特定属性
type ProductSummary = Pick<Product, 'id' | 'name' | 'price'>;
/*
{
id: number;
name: string;
price: number;
}
*/
// Omit - 排除特定属性
type ProductWithoutId = Omit<Product, 'id'>;
/*
{
name: string;
price: number;
description?: string;
category: string;
}
*/
// Record - 创建键值对类型
type StatusMap = Record<'loading' | 'success' | 'error', string>;
/*
{
loading: string;
success: string;
error: string;
}
*/
// Exclude 和 Extract - 联合类型过滤
type StringOrNumber = string | number | boolean;
type OnlyStringOrNumber = Exclude<StringOrNumber, boolean>; // string | number
type OnlyString = Extract<StringOrNumber, string>; // string
高级工具类型:
// ReturnType - 获取函数返回类型
function getUser(id: number): Promise<User> {
return Promise.resolve({} as User);
}
type GetUserReturn = ReturnType<typeof getUser>; // Promise<User>
// Parameters - 获取函数参数类型
type GetUserParams = Parameters<typeof getUser>; // [number]
// ConstructorParameters - 获取构造函数参数
class UserService {
constructor(apiUrl: string, timeout: number) {}
}
type UserServiceParams = ConstructorParameters<typeof UserService>; // [string, number]
// InstanceType - 获取构造函数实例类型
type UserServiceInstance = InstanceType<typeof UserService>; // UserService
自定义映射类型:
属性修饰符映射:
// 深度只读
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// 深度可选
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// 可空类型
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
// 使用示例
interface Config {
server: {
host: string;
port: number;
ssl: {
enabled: boolean;
cert: string;
};
};
database: {
url: string;
timeout: number;
};
}
type ReadonlyConfig = DeepReadonly<Config>;
type PartialConfig = DeepPartial<Config>;
type NullableConfig = Nullable<Config>;
键名转换映射:
// 添加前缀
type AddPrefix<T, Prefix extends string> = {
[P in keyof T as `${Prefix}${string & P}`]: T[P];
};
// 添加后缀
type AddSuffix<T, Suffix extends string> = {
[P in keyof T as `${string & P}${Suffix}`]: T[P];
};
// 转换为 getter 类型
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
// 转换为 setter 类型
type Setters<T> = {
[P in keyof T as `set${Capitalize<string & P>}`]: (value: T[P]) => void;
};
interface Person {
name: string;
age: number;
}
type PersonGetters = Getters<Person>;
/*
{
getName: () => string;
getAge: () => number;
}
*/
type PersonSetters = Setters<Person>;
/*
{
setName: (value: string) => void;
setAge: (value: number) => void;
}
*/
type PrefixedPerson = AddPrefix<Person, 'user'>;
/*
{
username: string;
userage: number;
}
*/
实际应用场景:
表单状态管理:
// 表单字段状态
type FieldState<T> = {
value: T;
error?: string;
touched: boolean;
dirty: boolean;
};
// 表单状态映射
type FormState<T> = {
[P in keyof T]: FieldState<T[P]>;
};
// 表单验证规则
type ValidationRules<T> = {
[P in keyof T]?: (value: T[P]) => string | undefined;
};
// 使用示例
interface LoginForm {
email: string;
password: string;
}
type LoginFormState = FormState<LoginForm>;
/*
{
email: FieldState<string>;
password: FieldState<string>;
}
*/
const loginState: LoginFormState = {
email: { value: '', touched: false, dirty: false },
password: { value: '', touched: false, dirty: false }
};
const validationRules: ValidationRules<LoginForm> = {
email: (value) => value.includes('@') ? undefined : 'Invalid email',
password: (value) => value.length >= 6 ? undefined : 'Too short'
};
API 客户端生成:
// API 端点定义
interface ApiEndpoints {
getUser: { params: { id: number }; response: User };
createUser: { body: Omit<User, 'id'>; response: User };
updateUser: { params: { id: number }; body: Partial<User>; response: User };
deleteUser: { params: { id: number }; response: { success: boolean } };
}
// 生成客户端方法类型
type ApiClient<T> = {
[K in keyof T]: T[K] extends { params: infer P; response: infer R }
? (params: P) => Promise<R>
: T[K] extends { body: infer B; response: infer R }
? (body: B) => Promise<R>
: T[K] extends { params: infer P; body: infer B; response: infer R }
? (params: P, body: B) => Promise<R>
: never;
};
type UserApiClient = ApiClient<ApiEndpoints>;
/*
{
getUser: (params: { id: number }) => Promise<User>;
createUser: (body: Omit<User, 'id'>) => Promise<User>;
updateUser: (params: { id: number }, body: Partial<User>) => Promise<User>;
deleteUser: (params: { id: number }) => Promise<{ success: boolean }>;
}
*/
状态管理类型:
// 状态 action 生成
type StateActions<T> = {
[P in keyof T as `set${Capitalize<string & P>}`]: (value: T[P]) => void;
} & {
[P in keyof T as `reset${Capitalize<string & P>}`]: () => void;
};
// 状态选择器生成
type StateSelectors<T> = {
[P in keyof T as `select${Capitalize<string & P>}`]: () => T[P];
};
interface AppState {
user: User | null;
loading: boolean;
error: string | null;
}
type AppStateActions = StateActions<AppState>;
/*
{
setUser: (value: User | null) => void;
setLoading: (value: boolean) => void;
setError: (value: string | null) => void;
resetUser: () => void;
resetLoading: () => void;
resetError: () => void;
}
*/
type AppStateSelectors = StateSelectors<AppState>;
/*
{
selectUser: () => User | null;
selectLoading: () => boolean;
selectError: () => string | null;
}
*/
条件映射类型:
// 过滤特定类型的属性
type FilterByType<T, U> = {
[P in keyof T]: T[P] extends U ? T[P] : never;
};
// 只保留函数类型的属性
type FunctionProperties<T> = {
[P in keyof T]: T[P] extends Function ? P : never;
}[keyof T];
// 只保留非函数类型的属性
type NonFunctionProperties<T> = {
[P in keyof T]: T[P] extends Function ? never : P;
}[keyof T];
interface MixedInterface {
name: string;
age: number;
getName: () => string;
setAge: (age: number) => void;
isActive: boolean;
}
type FunctionProps = FunctionProperties<MixedInterface>; // "getName" | "setAge"
type DataProps = NonFunctionProperties<MixedInterface>; // "name" | "age" | "isActive"
// 分离数据和方法
type DataOnly<T> = Pick<T, NonFunctionProperties<T>>;
type MethodsOnly<T> = Pick<T, FunctionProperties<T>>;
type MixedData = DataOnly<MixedInterface>;
/*
{
name: string;
age: number;
isActive: boolean;
}
*/
type MixedMethods = MethodsOnly<MixedInterface>;
/*
{
getName: () => string;
setAge: (age: number) => void;
}
*/
最佳实践:
What are index type queries (keyof) and indexed access types (T[K])?
What are index type queries (keyof) and indexed access types (T[K])?
What is the role of the infer keyword and its usage scenarios?
What is the role of the infer keyword and its usage scenarios?
答案:
infer 关键字用于条件类型中进行类型推断,允许在类型匹配过程中提取和使用某个类型的一部分。
基础用法:
函数类型推断:
// 推断返回值类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function getUser(): Promise<{ id: number; name: string }> {
return Promise.resolve({ id: 1, name: "张三" });
}
type UserReturn = ReturnType<typeof getUser>; // Promise<{ id: number; name: string }>
// 推断参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
function createUser(name: string, age: number, email: string): void {}
type CreateUserParams = Parameters<typeof createUser>; // [string, number, string]
// 推断第一个参数
type FirstParameter<T> = T extends (first: infer F, ...args: any[]) => any ? F : never;
type FirstParam = FirstParameter<typeof createUser>; // string
Promise 类型推断:
// 提取 Promise 内部类型
type Awaited<T> = T extends Promise<infer U> ? U : T;
type StringPromise = Awaited<Promise<string>>; // string
type NumberPromise = Awaited<Promise<number>>; // number
type DirectType = Awaited<string>; // string (非 Promise)
// 深层 Promise 推断
type DeepAwaited<T> = T extends Promise<infer U>
? U extends Promise<any>
? DeepAwaited<U>
: U
: T;
type NestedPromise = DeepAwaited<Promise<Promise<string>>>; // string
高级应用:
数组类型推断:
// 数组元素类型推断
type ArrayElement<T> = T extends (infer U)[] ? U : never;
type StringArray = ArrayElement<string[]>; // string
type NumberArray = ArrayElement<number[]>; // number
type MixedArray = ArrayElement<(string | number)[]>; // string | number
// 元组首尾类型推断
type Head<T extends readonly any[]> = T extends readonly [infer H, ...any[]] ? H : never;
type Tail<T extends readonly any[]> = T extends readonly [any, ...infer Rest] ? Rest : never;
type First = Head<[1, 2, 3, 4]>; // 1
type Rest = Tail<[1, 2, 3, 4]>; // [2, 3, 4]
// 反转数组类型
type Reverse<T extends readonly any[]> = T extends readonly [...infer Rest, infer Last]
? [Last, ...Reverse<Rest>]
: [];
type Reversed = Reverse<[1, 2, 3, 4]>; // [4, 3, 2, 1]
对象类型推断:
// 推断对象属性值类型
type ValueOf<T> = T extends { [key: string]: infer V } ? V : never;
interface User {
id: number;
name: string;
active: boolean;
}
type UserValues = ValueOf<User>; // number | string | boolean
// 推断函数对象的方法类型
type MethodNames<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
}[keyof T];
type Methods<T> = Pick<T, MethodNames<T>>;
interface Calculator {
value: number;
add(n: number): void;
subtract(n: number): void;
getValue(): number;
}
type CalcMethods = Methods<Calculator>;
// { add: (n: number) => void; subtract: (n: number) => void; getValue: () => number; }
实际应用场景:
API 响应处理:
// 从 fetch 响应推断数据类型
type ExtractData<T> = T extends { data: infer D } ? D : never;
interface ApiResponse<T> {
success: boolean;
data: T;
message?: string;
}
type UserData = ExtractData<ApiResponse<User>>; // User
// 推断嵌套API响应
type NestedResponse<T> = T extends {
result: { items: infer Items }
} ? Items : never;
interface PagedResponse<T> {
result: {
items: T[];
total: number;
page: number;
};
}
type ItemsType = NestedResponse<PagedResponse<User>>; // User[]
事件系统类型推断:
// 从事件处理器推断事件类型
type EventType<T> = T extends (event: infer E) => any ? E : never;
type ClickHandler = (event: MouseEvent) => void;
type KeyHandler = (event: KeyboardEvent) => void;
type ClickEvent = EventType<ClickHandler>; // MouseEvent
type KeyEvent = EventType<KeyHandler>; // KeyboardEvent
// 通用事件系统
type EventMap = {
click: MouseEvent;
keydown: KeyboardEvent;
scroll: UIEvent;
};
type ExtractEventType<T extends keyof EventMap> = EventMap[T];
function addEventListener<T extends keyof EventMap>(
type: T,
handler: (event: ExtractEventType<T>) => void
): void {
// 实现
}
addEventListener('click', (e) => {
// e 的类型自动推断为 MouseEvent
console.log(e.clientX, e.clientY);
});
状态管理类型推断:
// 从 reducer 推断 state 类型
type StateFromReducer<T> = T extends (state: infer S, action: any) => any ? S : never;
type ActionFromReducer<T> = T extends (state: any, action: infer A) => any ? A : never;
function userReducer(
state: { users: User[]; loading: boolean },
action: { type: 'ADD_USER'; user: User } | { type: 'SET_LOADING'; loading: boolean }
) {
return state;
}
type UserState = StateFromReducer<typeof userReducer>;
// { users: User[]; loading: boolean }
type UserAction = ActionFromReducer<typeof userReducer>;
// { type: 'ADD_USER'; user: User } | { type: 'SET_LOADING'; loading: boolean }
工具类型构建:
// 构建复杂的工具类型
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
}[keyof T];
type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;
// 从类构造函数推断实例类型
type InstanceType<T extends abstract new (...args: any) => any> =
T extends abstract new (...args: any) => infer R ? R : any;
class UserService {
getUsers(): User[] { return []; }
}
type ServiceInstance = InstanceType<typeof UserService>; // UserService
// 递归类型推断
type Flatten<T> = T extends (infer U)[]
? U extends (infer V)[]
? Flatten<V>
: U
: T;
type Deep = Flatten<string[][][]>; // string
最佳实践:
How to implement Type Challenges? Please give examples.
How to implement Type Challenges? Please give examples.
How is type compatibility determined in TypeScript?
How is type compatibility determined in TypeScript?
What is the never type? What are its application scenarios?
What is the never type? What are its application scenarios?
答案:
never 类型表示永不存在的值的类型,是 TypeScript 类型系统的底部类型,通常用于表示不可达的代码路径和详尽性检查。
never 类型特征:
函数永不返回:
// 抛出异常的函数
function throwError(message: string): never {
throw new Error(message);
}
// 无限循环的函数
function infiniteLoop(): never {
while (true) {
console.log("Running forever...");
}
}
// 使用 never 类型
function processValue(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase();
} else if (typeof value === "number") {
return value.toString();
}
// 这里的 value 类型是 never,因为所有可能的情况都已处理
return throwError("Unexpected type");
}
详尽性检查:
type Action = 'CREATE' | 'UPDATE' | 'DELETE';
function handleAction(action: Action): string {
switch (action) {
case 'CREATE':
return 'Creating...';
case 'UPDATE':
return 'Updating...';
case 'DELETE':
return 'Deleting...';
default:
// 确保所有情况都被处理
const _exhaustiveCheck: never = action;
throw new Error(`Unhandled action: ${_exhaustiveCheck}`);
}
}
// 如果添加新的 Action 类型,编译器会报错
type ExtendedAction = Action | 'ARCHIVE';
function handleExtendedAction(action: ExtendedAction): string {
switch (action) {
case 'CREATE':
return 'Creating...';
case 'UPDATE':
return 'Updating...';
case 'DELETE':
return 'Deleting...';
// 缺少 'ARCHIVE' 处理,编译器会在 default 中报错
default:
const _exhaustiveCheck: never = action; // 编译错误!
throw new Error(`Unhandled action`);
}
}
实际应用场景:
状态机验证:
type State = 'idle' | 'loading' | 'success' | 'error';
interface AppState {
status: State;
data?: any;
error?: string;
}
function assertNever(value: never): never {
throw new Error(`Unexpected value: ${value}`);
}
function renderStatus(state: AppState): string {
switch (state.status) {
case 'idle':
return 'Ready to start';
case 'loading':
return 'Loading...';
case 'success':
return `Data: ${state.data}`;
case 'error':
return `Error: ${state.error}`;
default:
return assertNever(state.status); // 确保所有状态都被处理
}
}
条件类型中的过滤:
// 过滤掉函数类型
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;
getName: () => string;
setAge: (age: number) => void;
}
type DataOnly = NonFunctionProperties<Example>;
// { name: string; age: number; }
联合类型缩减:
type Exclude<T, U> = T extends U ? never : T;
type WithoutString = Exclude<string | number | boolean, string>; // number | boolean
// 自定义过滤工具
type FilterFlags<Base, Condition> = {
[Key in keyof Base]: Base[Key] extends Condition ? never : Key;
}[keyof Base];
type AllowedNames = FilterFlags<{
name: string;
count: number;
isValid: boolean;
callback: () => void;
}, Function>; // "name" | "count" | "isValid"
never 类型的特性:
How does TypeScript implement type-safe method chaining?
How does TypeScript implement type-safe method chaining?
答案:
TypeScript 通过返回类型为 this 或自定义类型的方法实现类型安全的链式调用,确保每个调用步骤都有正确的类型推断和代码提示。
基础链式调用实现:
使用 this 类型:
class QueryBuilder<T> {
private conditions: string[] = [];
private selectFields: string[] = [];
private orderByField?: string;
private limitCount?: number;
constructor(private tableName: string) {}
// 返回 this 支持链式调用
select(...fields: (keyof T)[]): this {
this.selectFields.push(...fields as string[]);
return this;
}
where(condition: string): this {
this.conditions.push(condition);
return this;
}
orderBy(field: keyof T): this {
this.orderByField = field as string;
return this;
}
limit(count: number): this {
this.limitCount = count;
return this;
}
// 终结方法,返回查询字符串
build(): string {
let query = `SELECT ${this.selectFields.join(', ')} FROM ${this.tableName}`;
if (this.conditions.length > 0) {
query += ` WHERE ${this.conditions.join(' AND ')}`;
}
if (this.orderByField) {
query += ` ORDER BY ${this.orderByField}`;
}
if (this.limitCount) {
query += ` LIMIT ${this.limitCount}`;
}
return query;
}
}
// 使用示例
interface User {
id: number;
name: string;
email: string;
age: number;
}
const query = new QueryBuilder<User>('users')
.select('name', 'email') // 类型安全的字段选择
.where('age > 18')
.orderBy('name') // 类型检查字段名
.limit(10)
.build();
console.log(query);
// SELECT name, email FROM users WHERE age > 18 ORDER BY name LIMIT 10
状态管理的链式调用:
// 状态转换的类型安全链式调用
class StateMachine<TState extends string, TEvent extends string> {
private currentState: TState;
private transitions: Record<TState, Partial<Record<TEvent, TState>>> = {} as any;
constructor(initialState: TState) {
this.currentState = initialState;
}
// 定义状态转换规则
from(state: TState): StateTransitionBuilder<TState, TEvent> {
return new StateTransitionBuilder(state, this.transitions);
}
// 触发事件
trigger(event: TEvent): this {
const nextState = this.transitions[this.currentState]?.[event];
if (nextState) {
this.currentState = nextState;
}
return this;
}
getState(): TState {
return this.currentState;
}
}
class StateTransitionBuilder<TState extends string, TEvent extends string> {
constructor(
private state: TState,
private transitions: Record<TState, Partial<Record<TEvent, TState>>>
) {}
to(targetState: TState): EventBuilder<TState, TEvent> {
return new EventBuilder(this.state, targetState, this.transitions);
}
}
class EventBuilder<TState extends string, TEvent extends string> {
constructor(
private fromState: TState,
private toState: TState,
private transitions: Record<TState, Partial<Record<TEvent, TState>>>
) {}
on(event: TEvent): StateTransitionBuilder<TState, TEvent> {
if (!this.transitions[this.fromState]) {
this.transitions[this.fromState] = {};
}
this.transitions[this.fromState]![event] = this.toState;
return new StateTransitionBuilder(this.fromState, this.transitions);
}
}
// 使用示例
type OrderState = 'pending' | 'confirmed' | 'shipped' | 'delivered' | 'cancelled';
type OrderEvent = 'confirm' | 'ship' | 'deliver' | 'cancel';
const orderStateMachine = new StateMachine<OrderState, OrderEvent>('pending')
.from('pending').to('confirmed').on('confirm')
.from('pending').to('cancelled').on('cancel')
.from('confirmed').to('shipped').on('ship')
.from('confirmed').to('cancelled').on('cancel')
.from('shipped').to('delivered').on('deliver');
// 类型安全的状态转换
orderStateMachine
.trigger('confirm') // pending -> confirmed
.trigger('ship') // confirmed -> shipped
.trigger('deliver'); // shipped -> delivered
高级链式调用模式:
条件链式调用:
class ConditionalChain<T> {
constructor(private value: T) {}
// 条件执行
when<R>(
condition: (value: T) => boolean,
action: (value: T) => R
): ConditionalChain<R | T> {
if (condition(this.value)) {
return new ConditionalChain(action(this.value));
}
return new ConditionalChain(this.value);
}
// 映射转换
map<R>(fn: (value: T) => R): ConditionalChain<R> {
return new ConditionalChain(fn(this.value));
}
// 过滤
filter(predicate: (value: T) => boolean): ConditionalChain<T | null> {
return new ConditionalChain(predicate(this.value) ? this.value : null);
}
// 获取最终值
get(): T {
return this.value;
}
}
// 使用示例
const result = new ConditionalChain(10)
.when(x => x > 5, x => x * 2) // 10 > 5, so 10 * 2 = 20
.map(x => x + 1) // 20 + 1 = 21
.when(x => x % 2 === 0, x => x / 2) // 21 is odd, no change
.filter(x => x > 15) // 21 > 15, so keep it
.get(); // 21
异步链式调用:
class AsyncChain<T> {
constructor(private promise: Promise<T>) {}
// 异步映射
map<R>(fn: (value: T) => R | Promise<R>): AsyncChain<R> {
return new AsyncChain(this.promise.then(fn));
}
// 异步过滤
filter(predicate: (value: T) => boolean | Promise<boolean>): AsyncChain<T | null> {
return new AsyncChain(
this.promise.then(async value => {
const shouldKeep = await predicate(value);
return shouldKeep ? value : null;
})
);
}
// 错误处理
catch<R>(handler: (error: any) => R): AsyncChain<T | R> {
return new AsyncChain(this.promise.catch(handler));
}
// 副作用操作
tap(fn: (value: T) => void | Promise<void>): AsyncChain<T> {
return new AsyncChain(
this.promise.then(async value => {
await fn(value);
return value;
})
);
}
// 获取 Promise
toPromise(): Promise<T> {
return this.promise;
}
// 等待结果
async get(): Promise<T> {
return this.promise;
}
}
// 使用示例
async function fetchUserData(id: number): Promise<User> {
return fetch(`/api/users/${id}`).then(res => res.json());
}
const userData = await new AsyncChain(fetchUserData(1))
.map(user => ({ ...user, displayName: `${user.name} (${user.email})` }))
.filter(user => user.age >= 18)
.tap(user => console.log('Processing user:', user.displayName))
.catch(error => ({ id: -1, name: 'Unknown', email: '', age: 0 }))
.get();
实际应用场景:
HTTP 客户端:
class HttpClient {
private url: string = '';
private requestHeaders: Record<string, string> = {};
private requestBody?: any;
private method: string = 'GET';
baseURL(url: string): this {
this.url = url;
return this;
}
path(segment: string): this {
this.url += `/${segment}`;
return this;
}
header(key: string, value: string): this {
this.requestHeaders[key] = value;
return this;
}
headers(headers: Record<string, string>): this {
Object.assign(this.requestHeaders, headers);
return this;
}
auth(token: string): this {
this.requestHeaders['Authorization'] = `Bearer ${token}`;
return this;
}
json(data: any): this {
this.requestBody = JSON.stringify(data);
this.requestHeaders['Content-Type'] = 'application/json';
return this;
}
get(): this {
this.method = 'GET';
return this;
}
post(): this {
this.method = 'POST';
return this;
}
put(): this {
this.method = 'PUT';
return this;
}
delete(): this {
this.method = 'DELETE';
return this;
}
async send<T = any>(): Promise<T> {
const response = await fetch(this.url, {
method: this.method,
headers: this.requestHeaders,
body: this.requestBody
});
return response.json();
}
}
// 使用示例
const client = new HttpClient();
// GET 请求
const users = await client
.baseURL('https://api.example.com')
.path('users')
.header('Accept', 'application/json')
.auth('your-token-here')
.get()
.send<User[]>();
// POST 请求
const newUser = await client
.baseURL('https://api.example.com')
.path('users')
.auth('your-token-here')
.json({ name: '张三', email: '[email protected]' })
.post()
.send<User>();
数据验证链:
class Validator<T> {
private errors: string[] = [];
constructor(private value: T) {}
required(message: string = 'Value is required'): this {
if (this.value == null || this.value === '') {
this.errors.push(message);
}
return this;
}
min(minValue: number, message: string = `Value must be at least ${minValue}`): this {
if (typeof this.value === 'number' && this.value < minValue) {
this.errors.push(message);
}
return this;
}
max(maxValue: number, message: string = `Value must be at most ${maxValue}`): this {
if (typeof this.value === 'number' && this.value > maxValue) {
this.errors.push(message);
}
return this;
}
email(message: string = 'Invalid email format'): this {
if (typeof this.value === 'string') {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(this.value)) {
this.errors.push(message);
}
}
return this;
}
custom(fn: (value: T) => boolean, message: string): this {
if (!fn(this.value)) {
this.errors.push(message);
}
return this;
}
validate(): { isValid: boolean; errors: string[]; value: T } {
return {
isValid: this.errors.length === 0,
errors: [...this.errors],
value: this.value
};
}
}
// 使用示例
function validateUser(user: Partial<User>) {
const nameResult = new Validator(user.name)
.required('Name is required')
.custom(name => typeof name === 'string' && name.length >= 2, 'Name must be at least 2 characters')
.validate();
const emailResult = new Validator(user.email)
.required('Email is required')
.email()
.validate();
const ageResult = new Validator(user.age)
.required('Age is required')
.min(0, 'Age cannot be negative')
.max(120, 'Age seems unrealistic')
.validate();
return {
name: nameResult,
email: emailResult,
age: ageResult,
isValid: nameResult.isValid && emailResult.isValid && ageResult.isValid
};
}
类型安全的关键技巧:
泛型约束确保类型安全:
interface Chainable {
[key: string]: (...args: any[]) => this;
}
class TypeSafeChain<T extends Chainable> {
constructor(private instance: T) {}
call<K extends keyof T>(
method: K,
...args: T[K] extends (...args: infer P) => any ? P : never
): this {
(this.instance[method] as any)(...args);
return this;
}
get(): T {
return this.instance;
}
}
状态类型跟踪:
type BuilderState = 'initial' | 'configured' | 'ready';
class StatefulBuilder<TState extends BuilderState = 'initial'> {
private config: any = {};
configure(options: any): StatefulBuilder<'configured'> {
this.config = options;
return this as any;
}
// 只有在 configured 状态才能调用 build
build(this: StatefulBuilder<'configured'>): any {
return { ...this.config, timestamp: Date.now() };
}
}
// 使用时会有编译时状态检查
const builder = new StatefulBuilder()
.configure({ name: 'test' }) // 现在是 configured 状态
.build(); // 可以调用 build
// const invalid = new StatefulBuilder().build(); // 编译错误!
最佳实践:
this 类型支持继承中的链式调用build(), execute(), get())How to implement a DeepReadonly type?
How to implement a DeepReadonly type?
答案:
DeepReadonly 类型通过递归映射将对象的所有属性(包括嵌套对象)都设置为只读,是高级类型编程的典型应用。
基础实现:
简单版本:
// 基础深度只读实现
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// 使用示例
interface User {
id: number;
name: string;
profile: {
avatar: string;
settings: {
theme: string;
notifications: boolean;
};
};
}
type ReadonlyUser = DeepReadonly<User>;
/*
{
readonly id: number;
readonly name: string;
readonly profile: {
readonly avatar: string;
readonly settings: {
readonly theme: string;
readonly notifications: boolean;
};
};
}
*/
改进版本(处理数组和函数):
// 更完善的深度只读实现
type DeepReadonly<T> = T extends (infer U)[]
? ReadonlyArray<DeepReadonly<U>> // 处理数组
: T extends Function
? T // 函数保持不变
: T extends object
? { readonly [P in keyof T]: DeepReadonly<T[P]> } // 递归处理对象
: T; // 基本类型保持不变
// 测试复杂结构
interface ComplexData {
users: User[];
config: {
api: {
baseUrl: string;
endpoints: string[];
};
features: {
[key: string]: boolean;
};
};
methods: {
getUser: (id: number) => User;
updateUser: (user: User) => void;
};
}
type ReadonlyComplexData = DeepReadonly<ComplexData>;
/*
{
readonly users: ReadonlyArray<DeepReadonly<User>>;
readonly config: {
readonly api: {
readonly baseUrl: string;
readonly endpoints: ReadonlyArray<string>;
};
readonly features: {
readonly [key: string]: boolean;
};
};
readonly methods: {
readonly getUser: (id: number) => User;
readonly updateUser: (user: User) => void;
};
}
*/
高级实现:
处理特殊类型:
// 更精确的深度只读实现
type Primitive = string | number | boolean | bigint | symbol | undefined | null;
type DeepReadonly<T> = T extends Primitive
? T
: T extends (infer U)[]
? ReadonlyArray<DeepReadonly<U>>
: T extends Map<infer K, infer V>
? ReadonlyMap<K, DeepReadonly<V>>
: T extends Set<infer U>
? ReadonlySet<DeepReadonly<U>>
: T extends Function
? T
: T extends Date
? T
: {
readonly [P in keyof T]: DeepReadonly<T[P]>;
};
// 测试各种类型
interface TestData {
str: string;
num: number;
bool: boolean;
date: Date;
arr: number[];
map: Map<string, User>;
set: Set<string>;
fn: () => void;
nested: {
deep: {
value: string;
};
};
}
type ReadonlyTestData = DeepReadonly<TestData>;
可选的深度控制:
// 控制递归深度的深度只读
type DeepReadonlyWithDepth<T, Depth extends number = 10> = [Depth] extends [0]
? T
: T extends Primitive
? T
: T extends (infer U)[]
? ReadonlyArray<DeepReadonlyWithDepth<U, Prev<Depth>>>
: T extends Function
? T
: {
readonly [P in keyof T]: DeepReadonlyWithDepth<T[P], Prev<Depth>>;
};
// 辅助类型:递减数字
type Prev<T extends number> = T extends 0
? 0
: T extends 1
? 0
: T extends 2
? 1
: T extends 3
? 2
: T extends 4
? 3
: T extends 5
? 4
: number;
// 限制递归深度为 2 层
type ShallowReadonly = DeepReadonlyWithDepth<ComplexData, 2>;
实际应用场景:
状态管理:
// Redux 状态类型
interface AppState {
user: {
current: User | null;
preferences: {
theme: 'light' | 'dark';
language: string;
notifications: {
email: boolean;
push: boolean;
};
};
};
data: {
users: User[];
products: Product[];
cache: Map<string, any>;
};
}
// 只读状态,防止意外修改
type ReadonlyAppState = DeepReadonly<AppState>;
// 在 reducer 中使用
function rootReducer(
state: ReadonlyAppState,
action: any
): AppState {
// 只能返回新的状态,不能修改现有状态
return {
...state,
// 状态更新逻辑
};
}
配置对象:
// 应用配置
interface Config {
database: {
host: string;
port: number;
credentials: {
username: string;
password: string;
};
options: {
ssl: boolean;
timeout: number;
pool: {
min: number;
max: number;
};
};
};
api: {
baseUrl: string;
endpoints: Record<string, string>;
rateLimits: number[];
};
}
// 运行时不可修改的配置
type ReadonlyConfig = DeepReadonly<Config>;
function loadConfig(): ReadonlyConfig {
return {
database: {
host: "localhost",
port: 5432,
credentials: {
username: "admin",
password: "secret"
},
options: {
ssl: true,
timeout: 30000,
pool: { min: 2, max: 10 }
}
},
api: {
baseUrl: "https://api.example.com",
endpoints: {
users: "/users",
products: "/products"
},
rateLimits: [100, 1000, 5000]
}
};
}
const config = loadConfig();
// config.database.host = "newhost"; // 编译错误!
API 响应类型:
// API 响应应该是只读的
interface ApiResponse<T> {
data: T;
meta: {
timestamp: Date;
version: string;
pagination?: {
page: number;
total: number;
hasNext: boolean;
};
};
}
type ReadonlyApiResponse<T> = DeepReadonly<ApiResponse<T>>;
async function fetchUsers(): Promise<ReadonlyApiResponse<User[]>> {
const response = await fetch('/api/users');
return response.json();
}
// 使用时防止意外修改响应数据
const users = await fetchUsers();
// users.data.push(newUser); // 编译错误!
// users.meta.timestamp = new Date(); // 编译错误!
扩展应用:
// 可逆的深度只读(提供解除只读的工具)
type DeepMutable<T> = T extends ReadonlyArray<infer U>
? DeepMutable<U>[]
: T extends Function
? T
: T extends object
? { -readonly [P in keyof T]: DeepMutable<T[P]> }
: T;
// 选择性深度只读
type DeepReadonlyExcept<T, K extends keyof any = never> = {
readonly [P in keyof T]: P extends K
? T[P]
: T[P] extends object
? DeepReadonlyExcept<T[P], K>
: T[P];
};
// 排除某些属性的深度只读
type PartiallyReadonly = DeepReadonlyExcept<User, 'id'>;
// id 属性不会被设为只读,其他都是只读的
最佳实践:
What important features were added in TypeScript 4.x?
What important features were added in TypeScript 4.x?
- 考察点:最新特性与技术发展趋势。
答案:
TypeScript 4.x 系列引入了许多重要特性,显著提升了类型系统的表达能力、开发体验和性能,是 TypeScript 发展史上的重要里程碑。
TypeScript 4.0 主要特性:
可变元组类型(Variadic Tuple Types):
// 4.0 之前无法表达的类型
type Concat<T extends readonly unknown[], U extends readonly unknown[]> = [...T, ...U];
type Result1 = Concat<[1, 2], [3, 4]>; // [1, 2, 3, 4]
type Result2 = Concat<['a'], [1, 2, 3]>; // ['a', 1, 2, 3]
// 函数参数展开
function curry<T extends readonly unknown[], U extends readonly unknown[], R>(
f: (...args: [...T, ...U]) => R,
...first: T
) {
return (...second: U): R => f(...first, ...second);
}
// 使用示例
function add(a: number, b: number, c: number): number {
return a + b + c;
}
const curriedAdd = curry(add, 1, 2); // (c: number) => number
const result = curriedAdd(3); // 6
标记联合类型(Labeled Tuple Elements):
// 为元组元素添加标签
type Range = [start: number, end: number];
type Point3D = [x: number, y: number, z: number];
// 函数参数更清晰
function createRange(...args: Range): number[] {
const [start, end] = args;
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
}
// 更好的代码提示
function distance(from: Point3D, to: Point3D): number {
const [x1, y1, z1] = from;
const [x2, y2, z2] = to;
return Math.sqrt((x2-x1)**2 + (y2-y1)**2 + (z2-z1)**2);
}
TypeScript 4.1 主要特性:
模板字面量类型(Template Literal Types):
// 动态创建字符串类型
type World = "world";
type Greeting = `hello ${World}`; // "hello world"
// 实际应用:CSS 属性
type CSSProperties = 'margin' | 'padding' | 'border';
type CSSDirections = 'top' | 'right' | 'bottom' | 'left';
type CSSProperty = `${CSSProperties}-${CSSDirections}`;
// "margin-top" | "margin-right" | ... | "border-left"
// 事件处理器类型
type EventNames = 'click' | 'scroll' | 'mousemove';
type EventHandlers = {
[K in EventNames as `on${Capitalize<K>}`]: (event: Event) => void;
};
// { onClick: (event: Event) => void; onScroll: ...; onMousemove: ... }
键重映射(Key Remapping):
// 重新映射对象键
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface Person {
name: string;
age: number;
email: string;
}
type PersonGetters = Getters<Person>;
// {
// getName: () => string;
// getAge: () => number;
// getEmail: () => string;
// }
// 过滤属性
type RemoveKindField<T> = {
[K in keyof T as K extends 'kind' ? never : K]: T[K];
};
interface WithKind {
kind: string;
name: string;
value: number;
}
type WithoutKind = RemoveKindField<WithKind>; // { name: string; value: number; }
TypeScript 4.2 主要特性:
更智能的类型别名保留:
// 保留原始类型别名信息
type BasicPrimitive = number | string | boolean;
type Object = { x: number; y: string; };
type Tuple = [number, string];
// 在错误信息中会显示更清晰的类型名称
function process(value: BasicPrimitive): void {
// TypeScript 会显示 "BasicPrimitive" 而不是 "number | string | boolean"
}
前导/中间 Rest 元素:
// Rest 元素可以在中间位置
type StringNumberBooleans = [string, number, ...boolean[]];
type StringBooleansNumber = [string, ...boolean[], number];
type BooleansStringNumber = [...boolean[], string, number];
let a: StringNumberBooleans = ["hello", 42, true, false, true];
let b: StringBooleansNumber = ["hello", true, false, 42];
let c: BooleansStringNumber = [true, false, true, "hello", 42];
TypeScript 4.3 主要特性:
override 关键字:
class Base {
someMethod(): void {
console.log("Base method");
}
}
class Derived extends Base {
// 明确标记重写方法
override someMethod(): void {
console.log("Derived method");
}
// 编译器会检查是否真的在重写
// override nonExistentMethod(): void {} // 错误!
}
静态索引签名:
class MyClass {
// 静态属性的索引签名
static [s: string]: boolean | ((x: number) => number);
static a = true;
static b = false;
static f = (x: number) => x * 2;
}
TypeScript 4.4 主要特性:
控制流分析改进(符号别名):
function foo(arg: unknown) {
const argIsString = typeof arg === "string";
if (argIsString) {
// TypeScript 现在知道 arg 是 string 类型
console.log(arg.toUpperCase());
}
}
精确的可选属性类型:
interface Person {
name: string;
age?: number; // 可能是 number 或 undefined,但不能是其他类型
}
// 4.4 中更严格的检查
const person: Person = {
name: "Alice",
age: undefined // 明确允许
};
// const invalid: Person = { name: "Bob", age: null }; // 错误!
TypeScript 4.5 主要特性:
Awaited 类型:
// 内置的 Awaited 工具类型
type A = Awaited<Promise<string>>; // string
type B = Awaited<Promise<Promise<number>>>; // number
type C = Awaited<boolean | Promise<number>>; // boolean | number
// 自定义实现参考
type MyAwaited<T> = T extends null | undefined
? T
: T extends object & { then(onfulfilled: infer F, ...args: any): any }
? F extends (value: infer V, ...args: any) => any
? Awaited<V>
: never
: T;
类型修饰符改进:
// 更精确的 const 断言
const colors = ['red', 'green', 'blue'] as const;
type Color = typeof colors[number]; // "red" | "green" | "blue"
// 递归条件类型尾调用优化
type InfiniteLoop<T> = T extends string ? InfiniteLoop<T> : never;
TypeScript 4.6 主要特性:
代码操作和自动导入改进:
// 更好的自动导入建议
// 支持包导出的子路径
import { Component } from "some-ui-library/components";
import { utils } from "some-library/utils";
析构赋值中的 rest 类型改进:
interface Options {
width?: number;
height?: number;
color?: string;
}
function processOptions(options: Options) {
const { width, height, ...rest } = options;
// rest 的类型是 { color?: string }
}
TypeScript 4.7 主要特性:
instantiation 表达式:
function makeBox<T>(value: T) {
return { value };
}
// 实例化表达式
const makeStringBox = makeBox<string>;
const stringBox = makeStringBox("hello");
// 数组方法的特化
const numbers = [1, 2, 3, 4, 5];
const strings = numbers.map(String); // 明确的类型转换
variance 注解(实验性):
interface Producer<out T> {
produce(): T;
}
interface Consumer<in T> {
consume(value: T): void;
}
// 协变和逆变的显式标记
let p1: Producer<string>;
let p2: Producer<string | number> = p1; // OK,协变
TypeScript 4.8 主要特性:
交集和联合类型改进:
// 更好的交集类型处理
type A = { a: string } & { b: number };
type B = { a: string; b: number }; // 现在 A 和 B 更一致
// 未知类型的改进
function process<T>(value: T): T extends string ? string : T {
return value as any;
}
bind、call、apply 的改进:
function greet(name: string, age: number): string {
return `Hello ${name}, you are ${age} years old`;
}
// 更好的类型推断
const boundGreet = greet.bind(null, "Alice");
const result = boundGreet(25); // string
TypeScript 4.9 主要特性:
type Colors = "red" | "green" | "blue";
type RGB = [red: number, green: number, blue: number];
const palette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255]
} satisfies Record<Colors, string | RGB>;
// palette.red 被推断为 [number, number, number]
// palette.green 被推断为 string
const redComponent = palette.red.at(0); // number | undefined
const greenNormalized = palette.green.toUpperCase(); // string
实际应用价值:
// 综合运用 4.x 特性的实际示例
type APIEndpoints = {
'GET /users': { response: User[] };
'POST /users': { body: CreateUserRequest; response: User };
'GET /users/:id': { params: { id: string }; response: User };
'PUT /users/:id': { params: { id: string }; body: UpdateUserRequest; response: User };
};
type ExtractMethod<T> = T extends `${infer M} ${string}` ? M : never;
type ExtractPath<T> = T extends `${string} ${infer P}` ? P : never;
type APIClient = {
[K in keyof APIEndpoints as `${Lowercase<ExtractMethod<K>>}${Capitalize<string & ExtractPath<K>>}`]:
APIEndpoints[K] extends { params: infer P; body: infer B; response: infer R }
? (params: P, body: B) => Promise<R>
: APIEndpoints[K] extends { params: infer P; response: infer R }
? (params: P) => Promise<R>
: APIEndpoints[K] extends { body: infer B; response: infer R }
? (body: B) => Promise<R>
: APIEndpoints[K] extends { response: infer R }
? () => Promise<R>
: never;
};
// 生成的类型:
// {
// getUsers: () => Promise<User[]>;
// postUsers: (body: CreateUserRequest) => Promise<User>;
// getUsersId: (params: { id: string }) => Promise<User>;
// putUsersId: (params: { id: string }, body: UpdateUserRequest) => Promise<User>;
// }
升级建议:
satisfies 操作符平衡类型安全和推断override 关键字提高继承安全性Awaited 简化异步代码What are Covariance and Contravariance? How are they reflected in TypeScript?
What are Covariance and Contravariance? How are they reflected in TypeScript?
- 考察点:型变概念与实际体现。
答案:
协变和逆变是类型系统中描述类型参数在子类型关系中如何表现的概念。协变保持子类型关系的方向,逆变则反转子类型关系的方向,对于构建类型安全的系统至关重要。
基本概念:
协变(Covariance):
// 如果 Dog extends Animal,那么 Array<Dog> 也可以赋值给 Array<Animal>
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Dog extends Animal {
breed: string;
constructor(name: string, breed: string) {
super(name);
this.breed = breed;
}
bark(): void {
console.log("Woof!");
}
}
class Cat extends Animal {
meow(): void {
console.log("Meow!");
}
}
// 协变:数组的类型参数保持子类型关系
let dogs: Dog[] = [new Dog("Buddy", "Golden Retriever")];
let animals: Animal[] = dogs; // ✅ 协变,允许赋值
// 这是安全的,因为我们只是读取
animals.forEach(animal => console.log(animal.name));
逆变(Contravariance):
// 函数参数类型是逆变的
type AnimalHandler = (animal: Animal) => void;
type DogHandler = (dog: Dog) => void;
const handleAnimal: AnimalHandler = (animal: Animal) => {
console.log(`Handling animal: ${animal.name}`);
};
const handleDog: DogHandler = (dog: Dog) => {
console.log(`Handling dog: ${dog.name}, breed: ${dog.breed}`);
};
// 逆变:可以将 AnimalHandler 赋值给 DogHandler
const dogHandler: DogHandler = handleAnimal; // ✅ 逆变,允许赋值
// 这是安全的,因为 handleAnimal 可以处理任何 Animal(包括 Dog)
dogHandler(new Dog("Max", "Labrador"));
// 但反过来不行
// const animalHandler: AnimalHandler = handleDog; // ❌ 错误!
// 因为 handleDog 只能处理 Dog,不能处理所有 Animal
TypeScript 中的协变体现:
数组和只读数组:
// 普通数组(在读取位置协变,但写入不安全)
let dogArray: Dog[] = [new Dog("Buddy", "Golden Retriever")];
let animalArray: Animal[] = dogArray; // 协变
// 这会导致运行时错误!
// animalArray.push(new Cat()); // 编译时允许,但运行时会有问题
// 只读数组(安全的协变)
let readonlyDogs: readonly Dog[] = [new Dog("Buddy", "Golden Retriever")];
let readonlyAnimals: readonly Animal[] = readonlyDogs; // ✅ 安全的协变
// 不能修改,所以是类型安全的
// readonlyAnimals.push(new Cat()); // ❌ 编译错误
Promise 类型:
// Promise 是协变的
async function getDog(): Promise<Dog> {
return new Dog("Rover", "Beagle");
}
// 可以将 Promise<Dog> 赋值给 Promise<Animal>
const animalPromise: Promise<Animal> = getDog(); // ✅ 协变
animalPromise.then(animal => {
console.log(animal.name); // 安全访问
// animal.bark(); // ❌ 不能调用 Dog 特有方法
});
函数返回值:
// 函数返回值是协变的
type AnimalFactory = () => Animal;
type DogFactory = () => Dog;
const createDog: DogFactory = () => new Dog("Fido", "Poodle");
const createAnimal: AnimalFactory = createDog; // ✅ 返回值协变
const animal = createAnimal(); // 返回 Animal 类型
console.log(animal.name);
TypeScript 中的逆变体现:
函数参数:
// 在严格模式下,函数参数是逆变的
interface EventHandler<T> {
handle(event: T): void;
}
interface MouseEvent {
x: number;
y: number;
}
interface ClickEvent extends MouseEvent {
button: number;
}
class MouseHandler implements EventHandler<MouseEvent> {
handle(event: MouseEvent): void {
console.log(`Mouse at (${event.x}, ${event.y})`);
}
}
// 逆变:MouseHandler 可以赋值给 EventHandler<ClickEvent>
const clickHandler: EventHandler<ClickEvent> = new MouseHandler(); // ✅ 逆变
clickHandler.handle({ x: 100, y: 200, button: 1 }); // 安全调用
比较函数:
// 比较函数参数是逆变的
interface Comparable<T> {
compare(a: T, b: T): number;
}
const animalComparer: Comparable<Animal> = {
compare(a: Animal, b: Animal): number {
return a.name.localeCompare(b.name);
}
};
// 可以用 Animal 比较器来比较 Dog
const dogComparer: Comparable<Dog> = animalComparer; // ✅ 逆变
const dogs = [
new Dog("Alice", "Husky"),
new Dog("Bob", "Corgi")
];
dogs.sort(dogComparer.compare); // 安全使用
双变(Bivariance)问题:
// TypeScript 默认情况下方法参数是双变的(既协变又逆变)
interface Handler<T> {
handle(value: T): void;
}
class AnimalHandler implements Handler<Animal> {
handle(animal: Animal): void {
console.log(`Handling: ${animal.name}`);
}
}
class DogHandler implements Handler<Dog> {
handle(dog: Dog): void {
console.log(`Handling dog: ${dog.name}, breed: ${dog.breed}`);
}
}
// 双变允许这两种赋值(但可能不安全)
let h1: Handler<Animal> = new DogHandler(); // 协变方向
let h2: Handler<Dog> = new AnimalHandler(); // 逆变方向
// 使用 strictFunctionTypes 禁用双变
// 在 tsconfig.json 中设置 "strictFunctionTypes": true
实际应用场景:
事件系统设计:
// 协变用于事件数据,逆变用于事件处理器
interface Event {
timestamp: Date;
}
interface UserEvent extends Event {
userId: string;
}
interface ClickEvent extends UserEvent {
x: number;
y: number;
}
// 事件发布器(协变)
interface EventPublisher<T extends Event> {
publish(event: T): void;
subscribe(handler: (event: T) => void): void;
}
// 可以将更具体的发布器赋值给更通用的
let clickPublisher: EventPublisher<ClickEvent>;
let userEventPublisher: EventPublisher<UserEvent> = clickPublisher; // ✅ 协变
// 事件处理器(逆变)
const handleUserEvent = (event: UserEvent) => {
console.log(`User ${event.userId} triggered event at ${event.timestamp}`);
};
const handleClickEvent: (event: ClickEvent) => void = handleUserEvent; // ✅ 逆变
状态管理:
// 状态读取器(协变)
interface StateReader<T> {
get(): T;
}
// 状态写入器(逆变)
interface StateWriter<T> {
set(value: T): void;
}
// 完整状态管理器
interface State<T> extends StateReader<T>, StateWriter<T> {
update(updater: (current: T) => T): void;
}
interface AppState {
user: User | null;
theme: 'light' | 'dark';
}
interface UserState {
user: User | null;
}
// 读取器协变
let appStateReader: StateReader<AppState>;
let userStateReader: StateReader<UserState> = appStateReader; // ❌ 需要更精确的类型
// 写入器逆变
let userStateWriter: StateWriter<UserState>;
let appStateWriter: StateWriter<AppState> = userStateWriter; // ❌ 需要更精确的类型
API 设计:
// 泛型约束中的协变和逆变
interface Repository<T> {
find(id: string): Promise<T | null>;
save(entity: T): Promise<void>;
query(predicate: (entity: T) => boolean): Promise<T[]>;
}
// 协变:可以返回更具体的类型
class UserRepository implements Repository<User> {
async find(id: string): Promise<User | null> {
// 实现细节
return null;
}
async save(user: User): Promise<void> {
// 实现细节
}
// 逆变:查询谓词可以接受更通用的类型
async query(predicate: (user: User) => boolean): Promise<User[]> {
// 实现细节
return [];
}
}
// 工厂函数利用协变
function createRepository<T extends BaseEntity>(): Repository<T> {
return new GenericRepository<T>();
}
const userRepo = createRepository<User>(); // Repository<User>
最佳实践和配置:
// tsconfig.json 配置
{
"compilerOptions": {
"strict": true,
"strictFunctionTypes": true, // 启用严格函数类型检查(禁用双变)
"strictNullChecks": true,
"noImplicitReturns": true
}
}
// 显式协变和逆变注解(TypeScript 4.7+,实验性)
interface Producer<out T> { // 协变
produce(): T;
}
interface Consumer<in T> { // 逆变
consume(item: T): void;
}
interface Processor<in TInput, out TOutput> { // 混合
process(input: TInput): TOutput;
}
类型安全指导原则:
How to implement the Singleton pattern in TypeScript while ensuring type safety?
How to implement the Singleton pattern in TypeScript while ensuring type safety?
- 考察点:设计模式实现与类型技巧综合应用。
答案:
TypeScript 中的单例模式实现需要考虑类型安全、线程安全(在某些环境中)、扩展性和测试友好性,有多种实现方式可以满足不同的需求。
经典单例模式实现:
基础单例类:
class Logger {
private static instance: Logger;
private logLevel: 'debug' | 'info' | 'warn' | 'error';
// 私有构造函数防止外部实例化
private constructor() {
this.logLevel = 'info';
}
// 获取单例实例的静态方法
public static getInstance(): Logger {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
public setLogLevel(level: 'debug' | 'info' | 'warn' | 'error'): void {
this.logLevel = level;
}
public log(message: string, level: 'debug' | 'info' | 'warn' | 'error' = 'info'): void {
const levels = { debug: 0, info: 1, warn: 2, error: 3 };
if (levels[level] >= levels[this.logLevel]) {
console.log(`[${level.toUpperCase()}] ${new Date().toISOString()}: ${message}`);
}
}
// 防止克隆
public clone(): never {
throw new Error("Cannot clone singleton instance");
}
}
// 使用示例
const logger1 = Logger.getInstance();
const logger2 = Logger.getInstance();
console.log(logger1 === logger2); // true
logger1.setLogLevel('debug');
logger1.log('Debug message', 'debug');
logger2.log('Info message'); // 同一个实例,共享状态
泛型单例基类:
abstract class Singleton<T> {
private static instances: Map<any, any> = new Map();
constructor() {
const constructor = this.constructor as new () => T;
if (Singleton.instances.has(constructor)) {
throw new Error(`Instance of ${constructor.name} already exists. Use getInstance() instead.`);
}
Singleton.instances.set(constructor, this);
}
public static getInstance<T>(this: new () => T): T {
if (!Singleton.instances.has(this)) {
new this();
}
return Singleton.instances.get(this);
}
}
// 继承单例基类
class DatabaseConnection extends Singleton<DatabaseConnection> {
private connectionString: string;
private isConnected: boolean = false;
constructor() {
super();
this.connectionString = process.env.DB_URL || 'localhost:5432';
}
public connect(): void {
if (!this.isConnected) {
console.log(`Connecting to ${this.connectionString}`);
this.isConnected = true;
}
}
public disconnect(): void {
if (this.isConnected) {
console.log('Disconnecting from database');
this.isConnected = false;
}
}
public isConnectionActive(): boolean {
return this.isConnected;
}
}
// 使用示例
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();
console.log(db1 === db2); // true
现代 TypeScript 单例实现:
模块单例(推荐):
// config-manager.ts
interface AppConfig {
apiUrl: string;
timeout: number;
retries: number;
features: Record<string, boolean>;
}
class ConfigManager {
private config: AppConfig;
private readonly defaultConfig: AppConfig = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3,
features: {}
};
constructor() {
this.config = { ...this.defaultConfig };
this.loadConfig();
}
private loadConfig(): void {
// 从环境变量、配置文件等加载配置
if (process.env.API_URL) {
this.config.apiUrl = process.env.API_URL;
}
if (process.env.TIMEOUT) {
this.config.timeout = parseInt(process.env.TIMEOUT, 10);
}
}
public get<K extends keyof AppConfig>(key: K): AppConfig[K] {
return this.config[key];
}
public set<K extends keyof AppConfig>(key: K, value: AppConfig[K]): void {
this.config[key] = value;
}
public updateConfig(updates: Partial<AppConfig>): void {
this.config = { ...this.config, ...updates };
}
public getFullConfig(): Readonly<AppConfig> {
return Object.freeze({ ...this.config });
}
}
// 导出单例实例
export const configManager = new ConfigManager();
// 使用示例
// import { configManager } from './config-manager';
// const apiUrl = configManager.get('apiUrl');
// configManager.set('timeout', 10000);
工厂函数单例:
// cache-manager.ts
interface CacheOptions {
maxSize: number;
ttl: number; // Time to live in milliseconds
}
interface CacheEntry<T> {
value: T;
timestamp: number;
ttl: number;
}
class CacheManager<T = any> {
private cache = new Map<string, CacheEntry<T>>();
private readonly options: CacheOptions;
constructor(options: CacheOptions) {
this.options = options;
// 定期清理过期缓存
setInterval(() => this.cleanup(), this.options.ttl);
}
public set(key: string, value: T, ttl?: number): void {
// 检查缓存大小限制
if (this.cache.size >= this.options.maxSize && !this.cache.has(key)) {
// 移除最旧的条目
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(key, {
value,
timestamp: Date.now(),
ttl: ttl ?? this.options.ttl
});
}
public get(key: string): T | undefined {
const entry = this.cache.get(key);
if (!entry) return undefined;
// 检查是否过期
if (Date.now() - entry.timestamp > entry.ttl) {
this.cache.delete(key);
return undefined;
}
return entry.value;
}
public has(key: string): boolean {
return this.get(key) !== undefined;
}
public delete(key: string): boolean {
return this.cache.delete(key);
}
public clear(): void {
this.cache.clear();
}
public size(): number {
this.cleanup(); // 清理过期条目后返回大小
return this.cache.size;
}
private cleanup(): void {
const now = Date.now();
for (const [key, entry] of this.cache.entries()) {
if (now - entry.timestamp > entry.ttl) {
this.cache.delete(key);
}
}
}
}
// 单例工厂函数
let cacheInstance: CacheManager | null = null;
export function getCacheManager(options?: CacheOptions): CacheManager {
if (!cacheInstance) {
cacheInstance = new CacheManager(options || { maxSize: 100, ttl: 300000 });
}
return cacheInstance;
}
// 类型安全的特定缓存管理器
export function getTypedCacheManager<T>(options?: CacheOptions): CacheManager<T> {
return getCacheManager(options) as CacheManager<T>;
}
// 使用示例
interface User {
id: number;
name: string;
email: string;
}
const userCache = getTypedCacheManager<User>({ maxSize: 50, ttl: 600000 });
userCache.set('user:1', { id: 1, name: 'Alice', email: '[email protected]' });
const user = userCache.get('user:1'); // 类型为 User | undefined
高级单例实现:
依赖注入容器单例:
// dependency-container.ts
type Constructor<T = {}> = new (...args: any[]) => T;
type ServiceIdentifier<T = any> = string | symbol | Constructor<T>;
interface ServiceDefinition {
factory: () => any;
singleton: boolean;
instance?: any;
}
class DependencyContainer {
private static instance: DependencyContainer;
private services = new Map<ServiceIdentifier, ServiceDefinition>();
private constructor() {}
public static getInstance(): DependencyContainer {
if (!DependencyContainer.instance) {
DependencyContainer.instance = new DependencyContainer();
}
return DependencyContainer.instance;
}
// 注册单例服务
public registerSingleton<T>(
identifier: ServiceIdentifier<T>,
factory: () => T
): this {
this.services.set(identifier, {
factory,
singleton: true
});
return this;
}
// 注册瞬态服务
public registerTransient<T>(
identifier: ServiceIdentifier<T>,
factory: () => T
): this {
this.services.set(identifier, {
factory,
singleton: false
});
return this;
}
// 注册类
public registerClass<T>(
identifier: ServiceIdentifier<T>,
constructor: Constructor<T>,
singleton: boolean = true
): this {
this.services.set(identifier, {
factory: () => new constructor(),
singleton
});
return this;
}
// 解析服务
public resolve<T>(identifier: ServiceIdentifier<T>): T {
const service = this.services.get(identifier);
if (!service) {
throw new Error(`Service ${String(identifier)} not registered`);
}
if (service.singleton) {
if (!service.instance) {
service.instance = service.factory();
}
return service.instance;
}
return service.factory();
}
// 检查服务是否已注册
public isRegistered<T>(identifier: ServiceIdentifier<T>): boolean {
return this.services.has(identifier);
}
// 清除所有注册
public clear(): void {
this.services.clear();
}
}
// 便利函数
export const container = DependencyContainer.getInstance();
// 装饰器支持
export function Injectable<T extends Constructor>(constructor: T) {
return class extends constructor {
static getInstance() {
return container.resolve(constructor);
}
};
}
// 使用示例
interface IEmailService {
sendEmail(to: string, subject: string, body: string): Promise<void>;
}
@Injectable
class EmailService implements IEmailService {
async sendEmail(to: string, subject: string, body: string): Promise<void> {
console.log(`Sending email to ${to}: ${subject}`);
// 实际发送邮件的逻辑
}
}
interface IUserService {
createUser(userData: any): Promise<User>;
}
@Injectable
class UserService implements IUserService {
constructor(private emailService: IEmailService) {}
async createUser(userData: any): Promise<User> {
const user = { ...userData, id: Date.now() };
await this.emailService.sendEmail(
user.email,
'Welcome!',
'Welcome to our service'
);
return user;
}
}
// 注册服务
container
.registerClass('EmailService', EmailService)
.registerSingleton('UserService', () =>
new UserService(container.resolve('EmailService'))
);
// 使用
const userService = container.resolve<IUserService>('UserService');
状态管理单例:
// state-manager.ts
type StateListener<T> = (newState: T, oldState: T) => void;
type StateUpdater<T> = (currentState: T) => Partial<T>;
class StateManager<T extends Record<string, any>> {
private static instances = new Map<string, StateManager<any>>();
private state: T;
private listeners = new Set<StateListener<T>>();
private readonly stateKey: string;
private constructor(stateKey: string, initialState: T) {
this.stateKey = stateKey;
this.state = { ...initialState };
}
public static getInstance<T extends Record<string, any>>(
stateKey: string,
initialState: T
): StateManager<T> {
if (!StateManager.instances.has(stateKey)) {
StateManager.instances.set(stateKey, new StateManager(stateKey, initialState));
}
return StateManager.instances.get(stateKey);
}
// 获取当前状态
public getState(): Readonly<T> {
return Object.freeze({ ...this.state });
}
// 获取特定属性
public get<K extends keyof T>(key: K): T[K] {
return this.state[key];
}
// 更新状态
public setState(updates: Partial<T> | StateUpdater<T>): void {
const oldState = { ...this.state };
if (typeof updates === 'function') {
const newUpdates = updates(this.state);
this.state = { ...this.state, ...newUpdates };
} else {
this.state = { ...this.state, ...updates };
}
// 通知监听者
this.listeners.forEach(listener => listener(this.state, oldState));
}
// 订阅状态变化
public subscribe(listener: StateListener<T>): () => void {
this.listeners.add(listener);
// 返回取消订阅函数
return () => {
this.listeners.delete(listener);
};
}
// 重置状态
public reset(newState: T): void {
const oldState = { ...this.state };
this.state = { ...newState };
this.listeners.forEach(listener => listener(this.state, oldState));
}
// 清除所有监听者
public clearListeners(): void {
this.listeners.clear();
}
}
// 使用示例
interface AppState {
user: User | null;
theme: 'light' | 'dark';
language: string;
isLoading: boolean;
}
const initialAppState: AppState = {
user: null,
theme: 'light',
language: 'en',
isLoading: false
};
export const appStateManager = StateManager.getInstance('app', initialAppState);
// 使用状态管理器
appStateManager.subscribe((newState, oldState) => {
console.log('App state changed:', { oldState, newState });
});
appStateManager.setState({ theme: 'dark', isLoading: true });
const currentUser = appStateManager.get('user');
const currentTheme = appStateManager.get('theme');
测试友好的单例实现:
// testable-singleton.ts
interface SingletonOptions {
allowReset?: boolean;
testMode?: boolean;
}
class TestableLogger {
private static instance: TestableLogger | null = null;
private static options: SingletonOptions = { allowReset: false };
private logs: string[] = [];
private constructor() {}
public static getInstance(options?: SingletonOptions): TestableLogger {
if (options) {
TestableLogger.options = { ...TestableLogger.options, ...options };
}
if (!TestableLogger.instance) {
TestableLogger.instance = new TestableLogger();
}
return TestableLogger.instance;
}
public static reset(): void {
if (TestableLogger.options.allowReset) {
TestableLogger.instance = null;
} else {
throw new Error('Reset not allowed. Enable allowReset option.');
}
}
public log(message: string): void {
const logEntry = `${new Date().toISOString()}: ${message}`;
this.logs.push(logEntry);
if (!TestableLogger.options.testMode) {
console.log(logEntry);
}
}
public getLogs(): readonly string[] {
return [...this.logs];
}
public clearLogs(): void {
this.logs = [];
}
}
// 测试示例
describe('TestableLogger', () => {
beforeEach(() => {
// 在测试模式下允许重置
TestableLogger.reset();
});
it('should be a singleton', () => {
const logger1 = TestableLogger.getInstance({ testMode: true, allowReset: true });
const logger2 = TestableLogger.getInstance();
expect(logger1).toBe(logger2);
});
it('should log messages', () => {
const logger = TestableLogger.getInstance({ testMode: true, allowReset: true });
logger.log('Test message');
expect(logger.getLogs()).toHaveLength(1);
expect(logger.getLogs()[0]).toContain('Test message');
});
});
最佳实践总结: