核心类型系统✅
本主题涵盖TypeScript的核心类型系统,包括基础类型、联合类型、类型断言、类型守卫、类型兼容性等面试高频考点。
TypeScript中有哪些基础类型,各自的作用是什么?
答案
核心类型分类:
TypeScript的基础类型可以分为以下几类:
- 原始类型:
string
、number
、boolean
、symbol
、bigint
- 特殊类型:
any
、unknown
、never
、void
- 空值类型:
null
、undefined
- 对象类型:
object
、{}
字面量类型
示例说明:
- 交互式演示
- 类型检查概念
// TypeScript中的类型检查示例
function processValue(value: string | number): string {
if (typeof value === 'string') {
return value.toUpperCase();
}
return value.toString();
}
// 使用示例
console.log(processValue("hello")); // "HELLO"
console.log(processValue(42)); // "42"
面试官视角:
该题考察TypeScript类型系统基础,通过此题可以进一步探讨:
- 要点清单: 能说出5-6种基础类型及其用途;理解原始类型vs对象类型区别;知道特殊类型的应用场景
- 加分项: 提到
symbol
、bigint
新类型;解释{}
和object
的区别;理解类型层次关系 - 常见失误: 混淆
null
和undefined
;不理解never
类型的意义;把any
当作默认选择
延伸阅读:
- TypeScript Handbook - Basic Types — 官方基础类型指南
- TypeScript Deep Dive - TypeScript的类型系统 — 类型系统深入解析
any、never、unknown、null、undefined和void有什么区别?
答案
核心概念:
每种特殊类型都有明确的使用场景和安全级别:
any
: 类型检查的"逃生舱",绕过所有类型检查unknown
: 类型安全的any
,使用前必须类型缩减never
: 永远不存在值的类型,常用于穷尽检查void
: 无返回值函数的返回类型null
/undefined
: 明确的空值类型
示例说明:
- 交互式演示
- 类型概念
// TypeScript特殊类型概念说明
// any - 绕过所有类型检查(不推荐)
let anything: any = "hello";
anything.foo.bar; // 编译通过,运行时可能报错
// unknown - 类型安全的any(推荐)
let uncertain: unknown = "hello";
if (typeof uncertain === 'string') {
console.log(uncertain.length); // 必须先类型检查
}
// never - 表示永远不会出现的类型
function exhaustiveCheck(value: 'a' | 'b'): string {
switch (value) {
case 'a': return 'A';
case 'b': return 'B';
default:
const _exhaustive: never = value; // 穷尽检查
return _exhaustive;
}
}
// void - 无返回值函数
function logAction(): void {
console.log("执行操作");
}
// null vs undefined的区别
const user = {
avatar: null, // 明确表示没有头像
nickname: undefined // 表示未设置
};
面试官视角:
这道题是TypeScript类型安全的核心考点:
- 要点清单: 理解每种类型的用途;知道
unknown
比any
更安全;理解never
的防御性编程作用;掌握严格模式下null检查 - 加分项: 举例说明
never
在联合类型中被过滤;解释unknown
的类型缩减机制;提到strictNullChecks
配置 - 常见失误: 滥用
any
类型;不理解never
和void
的区别;在严格模式下忽略null检查
延伸阅读:
- TypeScript Handbook - More on Functions — void和never类型详解
- Basarat - never类型 — never类型的高级用法
什么是联合类型?如何使用?
答案
核心概念:
联合类型使用 |
符号表示一个值可以是几种类型中的任意一种,是TypeScript实现类型灵活性的重要机制。
示例说明:
- 交互式演示
- 联合类型概念
// TypeScript联合类型核心概念
// 基础联合类型
type StringOrNumber = string | number;
type Status = 'loading' | 'success' | 'error';
// 类型缩减
function processValue(input: string | number): string {
if (typeof input === 'string') {
return input.toUpperCase(); // input被缩减为string
}
return input.toString(); // input被缩减为number
}
// 判别联合类型
interface Circle { kind: 'circle'; radius: number }
interface Rectangle { kind: 'rectangle'; width: number; height: number }
type Shape = Circle | Rectangle;
function getArea(shape: Shape): number {
switch (shape.kind) { // 使用判别属性进行类型缩减
case 'circle':
return Math.PI * shape.radius ** 2;
case 'rectangle':
return shape.width * shape.height;
default:
const exhaustive: never = shape; // 穷尽检查
return exhaustive;
}
}
// 复杂联合类型 - API结果
type ApiResult<T> =
| { success: true; data: T }
| { success: false; error: string; code: number };
function handleApiResult<T>(result: ApiResult<T>): T | null {
if (result.success) {
return result.data; // 访问success情况下的data
} else {
console.error(`Error ${result.code}: ${result.error}`);
return null;
}
}
面试官视角:
联合类型是TypeScript实用性的重要体现:
- 要点清单: 理解联合类型语法;掌握类型缩减机制;能实现判别联合类型;理解交叉类型
&
的区别 - 加分项: 提到分布式条件类型;使用never进行穷尽检查;结合泛型使用联合类型
- 常见失误: 不理解类型缩减;混淆联合类型和交叉类型;忽略穷尽性检查
延伸阅读:
- TypeScript Handbook - Unions and Intersection Types — 联合与交叉类型官方指南
- TypeScript Deep Dive - 联合类型 — 判别联合类型详解
什么是类型断言?有哪些方式?
答案
核心概念:
类型断言是告诉编译器"相信我,我知道这个值的确切类型"的机制。有两种语法:as
关键字和尖括号语法。
示例说明:
- 交互式演示
- 类型断言概念
// TypeScript类型断言核心概念
// 基础语法
let someValue: unknown = "this is a string";
let strLength = (someValue as string).length; // as语法(推荐)
// DOM元素类型断言
const canvas = document.getElementById('canvas') as HTMLCanvasElement;
const ctx = canvas.getContext('2d'); // 类型安全
// 常量断言
const colors = ['red', 'green', 'blue'] as const;
// 类型变为: readonly ['red', 'green', 'blue']
const config = { mode: 'development', port: 3000 } as const;
// 类型变为: { readonly mode: 'development'; readonly port: 3000; }
// 非空断言操作符
function processInput(input?: string): void {
console.log(input!.length); // 非空断言:确定input不为空(危险!)
// 更安全的方式
if (input !== undefined) {
console.log(input.length); // 类型守卫
}
}
// API响应类型断言
interface User {
id: number;
name: string;
email: string;
}
function isUser(obj: any): obj is User {
return typeof obj === 'object' &&
obj !== null &&
typeof obj.id === 'number' &&
typeof obj.name === 'string' &&
typeof obj.email === 'string';
}
// 安全的断言方式
async function fetchUser(id: number): Promise<User | null> {
const response = await fetch(`/api/users/${id}`);
const data = await response.json();
return isUser(data) ? data : null; // 类型守卫验证
}
// 类型断言 vs 类型转换
let value: any = "123";
let assertedNum = value as number; // 运行时仍是字符串
let convertedNum = Number(value); // 运行时变为数字
面试官视角:
类型断言体现了TypeScript的渐进式特性:
- 要点清单: 掌握as语法和尖括号语法;理解非空断言操作符;知道常量断言;区分断言和类型转换
- 加分项: 提到双重断言的使用场景;解释为什么as语法更好;理解断言的运行时成本为零
- 常见失误: 滥用类型断言绕过类型检查;混淆断言和转换;在严格模式下过度使用非空断言
延伸阅读:
- TypeScript Handbook - Type Assertions — 类型断言官方文档
- TypeScript Deep Dive - 类型断言 — 类型断言最佳实践
什么是类型守卫?如何实现?
答案
核心概念:
类型守卫是运行时检查,用于在特定的代码块中缩减变量的类型范围,确保类型安全。包括内置守卫和用户自定义守卫。
示例说明:
// typeof类型守卫
function processValue(value: string | number): string {
if (typeof value === 'string') {
return value.toUpperCase(); // value被缩减为string
}
return value.toString(); // value被缩减为number
}
// instanceof类型守卫
class Bird { fly() { console.log('flying'); } }
class Fish { swim() { console.log('swimming'); } }
function move(animal: Bird | Fish) {
if (animal instanceof Bird) {
animal.fly(); // animal被缩减为Bird类型
} else {
animal.swim(); // animal被缩减为Fish类型
}
}
// in操作符类型守卫
interface Rectangle { width: number; height: number; }
interface Circle { radius: number; }
function getArea(shape: Rectangle | Circle): number {
if ('width' in shape) {
return shape.width * shape.height; // shape被缩减为Rectangle
}
return Math.PI * shape.radius ** 2; // shape被缩减为Circle
}
// 用户自定义类型守卫
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function processUnknown(value: unknown) {
if (isString(value)) {
console.log(value.length); // value被缩减为string
}
}
// 复杂类型守卫
interface User { name: string; email: string; }
function isUser(obj: unknown): obj is User {
return typeof obj === 'object' &&
obj !== null &&
'name' in obj &&
'email' in obj &&
typeof (obj as User).name === 'string' &&
typeof (obj as User).email === 'string';
}
// 断言函数(assert函数)
function assertIsNumber(value: unknown): asserts value is number {
if (typeof value !== 'number') {
throw new Error('Expected number');
}
}
function processInput(input: unknown) {
assertIsNumber(input);
console.log(input.toFixed(2)); // input在此处被断言为number
}
面试官视角:
类型守卫是TypeScript实现动态类型安全的核心机制:
- 要点清单: 理解内置类型守卫(typeof、instanceof、in);能实现自定义类型守卫;理解类型缩减机制;掌握断言函数
- 加分项: 提到断言函数与类型守卫的区别;实现复杂对象的类型守卫;结合泛型使用类型守卫
- 常见失误: 类型守卫逻辑不严谨;不理解类型缩减的作用域;混淆类型断言和类型守卫
延伸阅读:
- TypeScript Handbook - Narrowing — 类型缩减官方指南
- TypeScript 4.0 - 断言函数 — 断言函数新特性
介绍TypeScript中的类型兼容性——逆变、协变、双向协变和不变
答案
核心概念:
类型兼容性描述了类型之间的赋值关系。在TypeScript中,不同的类型位置具有不同的型变特性:
- 协变: 子类型可以赋值给父类型
- 逆变: 父类型可以赋值给子类型
- 双向协变: 父子类型可以相互赋值
- 不变: 类型必须完全相同才能赋值
示例说明:
// 定义类型层次
interface Animal { name: string; }
interface Dog extends Animal { breed: string; }
// 1. 协变(Covariance)- 数组、返回值
const animals: Animal[] = [];
const dogs: Dog[] = [{ name: 'Buddy', breed: 'Golden' }];
// 数组是协变的
const animalArray: Animal[] = dogs; // ✅ Dog[] 可以赋值给 Animal[]
// 函数返回值是协变的
type AnimalFactory = () => Animal;
type DogFactory = () => Dog;
const dogFactory: DogFactory = () => ({ name: 'Max', breed: 'Husky' });
const animalFactory: AnimalFactory = dogFactory; // ✅ 返回Dog的函数可以赋值给返回Animal的函数
// 2. 逆变(Contravariance)- 严格模式下的函数参数
type AnimalHandler = (animal: Animal) => void;
type DogHandler = (dog: Dog) => void;
const handleAnimal: AnimalHandler = (animal) => console.log(animal.name);
const handleDog: DogHandler = handleAnimal; // ✅ 处理Animal的函数可以处理Dog
// 3. 双向协变(Bivariance)- 默认模式下的函数参数
// 在非严格函数类型检查下,函数参数是双向协变的
function processWithAnimalHandler(handler: AnimalHandler) {}
function processWithDogHandler(handler: DogHandler) {}
// 在默认情况下这些都是允许的(不推荐)
const flexibleHandler1: AnimalHandler = (dog: Dog) => console.log(dog.breed);
const flexibleHandler2: DogHandler = (animal: Animal) => console.log(animal.name);
// 4. 不变(Invariance)- 泛型容器
interface Container<T> {
value: T;
setValue: (value: T) => void;
}
const animalContainer: Container<Animal> = {
value: { name: 'Generic Animal' },
setValue: (animal) => console.log(animal.name)
};
const dogContainer: Container<Dog> = {
value: { name: 'Specific Dog', breed: 'Labrador' },
setValue: (dog) => console.log(`${dog.name} is a ${dog.breed}`)
};
// 泛型容器是不变的
// const container1: Container<Animal> = dogContainer; // ❌ 错误
// const container2: Container<Dog> = animalContainer; // ❌ 错误
// 启用严格函数类型检查的效果
// tsconfig.json: "strictFunctionTypes": true
type StrictAnimalHandler = (animal: Animal) => void;
type StrictDogHandler = (dog: Dog) => void;
const strictAnimalHandler: StrictAnimalHandler = (animal) => console.log(animal.name);
// const strictDogHandler: StrictDogHandler = strictAnimalHandler; // ✅ 逆变
// const invalidHandler: StrictAnimalHandler = (dog: Dog) => {}; // ❌ 严格模式下错误
面试官视角:
类型兼容性是TypeScript高级特性,考察对类型系统的深度理解:
- 要点清单: 理解四种型变关系;知道数组和返回值的协变特性;理解函数参数在严格模式下的逆变;掌握泛型的不变性
- 加分项: 解释strictFunctionTypes配置的作用;提到方法和函数的型变差异;理解型变在实际开发中的意义
- 常见失误: 混淆协变和逆变的概念;不理解双向协变的问题;忽略严格模式对型变的影响
延伸阅读:
- TypeScript FAQ - 型变 — 官方FAQ中的型变解释
- Covariance and Contravariance — 协变和逆变详解
is关键字的作用是什么?
答案
核心概念:
is
关键字用于创建用户定义的类型守卫函数,它是一种类型谓词,帮助TypeScript在运行时进行类型判断和类型缩减。
示例说明:
// 基础类型守卫
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function processValue(input: unknown) {
if (isString(input)) {
// input在这里被缩减为string类型
console.log(input.toUpperCase());
console.log(input.length);
}
}
// 复杂对象类型守卫
interface User {
id: number;
name: string;
email: string;
}
function isUser(obj: unknown): obj is User {
return typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
'name' in obj &&
'email' in obj &&
typeof (obj as User).id === 'number' &&
typeof (obj as User).name === 'string' &&
typeof (obj as User).email === 'string';
}
// API响应处理
async function fetchUser(id: number): Promise<User | null> {
const response = await fetch(`/api/users/${id}`);
const data = await response.json();
if (isUser(data)) {
return data; // data被确认为User类型
}
return null;
}
// 联合类型过滤
interface Circle { kind: 'circle'; radius: number; }
interface Rectangle { kind: 'rectangle'; width: number; height: number; }
type Shape = Circle | Rectangle;
function isCircle(shape: Shape): shape is Circle {
return shape.kind === 'circle';
}
function processShapes(shapes: Shape[]) {
const circles = shapes.filter(isCircle); // circles的类型是Circle[]
circles.forEach(circle => {
console.log(`Circle area: ${Math.PI * circle.radius ** 2}`);
});
}
// 泛型类型守卫
function isArrayOf<T>(
value: unknown,
guard: (item: unknown) => item is T
): value is T[] {
return Array.isArray(value) && value.every(guard);
}
const data: unknown = ['hello', 'world', 123];
if (isArrayOf(data, isString)) {
// data被缩减为string[]类型
data.forEach(str => console.log(str.toUpperCase()));
}
// 与断言函数的区别
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
throw new Error('Expected string');
}
}
// 使用对比
function example(input: unknown) {
// 类型守卫 - 返回boolean
if (isString(input)) {
console.log(input.length); // input是string
}
// 断言函数 - 抛出异常或继续执行
assertIsString(input);
console.log(input.length); // input被断言为string
}
面试官视角:
is关键字是TypeScript实现类型安全的重要工具:
- 要点清单: 理解类型谓词的语法;能编写复杂对象的类型守卫;掌握与断言函数的区别;理解类型缩减机制
- 加分项: 实现泛型类型守卫;结合数组方法使用类型守卫;提到运行时类型验证的重要性
- 常见失误: 类型守卫函数逻辑不严谨;混淆类型守卫和断言函数;忽略null和undefined的检查
延伸阅读:
- TypeScript Handbook - 类型谓词 — 官方类型谓词文档
- Advanced TypeScript - 用户定义类型守卫 — 高级类型守卫用法
typeof、instanceof、in操作符在TypeScript中的作用
答案
核心概念:
这三个操作符都用于运行时类型检查和编译时类型缩减,但适用场景不同:
- typeof: 检查原始类型和函数
- instanceof: 检查类实例和原型链
- in: 检查对象属性存在
示例说明:
// 1. typeof操作符
function processValue(input: string | number | boolean | Function) {
if (typeof input === 'string') {
console.log(input.toUpperCase()); // input缩减为string
} else if (typeof input === 'number') {
console.log(input.toFixed(2)); // input缩减为number
} else if (typeof input === 'boolean') {
console.log(input ? 'true' : 'false'); // input缩减为boolean
} else if (typeof input === 'function') {
input(); // input缩减为Function
}
}
// typeof用于类型查询
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3
};
type Config = typeof config; // 获取config的类型
// Config = { apiUrl: string; timeout: number; retries: number; }
// 2. instanceof操作符
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() {
console.log('Woof!');
}
}
class Cat extends Animal {
meow() {
console.log('Meow!');
}
}
function handlePet(pet: Animal) {
if (pet instanceof Dog) {
pet.bark(); // pet缩减为Dog类型,可以访问bark方法
console.log(`${pet.name} is a ${pet.breed}`);
} else if (pet instanceof Cat) {
pet.meow(); // pet缩减为Cat类型,可以访问meow方法
}
// 对于内置类型
const date = new Date();
if (date instanceof Date) {
console.log(date.getFullYear()); // date是Date类型
}
const arr = [1, 2, 3];
if (arr instanceof Array) {
console.log(arr.length); // arr是Array类型
}
}
// 3. in操作符
interface Rectangle {
width: number;
height: number;
}
interface Circle {
radius: number;
}
type Shape = Rectangle | Circle;
function getArea(shape: Shape): number {
if ('width' in shape) {
// shape缩减为Rectangle类型
return shape.width * shape.height;
} else {
// shape缩减为Circle类型
return Math.PI * shape.radius ** 2;
}
}
// in操作符用于可选属性检查
interface User {
name: string;
email: string;
phone?: string;
}
function displayUser(user: User) {
console.log(`Name: ${user.name}`);
console.log(`Email: ${user.email}`);
if ('phone' in user && user.phone) {
console.log(`Phone: ${user.phone}`);
}
}
// 高级用法:组合使用
function processData(data: unknown) {
// 先检查是否为对象
if (typeof data === 'object' && data !== null) {
// 再检查特定属性
if ('id' in data && 'name' in data) {
console.log('Found user-like object');
}
// 检查是否为特定类的实例
if (data instanceof Error) {
console.log(`Error: ${data.message}`);
}
}
}
// 类型守卫函数中的组合使用
function isUserWithPhone(obj: unknown): obj is User & { phone: string } {
return typeof obj === 'object' &&
obj !== null &&
'name' in obj &&
'email' in obj &&
'phone' in obj &&
typeof (obj as any).name === 'string' &&
typeof (obj as any).email === 'string' &&
typeof (obj as any).phone === 'string';
}
面试官视角:
这三个操作符是TypeScript类型安全的基础:
- 要点清单: 掌握三个操作符的使用场景;理解类型缩减机制;能组合使用进行复杂判断;了解typeof的类型查询功能
- 加分项: 提到typeof null === 'object'的JavaScript陷阱;理解instanceof的原型链检查;掌握可选属性的in检查技巧
- 常见失误: 混淆instanceof和typeof的使用场景;忽略null检查;不理解类型缩减的作用域
延伸阅读:
- MDN - typeof操作符 — typeof详细用法
- MDN - instanceof操作符 — instanceof原型链检查原理
如何使用unknown类型替代any?
答案
核心概念:
unknown
是类型安全的any
,它表示任何值,但在使用前必须进行类型检查或类型断言。这强制开发者编写更安全的代码。
示例说明:
// ❌ 不安全的any用法
function processAny(data: any): string {
return data.toString().toUpperCase(); // 可能运行时报错
}
// ✅ 安全的unknown用法
function processUnknown(data: unknown): string {
// 必须先进行类型检查
if (typeof data === 'string') {
return data.toUpperCase();
} else if (typeof data === 'number') {
return data.toString().toUpperCase();
} else if (data && typeof data === 'object' && 'toString' in data) {
return String(data).toUpperCase();
}
return 'UNKNOWN';
}
// API响应处理
interface User {
id: number;
name: string;
email: string;
}
async function fetchUser(id: number): Promise<User | null> {
const response = await fetch(`/api/users/${id}`);
const data: unknown = await response.json(); // 不确定API返回的结构
// 使用类型守卫验证数据
if (isUser(data)) {
return data;
}
return null;
}
function isUser(obj: unknown): obj is User {
return typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
'name' in obj &&
'email' in obj &&
typeof (obj as User).id === 'number' &&
typeof (obj as User).name === 'string' &&
typeof (obj as User).email === 'string';
}
// 错误处理
function handleError(error: unknown): string {
// 检查是否为Error实例
if (error instanceof Error) {
return error.message;
}
// 检查是否为字符串
if (typeof error === 'string') {
return error;
}
// 检查是否有toString方法
if (error && typeof error === 'object' && 'toString' in error) {
return String(error);
}
return 'Unknown error occurred';
}
// JSON解析安全处理
function safeJsonParse(jsonString: string): unknown {
try {
return JSON.parse(jsonString);
} catch {
return null;
}
}
function processJsonData(jsonString: string) {
const data = safeJsonParse(jsonString);
if (data === null) {
console.log('Invalid JSON');
return;
}
// 进一步类型验证
if (typeof data === 'object' && data !== null && 'users' in data) {
const users = (data as any).users;
if (Array.isArray(users)) {
users.forEach(user => {
if (isUser(user)) {
console.log(`User: ${user.name}`);
}
});
}
}
}
// 泛型中使用unknown
function createValidator<T>(validator: (value: unknown) => value is T) {
return (input: unknown): T | null => {
if (validator(input)) {
return input;
}
return null;
};
}
const validateUser = createValidator(isUser);
const validateString = createValidator((value): value is string => typeof value === 'string');
// 事件处理器
function handleEvent(event: unknown) {
// DOM事件检查
if (typeof event === 'object' && event !== null && 'target' in event) {
const domEvent = event as Event;
console.log('DOM event:', domEvent.type);
}
// 自定义事件检查
if (typeof event === 'object' &&
event !== null &&
'type' in event &&
'data' in event) {
const customEvent = event as { type: string; data: unknown };
console.log('Custom event:', customEvent.type);
}
}
// 迁移策略:从any到unknown
// 第一步:将any改为unknown
function processLegacyData(input: unknown /* 原来是any */) {
// 第二步:添加类型检查
if (typeof input === 'object' && input !== null) {
// 第三步:渐进式类型验证
if ('id' in input && typeof (input as any).id === 'number') {
console.log(`Processing item ${(input as any).id}`);
}
}
}
面试官视角:
unknown类型体现了TypeScript渐进式类型安全的理念:
- 要点清单: 理解unknown与any的区别;掌握类型缩减技巧;能实现安全的API响应处理;了解迁移策略
- 加分项: 提到unknown在泛型中的应用;实现复杂的类型验证逻辑;理解unknown的性能优势
- 常见失误: 直接使用类型断言绕过检查;类型守卫逻辑不完整;忽略边界情况处理
延伸阅读:
- TypeScript 3.0 - unknown类型 — unknown类型的官方介绍
- TypeScript Deep Dive - unknown — unknown类型最佳实践
TypeScript中的类型推断机制是如何工作的?
答案
核心概念:
TypeScript的类型推断通过上下文分析自动推导变量和表达式的类型,减少显式类型注解的需要。主要包括变量推断、返回值推断、上下文推断等。
示例说明:
// 1. 变量类型推断
let name = "TypeScript"; // 推断为string
let version = 4.5; // 推断为number
let isStrict = true; // 推断为boolean
// 数组类型推断
let numbers = [1, 2, 3]; // 推断为number[]
let mixed = [1, "two", true]; // 推断为(string | number | boolean)[]
let empty = []; // 推断为any[]
// 2. 函数返回值推断
function add(a: number, b: number) {
return a + b; // 推断返回类型为number
}
function getUser() {
return {
id: 1,
name: "Alice",
email: "alice@example.com"
}; // 推断返回类型为 { id: number; name: string; email: string; }
}
// 3. 上下文类型推断
interface User {
id: number;
name: string;
onClick: (event: MouseEvent) => void;
}
const user: User = {
id: 1,
name: "Bob",
onClick: (e) => { // e被推断为MouseEvent类型
console.log(e.clientX);
}
};
// 数组方法的上下文推断
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2); // n被推断为number,返回number[]
// 4. 最佳公共类型推断
let values = [1, "hello", true]; // 推断为(string | number | boolean)[]
class Animal { name: string = ""; }
class Dog extends Animal { breed: string = ""; }
class Cat extends Animal { color: string = ""; }
let pets = [new Dog(), new Cat()]; // 推断为(Dog | Cat)[]
let animals: Animal[] = [new Dog(), new Cat()]; // 显式指定为Animal[]
// 5. 泛型类型推断
function identity<T>(arg: T): T {
return arg;
}
let result1 = identity(42); // T推断为number
let result2 = identity("hello"); // T推断为string
// 多个泛型参数推断
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
let stringNumberPair = pair("age", 25); // 推断为[string, number]
// 6. 条件类型中的推断
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function getString(): string { return "hello"; }
type StringReturnType = ReturnType<typeof getString>; // 推断为string
// 7. 字面量类型推断
let status = "loading"; // 推断为string
const theme = "dark"; // 推断为"dark"字面量类型
// 使用as const进行字面量推断
let config = {
mode: "development",
port: 3000
} as const; // 推断为 { readonly mode: "development"; readonly port: 3000; }
// 8. 函数参数推断
function processCallback<T>(items: T[], callback: (item: T) => void) {
items.forEach(callback);
}
processCallback([1, 2, 3], (num) => {
console.log(num.toFixed(2)); // num被推断为number
});
// 9. Promise类型推断
async function fetchData() {
const response = await fetch('/api/data');
const data = await response.json(); // 推断为any,需要类型断言
return data;
}
// 更好的Promise推断
async function fetchUser(): Promise<User> {
const response = await fetch('/api/user');
return response.json(); // 返回类型被约束为User
}
// 10. 索引访问类型推断
interface Config {
database: {
host: string;
port: number;
};
cache: {
ttl: number;
};
}
function getConfig<K extends keyof Config>(key: K): Config[K] {
// ... 实现
return {} as Config[K];
}
let dbConfig = getConfig("database"); // 推断为 { host: string; port: number; }
// 11. 映射类型推断
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface Original {
name: string;
age: number;
}
let partial: Partial<Original> = {}; // 推断为 { name?: string; age?: number; }
// 12. 类型推断的限制和提示
// 有时需要显式类型注解来帮助推断
function combine<T>(items: T[]): T[] {
return items.concat(items);
}
// 这种情况推断可能不准确,需要显式指定
let result = combine([]); // T推断为never,通常不是我们想要的
let betterResult = combine<string>([]); // 显式指定T为string
面试官视角:
类型推断是TypeScript开发体验的核心:
- 要点清单: 理解变量、函数、泛型的推断机制;掌握上下文类型推断;了解最佳公共类型算法;知道as const的作用
- 加分项: 提到类型推断的性能考量;理解推断算法的限制;掌握复杂泛型场景的推断技巧
- 常见失误: 过度依赖推断导致类型不明确;忽略上下文推断的作用;不理解字面量类型推断
延伸阅读:
- TypeScript Handbook - 类型推断 — 官方类型推断指南
- TypeScript Deep Dive - 类型推断 — 类型推断深入解析