TypeScript 高级技能认证测试
考察知识点
- 高阶类型体系: 类型兼容性、类型守卫进阶、条件类型深入、类型系统原理、高级类型模式
- 高阶工具类型: 条件类型(infer/分布式)、模板字面类型、映射类型进阶、元组类型操作、自定义工具类型
- 深度类型编程: 类型级计算、递归类型、类型约束高级技巧、类型系统设计、类型编程模式
- 高阶文件声明: 复杂声明语法、模块增强、第三方库声明、声明文件优化、声明发布
关于 keyof 操作符在 TypeScript 中的行为,以下哪些说法是正确的?
A. keyof string[] 的结果包含 number 类型,因为数组索引是数字
B. keyof readonly number[] 的结果与 keyof number[] 完全相同
C. 当一个类包含 private 字段时,keyof 该类的结果仍然只包含 public 成员的键
D. keyof typeof someObject 可以获取对象值的类型,而不仅仅是键的联合类型
以下代码存在类型错误,请分析错误原因并提供修复方案:
const routes = {
getUsers: '/api/users',
getUserById: '/api/users/:id',
createUser: '/api/users'
};
// 目标:创建一个类型安全的 getRoute 函数
// 只接受 routes 对象中存在的键,并返回对应的路径字符串
function getRoute(routeName: keyof routes): string {
return routes[routeName];
}
// 期望的调用:
// getRoute('getUsers') // 正确
// getRoute('getProducts') // 应报类型错误
请指出代码中的错误,并给出正确的实现。
interface EventMap {
click: { x: number; y: number };
keydown: { key: string; code: number };
resize: { width: number; height: number };
}
function handleEvent<K extends keyof EventMap>(
eventName: K,
handler: (event: !!1_答案!!) => void
): void {
// ...
}
请填写正确的类型表达式,使得 handler 的参数类型能够根据 eventName 的类型自动推导为对应的事件数据类型。例如,当 eventName 为 "click" 时,event 参数应为 { x: number; y: number } 类型。
考虑以下代码:
type Shape = Circle | Rectangle | Triangle;
interface Circle { kind: 'circle'; radius: number }
interface Rectangle { kind: 'rectangle'; width: number; height: number }
interface Triangle { kind: 'triangle'; base: number; height: number }
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle': return Math.PI * shape.radius ** 2;
case 'rectangle': return shape.width * shape.height;
case 'triangle': return 0.5 * shape.base * shape.height;
default:
const _exhaustive: never = shape;
return _exhaustive;
}
}
关于上述代码,以下哪些说法是正确的?
A. 如果向 Shape 联合类型中添加 Square 类型但不添加对应的 case 分支,TypeScript 会在 default 行报类型错误
B. _exhaustive: never = shape 利用了 TypeScript 的穷举检查模式,确保所有联合类型分支都被处理
C. 将 default 分支改为 throw new Error() 后,_exhaustive 行可以省略,因为 TypeScript 知道该分支不会返回
D. 如果删除 default 分支,函数仍然能正确处理所有 Shape 类型,因为 TypeScript 的控制流分析会自动推导
关于 TypeScript 条件类型 T extends U ? X : Y,以下哪些说法是正确的?
A. 条件类型中的 extends 与泛型约束中的 extends 含义相同,都表示子类型关系判断
B. T extends U ? X : Y 中的 X 和 Y 可以是类型推导表达式,包含对 T 和 U 的引用
C. 当 T 是联合类型时,条件类型会自动对联合类型的每个成员分别应用条件判断,这称为分布式条件类型
D. 嵌套条件类型如 A extends B ? C extends D ? E : F : G 的解析顺序是从左到右,等价于 (A extends B ? (C extends D ? E : F) : G)
TypeScript 内置的 Exclude<T, U> 和 Extract<T, U> 工具类型的定义如下:
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
请分析:
- 当
T = 'a' | 'b' | 'c',U = 'a' | 'b'时,Exclude<T, U>和Extract<T, U>分别得到什么结果?推导过程是什么? - 为什么这两个类型定义中使用了
never,但最终结果中不会出现never? - 如果将定义改为非分布式写法
[T] extends [U] ? never : T,结果会有什么不同?
在 TypeScript 中,以下哪个(些)工具类型定义能够正确实现一个 DeepRequired<T>,使其递归地将对象类型 T 及其所有嵌套属性的可选修饰符移除(即所有属性变为 required)?
请补全以下工具类型 RenameField<T, OldKey, NewKey>,使其能够将对象类型 T 中的指定属性 OldKey 重命名为 NewKey,同时保持其他属性和类型不变。
type RenameField<T, OldKey extends keyof T, NewKey extends string> =
Omit<T, OldKey> & !!1_Record<NewKey, T[OldKey]>!!
当使用 RenameField<{ name: string; age: number }, "name", "userName"> 时,结果类型应等价于 { age: number; userName: string }。
请写出 !!1!! 处应填入的类型工具名称。
在自定义复杂工具类型时,关于联合类型的分发行为,以下哪些说法是正确的?
// 实现 DeepPartial<T>:递归地将对象 T 的所有属性变为可选
// 包括嵌套对象和数组中的对象也需要处理
type DeepPartial<T> = T extends object
? { [K in keyof T]?: !!1_答案!! }
: T;
// 测试
interface Nested {
a: {
b: { c: string };
};
items: { name: string }[];
}
type Result = DeepPartial<Nested>;
// 期望: {
// a?: { b?: { c?: string } };
// items?: { name?: string }[];
// }
关于 TypeScript 中递归类型的特性,以下哪些说法是正确的?
以下类型用于将嵌套数组展平为一维数组,请填写 ______ 处缺失的代码:
type Flatten<T> = T extends (infer U)[]
? !!1_答案!!
: T;
// 测试:
// type Result = Flatten<number[][][]> // 应为 number
// type Result2 = Flatten<string> // 应为 string
以下关于递归类型别名的使用限制和技巧,哪些说法是正确的?
关于 TypeScript 中泛型的协变(covariance)与逆变(contravariance),以下哪些说法是正确的?
关于 TypeScript 编译器(tsc)的编译流程,以下说法正确的有哪些?
在 TypeScript 的结构化类型系统中,判断以下函数类型的赋值兼容性:
type Handler = (x: string | number) => void;
type SpecificHandler = (x: string) => void;
const h1: Handler = function(x: string) { console.log(x); };
const h2: SpecificHandler = function(x: string | number) { console.log(x); };
上述代码中,赋值语句 const h1: Handler = ... 是否会报类型错误?赋值语句 const h2: SpecificHandler = ... 是否会报类型错误?
请分别回答:h1 赋值 ________,h2 赋值 ______。(填写"会报错"或"不会报错")
TypeScript 编译器在词法分析阶段会生成哪些产物?
在 TypeScript 的结构化类型系统中,函数 A = (x: Animal) => void 能否赋值给函数 B = (x: Dog) => void?
答案:________________________
TypeScript 内置的 Pick<T, K> 和 Omit<T, K> 工具类型定义如下:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
请分析:
Pick类型中的K extends keyof T约束的作用是什么?如果去掉这个约束会有什么问题?Omit如何通过组合Pick和Exclude实现"排除指定键"的功能?- 如果直接使用映射类型写
Omit,如下所示,与原定义有什么区别?TypeScripttype OmitDirect<T, K extends keyof T> = { [P in keyof T as P extends K ? never : P]: T[P]; };
请解释为什么函数参数位置是逆变的,而返回值位置是协变的。请通过具体的代码示例说明,如果不遵守这些规则会导致什么样的类型安全问题。
📝 发现内容有误?点击此处直接编辑
长按或扫描二维码,立即体验