面试题答案
一键面试类型系统施加的约束
- 赋值兼容性:当基于复杂类型进行代码生成时,赋值操作必须满足类型兼容性规则。例如,在联合类型
A | B
中,赋值给该联合类型变量的值必须是A
类型或B
类型。type Fruit = 'apple' | 'banana'; let myFruit: Fruit; myFruit = 'apple'; // 正确 myFruit = 'cherry'; // 错误,'cherry' 不在联合类型中
- 属性访问:对于交叉类型
A & B
,访问属性时必须确保该属性同时存在于A
和B
类型中。type User = { name: string }; type Admin = { role: string }; type UserAdmin = User & Admin; let userAdmin: UserAdmin; userAdmin.name; // 正确 userAdmin.role; // 正确 userAdmin.age; // 错误,User 和 Admin 类型都没有 'age' 属性
- 函数参数和返回值:如果基于复杂类型定义函数参数或返回值,调用函数时传入的参数必须符合参数类型,返回值必须符合返回类型。
type NumOrStr = number | string; function printValue(value: NumOrStr): void { if (typeof value === 'number') { console.log(value.toFixed(2)); } else { console.log(value.toUpperCase()); } } printValue(123); // 正确 printValue('abc'); // 正确 printValue(true); // 错误,boolean 类型不符合 NumOrStr 类型
突破部分约束以实现特定代码生成需求
- 类型断言:通过类型断言,可以告诉编译器某个值的类型,从而突破部分类型约束。但需谨慎使用,因为如果断言错误,可能导致运行时错误。
type Shape = { kind: 'circle' |'square' }; type Circle = Shape & { kind: 'circle'; radius: number }; type Square = Shape & { kind:'square'; sideLength: number }; let shape: Shape = { kind: 'circle' }; let circle = shape as Circle; // 假设已知 shape 实际是 Circle 类型 console.log(circle.radius); // 这里如果 shape 实际不是 Circle 类型会出错
- 类型守卫:使用类型守卫可以在运行时检查值的类型,从而在遵循类型安全的前提下,针对不同类型执行特定代码生成逻辑。
type Animal = { type: 'dog' | 'cat' }; type Dog = Animal & { type: 'dog'; bark: () => void }; type Cat = Animal & { type: 'cat'; meow: () => void }; function handleAnimal(animal: Animal) { if (animal.type === 'dog') { const dog = animal as Dog; dog.bark(); } else { const cat = animal as Cat; cat.meow(); } }
- 索引类型查询和映射类型:在处理对象类型时,索引类型查询和映射类型可以动态生成新类型,从而满足特定代码生成需求。
type UserInfo = { name: string; age: number }; type UserInfoKeys = keyof UserInfo; // 'name' | 'age' type ReadonlyUserInfo = { readonly [K in UserInfoKeys]: UserInfo[K] }; let readonlyUser: ReadonlyUserInfo = { name: 'John', age: 30 }; // readonlyUser.name = 'Jane'; // 错误,ReadonlyUserInfo 类型的属性是只读的