全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📝 1 篇文章 12 道配套习题

深度类型编程专题

专题说明

本专题涵盖TypeScript深度类型编程的高级技巧,包括类型级计算、类型验证、高级条件类型等极致类型编程知识。

学习目标

  1. 掌握类型级计算与类型验证技巧
  2. 理解高级条件类型的复杂应用
  3. 能够使用类型编程实现编译期验证

学习建议

这是TypeScript类型系统的巅峰内容,建议先掌握所有进阶知识后再学习。

📝 发现内容有误?点击此处直接编辑

📝 配套习题(12 题)

1
多选题

关于 TypeScript 中递归类型的特性,以下哪些说法是正确的?

A

在 TypeScript 4.1 之前,interface 可以隐式支持递归,而 type 别名不支持直接递归

B

递归类型可以用来定义树形结构,如二叉树 type Tree = { value: number; left?: Tree; right?: Tree }

C

递归类型的深度在运行时受到 JavaScript 调用栈的限制,但在编译时类型检查没有深度限制

D

使用递归类型时,TypeScript 编译器会在类型展开过程中进行"递归深度"检测以防止无限循环

2
多选题

以下哪些类型定义是正确的递归类型定义?

A

type LinkedList<T> = { value: T; next?: LinkedList<T> }

B

type NestedArray<T> = T | NestedArray<T>[]

C

type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T

D

type JsonValue = string | number | boolean | null | JsonObject | JsonArray; type JsonObject = { [key: string]: JsonValue }; type JsonArray = JsonValue[]

3
填空题

以下类型用于将嵌套数组展平为一维数组,请填写 ______ 处缺失的代码:

TypeScript
type Flatten<T> = T extends (infer U)[]
  ? !!1_答案!!
  : T;

// 测试:
// type Result = Flatten<number[][][]>  // 应为 number
// type Result2 = Flatten<string>       // 应为 string
4
简答题

请实现一个递归类型 DeepReadonly<T>,将对象的所有层级(包括嵌套对象和数组)都变为 readonly。请解释你的实现思路,并说明为什么需要递归。

5
多选题

关于 TypeScript 中递归类型别名(recursive type aliases),以下哪些说法是正确的?

A

在 TypeScript 4.1 中引入递归类型别名支持后,type 别名可以直接引用自身

B

type Json = string | number | boolean | null | { [key: string]: Json } | Json[] 在 TypeScript 4.1+ 中是合法的类型定义

C

递归类型别名在 TypeScript 中的展开深度限制与递归 interface 完全相同

D

递归类型别名可以用于定义 JSON Schema 的类型约束,因为它能精确描述 JSON 数据的所有可能形态

6
多选题

以下关于递归类型别名的使用限制和技巧,哪些说法是正确的?

A

递归类型别名必须至少有一个非递归的"基础情况"(base case),否则会导致类型无限展开

B

type Loop = Loop 是合法的递归类型别名定义,但类型 Loop 永远无法被实例化

C

递归类型别名可以与条件类型结合使用,实现类型级别的递归计算,如计算元组长度

D

TypeScript 对递归类型别名的展开有硬性深度限制,可以通过编译器配置调整此限制

7
填空题

以下类型用于提取对象中所有叶子节点值的联合类型,请填写 ______ 处缺失的代码:

TypeScript
type LeafValue<T> = T extends object
  ? !!1_答案!!
  : T;

// 测试:
// type Data = { a: { b: number }; c: string };
// type Result = LeafValue<Data>  // 应为 number | string
8
简答题

请比较递归类型别名(type)与递归接口(interface)在定义树形结构时的区别。请以二叉树为例,分别用两种方式定义,并分析它们在类型检查行为、可扩展性和使用场景上的差异。

9
多选题

关于 TypeScript 中泛型的协变(covariance)与逆变(contravariance),以下哪些说法是正确的?

A

对于函数类型 (x: Animal) => void(x: Dog) => void(其中 Dog extends Animal),前者是后者的子类型,这体现了参数位置的逆变性

B

TypeScript 数组类型 Array<T> 是协变的,即 Array<Dog>Array<Animal> 的子类型

C

TypeScript 严格模式下,函数参数的类型检查使用逆变规则,而函数返回值使用协变规则

D

一个泛型接口 Wrapper<T> 如果既在协变位置又在逆变位置使用 T,则 T 在该接口中是不变的(invariant)

10
多选题

以下哪些场景涉及函数类型变型规则的实际应用?

A

(e: MouseEvent) => void 赋值给期望 (e: Event) => void 的参数时,利用了参数位置的逆变性

B

定义 Promise<T> 时,T 只出现在 then 回调的返回值位置,因此 Promise<Dog>Promise<Animal> 的子类型

C

事件监听器 addEventListener('click', handler) 中,handler 可以接受比 MouseEvent 更窄的类型

D

Record<K, V> 中,K 出现在键位置(逆变),V 出现在值位置(协变),因此 Record<string, Dog>Record<string, Animal> 的子类型

11
填空题

已知 class Animal {}class Dog extends Animal {}class Cat extends Animal {}。请判断以下赋值是否合法,在 ______ 处填写 合法不合法

TypeScript
type Handler<T> = (value: T) => T;

declare let handlerAnimal: Handler<Animal>;
declare let handlerDog: Handler<Dog>;

// 赋值 1:handlerDog = handlerAnimal
handlerDog = handlerAnimal;  // ______
12
简答题

请解释为什么函数参数位置是逆变的,而返回值位置是协变的。请通过具体的代码示例说明,如果不遵守这些规则会导致什么样的类型安全问题。

← 上一个专题 泛型专题
下一个专题 → 环境搭建与基础配置专题

📝 发现内容有误?点击此处直接编辑

想查看更多习题和详细解析?
小程序提供完整的题库和详细解析

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库