定义类型构造器
将类型构造器编码为接口查找。
定义类型构造器 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 4 节课。
命名类型构造器
编码首先要为每个类型构造器提供一个唯一的字符串标识符,称为统一资源标识符。统一资源标识符是一种标签,在无法传递构造器本身的地方代替它。
type ArrayURI = "Array";
type OptionURI = "Option";
// Each container gets a unique string tag一个注册表接口
我们维护一个统一的接口,将每个统一资源标识符映射到它针对给定参数 A 所产生的具体类型。这个接口就是注册表。每个键都是一个统一资源标识符,每个值都是解析后的类型。
interface URItoKind<A> {
Array: Array<A>;
Option: A | null;
}
// URItoKind<number>["Array"] is number[]查找构造器
由于注册表以统一资源标识符为键,应用构造器就变成了一次索引访问。要获取“将数组构造器应用于数字”的结果,您只需对注册表进行索引。
type ArrayOfNumber = URItoKind<number>["Array"]; // number[]
type OptionOfString = URItoKind<string>["Option"]; // string | nullURIS 联合类型
有效构造器标签的集合就是注册表的键。keyof URItoKind<unknown> 会给出所有已注册统一资源标识符的联合类型,我们将其命名为 URIS。
type URIS = keyof URItoKind<unknown>; // "Array" | "Option"声明合并扩展注册表
TypeScript 有一项强大的功能:同名接口会合并。库会利用这一点,让每个模块都能将自己的容器添加到 URItoKind,而无需编辑原始定义。
interface URItoKind<A> {
Tree: { value: A; children: Array<A> };
}
// Now URIS also includes "Tree"模块化容器
得益于声明合并,在一个文件中定义的容器可以将自身注册到全局。任何导入它的代码都可以在泛型抽象中使用它的统一资源标识符。这正是 fp-ts 添加选项、二选一、任务等类型的方式。
// file a.ts
interface URItoKind<A> { Either: { left: A } | { right: A } }
// file b.ts can now reference the "Either" URI一个幽灵载体
为了以值级类型传递“是哪一种构造器”,实例会在某个属性中携带其统一资源标识符,通常是一个幽灵字段。这个字段从不保存真实值,它只为类型系统记录该标签。
interface HasURI<F extends URIS> {
readonly _URI: F;
}
// _URI records which constructor an instance is for将统一资源标识符与参数配对
一次构造器应用需要两样东西:统一资源标识符 F 和参数类型 A。我们会通过查找将它们组合起来,使 (F, A) 能通过注册表解析为具体类型。
type Apply<F extends URIS, A> = URItoKind<A>[F];
type A = Apply<"Array", number>; // number[]
type B = Apply<"Option", string>; // string | null为什么使用字符串而不是构造器
我们使用字符串,是因为 TypeScript 可以将字符串存储并比较为字面量类型,也可以用它们索引注册表。我们无法将构造器 Array 作为类型参数存储,但可以存储字面量 "Array" 并对其进行查找。
type Tag = "Array";
type Resolved = URItoKind<boolean>[Tag]; // boolean[]查找是关键步骤
这个索引访问 URItoKind<A>[F] 是整个编码的核心。它将不可能实现的类型应用 F<A> 转换为合法的注册表查找。下一课会将它正式定义为 Kind<F, A>。
type Kind<F extends URIS, A> = URItoKind<A>[F];
type X = Kind<"Array", number>; // number[]设置部分回顾
现在您已经拥有了各个组成部分:作为标签的统一资源标识符、将标签和参数映射到具体类型的 URItoKind 注册表、作为标签联合类型的 URIS,以及用于扩展注册表的声明合并。对注册表进行索引就可以模拟类型应用。
// URItoKind = registry, URIS = valid tags,
// URItoKind<A>[F] = "apply constructor F to A"快速检查
测试您对注册表编码的理解。
回顾
您学会了使用字符串编码类型构造器。
- 每个构造器都会获得一个唯一的统一资源标识符标签。
URItoKind<A>将标签映射到具体类型。URIS通过keyof表示所有标签的联合类型。- 声明合并让模块能够注册新的容器。
下一步:将查找封装为轻量级的 Kind 模式。
常见问题解答
「定义类型构造器」课时是免费的吗?
是的 — 「定义类型构造器」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 4 节课。
「定义类型构造器」这节课中我会学到什么?
将类型构造器编码为接口查找。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 TypeScript Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「定义类型构造器」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 TypeScript Academy 课中编写并运行代码吗?
能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。