TypeScript 高级技能认证测试
考察知识点
- 高阶类型体系: 类型兼容性、类型守卫进阶、条件类型深入、类型系统原理、高级类型模式
- 高阶工具类型: 条件类型(infer/分布式)、模板字面类型、映射类型进阶、元组类型操作、自定义工具类型
- 深度类型编程: 类型级计算、递归类型、类型约束高级技巧、类型系统设计、类型编程模式
- 高阶文件声明: 复杂声明语法、模块增强、第三方库声明、声明文件优化、声明发布
在 TypeScript 的结构化类型系统中,函数 A = (x: Animal) => void 可以赋值给函数 B = (x: Dog) => void。
以下关于递归类型别名的使用限制和技巧,哪些说法是正确的?
TypeScript 编译器在词法分析阶段会生成哪些产物?
考虑以下代码:
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;
}
}
关于上述代码,以下哪些说法是正确的?
关于 TypeScript 条件类型 T extends U ? X : Y,以下哪些说法是正确的?
关于 keyof 操作符在 TypeScript 中的行为,以下哪些说法是正确的?
在 TypeScript 中,以下哪个(些)工具类型定义能够正确实现一个 DeepRequired<T>,使其递归地将对象类型 T 及其所有嵌套属性的可选修饰符移除(即所有属性变为 required)?
关于 TypeScript 编译器(tsc)的编译流程,以下说法正确的有哪些?
在自定义复杂工具类型时,关于联合类型的分发行为,以下哪些说法是正确的?
关于 TypeScript 中泛型的协变(covariance)与逆变(contravariance),以下哪些说法是正确的?
关于 TypeScript 中递归类型的特性,以下哪些说法是正确的?
在 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 赋值 ____________。(填写"会报错"或"不会报错")
以下类型用于将嵌套数组展平为一维数组,请填写 ____________ 处缺失的代码:
type Flatten<T> = T extends (infer U)[]
? !!1_答案!!
: T;
// 测试:
// type Result = Flatten<number[][][]> // 应为 number
// type Result2 = Flatten<string> // 应为 string
// 实现 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 }[];
// }
请补全以下工具类型 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 }。
请写出 ____________ 处应填入的类型工具名称。
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 } 类型。
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,结果会有什么不同?
以下代码存在类型错误,请分析错误原因并提供修复方案:
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') // 应报类型错误
请指出代码中的错误,并给出正确的实现。
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]; };
请解释为什么函数参数位置是逆变的,而返回值位置是协变的。请通过具体的代码示例说明,如果不遵守这些规则会导致什么样的类型安全问题。