高阶类型体系专题测试
考察知识点
- 类型兼容性: 结构子类型、方差、 bivariance
- 类型守卫进阶: 自定义类型守卫、类型谓词、标签联合
- 条件类型深入: infer 高级用法、类型推断、递归类型
- 类型系统原理: 类型检查算法、编译时类型、运行时类型
- 高级类型模式: 类型级编程、Church 编码、类型计算
关于 keyof 操作符在 TypeScript 中的行为,以下哪些说法是正确的?
考虑以下代码:
function greet(name: string): void {}
class User { constructor(public name: string) {} }
const obj = { a: 1, b: 2, c: 3 };
enum Color { Red, Green, Blue }
关于 typeof 操作符的使用,以下哪些说法是正确的?
考虑以下映射类型定义:
type CreateMutable<T> = {
-readonly [K in keyof T]: T[K];
};
type Concrete<T> = {
[K in keyof T]-?: T[K];
};
type Getters<T> = {
readonly [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type PartialBy<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P];
} & {
[P in K]?: T[P];
};
关于上述定义,以下哪些说法是正确的?
关于 TypeScript 4.1+ 引入的映射类型 as 子句(键重映射),以下哪些说法是正确的?
关于 TypeScript 中的索引访问类型(Indexed Access Types),以下哪些说法是正确的?
考虑以下类型定义:
interface APIResponse {
data: { users: User[]; posts: Post[] };
meta: { page: number; total: number };
error?: string;
}
type DataSection = APIResponse['data'];
type MetaPage = APIResponse['meta']['page'];
type AllKeys = keyof APIResponse;
type PossibleValues = APIResponse[keyof APIResponse];
关于上述类型推导,以下哪些说法是正确的?
考虑以下条件类型定义:
type NonNullable<T> = T extends null | undefined ? never : T;
type Flatten<T> = T extends any[] ? T[number] : T;
type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N;
type IfNever<T, Y, N> = [T] extends [never] ? Y : N;
关于上述定义,以下哪些说法是正确的?
关于 TypeScript 条件类型 T extends U ? X : Y,以下哪些说法是正确的?
关于 TypeScript 中的类型守卫(Type Guards),以下哪些说法是正确的?
考虑以下代码:
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;
}
}
关于上述代码,以下哪些说法是正确的?
interface AdminUser {
role: 'admin';
permissions: string[];
username: string;
}
interface GuestUser {
role: 'guest';
username: string;
}
type User = AdminUser | GuestUser;
// 自定义类型守卫:检查用户是否拥有特定权限
function isAdmin(user: User): !!1_答案!! {
return 'permissions' in user && Array.isArray(user.permissions);
}
function checkAccess(user: User) {
if (isAdmin(user)) {
// 此处 user 类型应被收窄为 AdminUser
console.log(user.permissions); // 合法
}
}
请填写正确的类型注解,使 isAdmin 函数成为自定义类型守卫函数,在 if 分支内 TypeScript 能自动将 user 类型收窄为 AdminUser。
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 } 类型。
// 定义一个条件类型:如果 T 是字符串则返回模板字符串类型,否则返回 T 本身
type Stringify<T> = T extends string ? !!1_答案!! : T;
// 期望行为:
// Stringify<'hello'> => `hello` (模板字面量类型)
// Stringify<number> => number
// Stringify<string> => string
请填写正确的类型表达式,使 Stringify<T> 在 T 是字符串字面量类型时返回模板字面量类型 `${T}`,将字面量提升为模板类型。
const config = {
host: 'localhost',
port: 8080,
ssl: true,
timeout: 3000
};
type ConfigKey = !!1_答案!!;
// ConfigKey 应为 "host" | "port" | "ssl" | "timeout"
请填写正确的类型表达式,使 ConfigKey 类型为对象 config 的所有键的联合类型。
interface User {
name: string;
age: number;
active: boolean;
}
// 定义一个映射类型,使 User 的所有属性值类型变为可选(允许 undefined)
type OptionalValues<T> = {
[K in keyof T]: !!1_答案!!
};
// 期望行为:
// type Result = OptionalValues<User>;
// Result 应为 { name: string | undefined; age: number | undefined; active: boolean | undefined }
请填写正确的类型表达式,使 OptionalValues<T> 将类型 T 的每个属性值类型扩展为包含 undefined 的联合类型。
以下两种类型守卫写法在处理数据验证时有何区别?请分析各自的类型推导行为和使用场景:
// 写法一:is 类型谓词
function isString(value: unknown): value is string {
return typeof value === 'string';
}
// 写法二:asserts 类型谓词
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
throw new Error('Expected a string');
}
}
// 使用场景
function processA(input: unknown) {
if (isString(input)) {
console.log(input.toUpperCase());
}
}
function processB(input: unknown) {
assertIsString(input);
console.log(input.toUpperCase());
}
请说明:
- 两种写法在类型收窄行为上的差异
- 各自适用的编程场景
asserts谓词的返回值要求
以下代码存在类型问题,请分析原因并修复:
interface Database {
users: { id: number; name: string }[];
posts: { id: number; title: string }[];
comments: { id: number; text: string }[];
}
type TableName = keyof Database;
function getRecord<Table extends TableName>(
db: Database,
table: Table,
id: number
): Database[Table][number] {
return db[table].find(record => record.id === id);
}
// 问题:上述函数的返回类型推导是否正确?
// find 方法返回 T | undefined,但返回类型声明为 Database[Table][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]; };