TypeScript 面试真题补充
本篇整理自 8.Typescript面试真题-62页.pdf,补充函数签名、类修饰符、枚举运行时、装饰器和 React/Vue 集成等代码型追问。
TypeScript 面试真题补充
本篇整理自
8.Typescript面试真题-62页.pdf,补充函数签名、类修饰符、枚举运行时、装饰器和 React/Vue 集成等代码型追问。
一、函数签名:可选、剩余与重载
const add = (a: number, b?: number) => a + (b ?? 0);
const sum = (first: number, ...rest: number[]) =>
rest.reduce((total, value) => total + value, first);
可选参数的类型通常包含 undefined,实现时要先处理缺省值。剩余参数应标注为数组类型。
函数重载用多个公开签名描述调用者可用的组合,再用一个兼容实现承接运行时分支:
function format(value: string): string;
function format(value: number): number;
function format(value: string | number): string | number {
return typeof value === 'string' ? value.trim() : value;
}
实现签名对外不可见;重载签名必须彼此不矛盾,调用者只能匹配前面的公开签名。(深入阅读:函数类型)
二、泛型约束与 keyof
泛型的价值是保留输入和输出之间的类型关系,而不是把参数变成 any:
function getValue<T extends object, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: 'Ada', age: 20 };
const name = getValue(user, 'name'); // string
keyof User 产生属性名联合;K extends keyof T 保证传入的 key 确实存在。复杂类型题可按三步回答:先确定约束,再推断类型参数,最后说明运行时仍需做校验。(深入阅读:索引类型约束)
三、交叉、映射与条件类型
A & B要求值同时满足两种结构,常用于组合对象能力。A | B表示二选一,使用前通常需要类型守卫。- 映射类型遍历
keyof,例如Readonly<T>给每个属性加readonly。 - 条件类型
T extends U ? X : Y根据类型关系选择结果,配合泛型可构造工具类型。
type ReadonlyCopy<T> = {
readonly [P in keyof T]: T[P];
};
type ElementType<T> = T extends (infer U)[] ? U : T;
这些运算只存在于编译期;不能把它们误解为运行时数据转换。(深入阅读:映射类型;条件类型)
四、类、继承与修饰符
PDF 通过 extends、super、protected、readonly、static 和 abstract 对比 ES6 class:
| 修饰符 | 访问/实例化语义 |
|---|---|
public |
默认成员,任何持有实例的代码可访问 |
private |
仅当前类源码可访问,主要是编译期约束 |
protected |
当前类和子类可访问 |
readonly |
初始化后不能再次赋值(引用对象内部仍可能可变) |
static |
挂在类本身,使用 Class.member 访问 |
abstract |
只能声明抽象类/成员,子类必须实现,抽象类不能直接 new |
abstract class Animal {
protected constructor(public readonly name: string) {}
abstract move(): void;
}
class Dog extends Animal {
move() { console.log(this.name + ' moves'); }
}
面试时要区分 TS 的 private 与 JavaScript 的运行时私有字段 #field:前者擦除后不提供运行时隔离。(深入阅读:类实现接口)
五、枚举的运行时行为
数字枚举成员默认从 0 自增,也可以从指定值开始;数字枚举编译后通常生成双向映射,Direction[0] 可以得到成员名。字符串枚举必须为每个成员指定字符串值,没有同样的反向数字映射。
enum Direction {
Up = 10,
Down,
Left,
Right
}
// Direction.Down === 11
enum Method {
Get = 'GET',
Post = 'POST'
}
枚举会产出运行时代码;只需要编译期联合时可考虑字面量联合类型,避免额外输出。 const enum 是否可用还取决于构建链配置,不能在所有库发布场景中默认使用。(深入阅读:编译结果;反向映射)
六、装饰器:类型检查之外的运行时机制
PDF 展示了类、属性、方法、访问器、参数装饰器和装饰器工厂。传统装饰器需要在 tsconfig.json 开启 experimentalDecorators;它们编译后通过 Object.defineProperty 等机制改写构造函数或属性描述符。
function log(target: object, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: unknown[]) {
console.log(key, args);
return original.apply(this, args);
};
}
class Service {
@log
getUser(id: number) {
return { id };
}
}
装饰器工厂先求值,再按规则调用;同一成员上的多个装饰器通常表现为“工厂自上而下求值、调用自下而上”。回答时应说明装饰器方案受 TypeScript 版本和编译器装饰器提案影响,不能把示例直接当作所有运行时的标准行为。(深入阅读:启用装饰器;装饰器执行顺序)
七、模块、命名空间与声明文件
- 任何包含顶层
import/export的文件都是模块,作用域默认隔离。 export default适合单一默认值,命名导出适合多个 API;导入时语法必须匹配。namespace会生成一个运行时对象,适合维护旧的全局脚本或声明合并;现代项目优先使用 ES Module。- JavaScript 第三方包没有类型时,可安装对应的
@types/*或编写.d.ts声明文件。声明只描述类型,不会生成实现代码。
// export.ts
export const version = '1.0';
export interface User { name: string }
// consumer.ts
import { version, User } from './export';
路径别名、module/target 和模块解析策略要和打包器保持一致,否则“类型检查通过但运行时找不到模块”是常见故障。(深入阅读:声明文件(.d.ts);命名空间与模块的主要区别)
八、React 与 Vue 集成中的类型边界
1. React
函数组件应为 props 建立接口,children 使用 React.ReactNode;类组件通常通过 React.Component<Props, State> 传入两个类型参数,事件对象要指定元素类型:
interface ButtonProps {
label?: string;
children?: React.ReactNode;
}
function Button({ label, children }: ButtonProps) {
return <button>{label ?? children}</button>;
}
function onInput(event: React.ChangeEvent<HTMLInputElement>) {
console.log(event.target.value);
}
不要用 any 接管整个 props;可选字段读取时要处理 undefined。默认值应与接口约束一致。(深入阅读:React 项目配置)
2. Vue class 装饰器(历史代码)
PDF 还展示了 vue-property-decorator 的 @Component、@Prop、@Watch、@Emit。这类写法可以把 props、watch 和事件映射到 class 成员,但依赖 Vue 版本与装饰器构建配置;新项目应先确认框架推荐的 Composition API 方案,再决定是否沿用。(深入阅读:TypeScript 支持)
九、类型题常见陷阱
as User只是类型断言,不会在运行时补齐字段或验证对象。any会关闭检查并污染后续推断;不确定输入优先用unknown,先做类型守卫。never表示不可正常返回的分支(抛错或死循环),不是“任意类型”。null/undefined是否可赋给其他类型受strictNullChecks影响。- 装饰器、枚举和命名空间会产生运行时代码,不能只按“类型擦除”来估算产物。
(深入阅读:unknown 类型;类型断言的安全使用;严格类型检查)