Gleam 编译器 TypeScript 声明生成验证:typescript_declarations 集成测试解析

Gleam 编译器 TypeScript 声明生成验证:typescript_declarations 集成测试解析 Gleam 编译器 TypeScript 声明生成验证typescript_declarations 集成测试解析【免费下载链接】gleam⭐️ A friendly language for building type-safe, scalable systems!项目地址: https://gitcode.com/GitHub_Trending/gl/gleam本指南以仓库中 typescript_declarations 集成测试项目 为主线讲解 Gleam 语言在 JavaScript 目标下如何自动生成 TypeScript 类型声明.d.mts文件以及如何借助tsc经由bunx对这些声明进行编译级校验。读完本文你将掌握 Gleam 项目开启 TypeScript 声明生成的配置方法、satisfies断言与isX()类型收窄重载的验证思路以及该测试在仓库 CI 体系中的运行方式与底层实现位置。一、这个测试项目要解决什么问题test/typescript_declarations/README.md 的开篇只有一句话却精准概括了它的使命Check that generated TypeScript declarations are correct. It usestscviabunx.当 Gleam 代码被编译到 JavaScript 目标时编译器除了产出.mjs运行时模块还会根据配置生成配套的 TypeScript 声明文件.d.mts让 TypeScript 工程可以类型安全地消费 Gleam 模块。声明文件的正确性直接决定下游 TypeScript 使用体验类型别名是否保留、常量类型是否准确、泛型参数是否落在正确位置、记录变体的收窄函数isX()能否帮助类型系统正确推断——这些细节一旦出错TypeScript 消费者就会得到错误的类型提示甚至编译失败。该测试项目正是为此而生它编写了一个小而全的 Gleam 模块编译后生成声明文件再用 TypeScript 编译器tsc通过bunx调用无需全局安装以严格模式检查这些声明是否符合预期。这是一种用类型编译器验证类型编译器的集成测试方案。二、项目结构与各文件职责仓库中该测试项目的完整结构如下目录列表test/typescript_declarations/ ├── Makefile # 测试入口构建 tsc 校验 ├── README.md # 测试说明 ├── gleam.toml # 项目配置开启声明生成 ├── manifest.toml # 依赖锁文件 ├── main.ts # 主声明校验脚本satisfies 断言 ├── typescript_is_overload.ts # 类型收窄重载校验脚本 └── src/ ├── typescript_declarations.gleam # 被测试的 Gleam 模块 └── typescript_is_overload.gleam # 泛型自定义类型Box整个测试的流程是Gleam 源码 →gleam build编译生成.d.mts声明 → 两个.ts脚本用tsc --strict对声明做类型断言校验。任何一环的类型信息不正确tsc都会报错测试即失败。三、如何开启 TypeScript 声明生成3.1 配置项[javascript] typescript_declarations true测试项目的 gleam.toml 给出了最小可用配置name typescript_declarations version 1.0.0 target javascript [javascript] typescript_declarations true [dependencies] gleam_stdlib 0.44.0 and 2.0.0关键点有两个target javascript声明生成仅作用于 JavaScript 编译目标Erlang 目标不产出 TypeScript 声明[javascript] typescript_declarations true显式开启声明文件生成开关。在编译器源码中该开关对应 compiler-core/src/config.rs 里的pub typescript_declarations: bool字段并在package_config_to_json的快照如 compiler-core/src/snapshots/gleam_core__config__package_config_to_json.snap中序列化保存。从源码结构可以推断该字段由gleam.toml的[javascript]段解析得到随后传递给 JavaScript 代码生成阶段决定是否渲染.d.mts文件。3.2 依赖约束manifest.toml 锁定gleam_stdlib为0.62.1要求 0.44.0 and 2.0.0。测试模块function_option()中直接调用了gleam/option的option.Some(0)其声明Option$正是从build/dev/javascript/gleam_stdlib/gleam/option.d.mts导入的——这意味着测试同时校验了标准库生成的声明与被测项目自身生成的声明。四、声明正确性如何被验证satisfies断言4.1 校验脚本 main.tsmain.ts 是声明校验的核心其注释说明了策略// These statements use the satisfies keyword to assert the types are // what we expect.它先从构建产物导入类型与模块import type { Option$ } from ./build/dev/javascript/gleam_stdlib/gleam/option.d.mts; import type { List } from ./build/dev/javascript/typescript_declarations/gleam.d.mts; import * as gleam from ./build/dev/javascript/typescript_declarations/typescript_declarations.mjs;然后逐条对每个导出符号做类型断言。satisfies是 TypeScript 4.9 的关键字它要求表达式的类型与给定类型兼容但不会像类型注解那样收窄推断结果非常适合断言类型是预期值的测试场景。例如gleam.const_int satisfies number gleam.const_int_alias satisfies number gleam.const_int_list satisfies Listnumber gleam.const_string_list satisfies Liststring gleam.const_tuple satisfies [string, number] gleam.either_int satisfies gleam.Either$number, number gleam.function_int_int_returns_int_alias satisfies (a: number, b: number) number gleam.function_closure_returns_fn_int_which_returns_int_alias satisfies () (a: number) number这些断言覆盖了声明生成器的各类核心场景被测 Gleam 符号断言的 TS 类型验证要点const_int/const_int_aliasnumberInt别名在声明中正确展开为numberconst_string_list/const_int_listListstring/Listnumber泛型容器List的元素类型正确const_tuple[string, number]元组被映射为 TS 元组类型either_intEither$number, number泛型自定义类型Either(a, b)的类型参数按声明顺序填充function_int_int_returns_int_alias(a: number, b: number) number函数别名保留完整函数签名function_closure_returns_fn_int_which_returns_int_alias() (a: number) number闭包返回的高阶函数签名正确4.2 泛型函数的断言对泛型函数function_generic_fn_generic_which_returns_generic_returns_generic测试定义了具名泛型别名来断言type GenericFnT any (a: T, fn: (a: T) T) T; gleam.function_generic_fn_generic_which_returns_generic_returns_generic satisfies GenericFn对应 typescript_declarations.gleam 中的定义pub fn function_generic_fn_generic_which_returns_generic_returns_generic( val: a, function: fn(a) - a, ) - a { function(function(val)) }以及标准库 Option 的消费场景gleam.function_option satisfies () Option$number对应 Gleam 侧function_option() - Option(Int)返回option.Some(0)。这里连标准库gleam/option.d.mts的Option$number一并被验证。4.3 被测 Gleam 模块typescript_declarations.gleam 是一个麻雀虽小五脏俱全的样例模块刻意覆盖了声明生成器的主要语法面类型别名pub type IntAlias Int各类常量Int、IntAlias、List(String)、List(Int)、元组#(hello, 0)函数及其别名function_int_int_returns_int与pub const function_int_int_returns_int_alias function_int_int_returns_int高阶函数与闭包function_closure_returns_fn_int_which_returns_int_alias泛型函数function_generic_fn_generic_which_returns_generic_returns_generic泛型自定义类型pub type Either(a, b) { Left(a) Right(b) }标准库类型消费返回Option(Int)五、类型收窄重载isX()的专项验证5.1 为什么要单独测试isX()Gleam 的自定义类型编译到 JavaScript 后每个变体都会生成一个isVariant()判定函数用于在运行时区分记录属于哪个变体。为了让 TypeScript 消费者在if (isX(value))分支内获得类型收窄type narrowing能力声明生成器必须为这些函数生成带有泛型参数的重载签名overload。第二个脚本 typescript_is_overload.ts 专门验证这一点。测试用了一个三类型参数的自定义类型// src/typescript_is_overload.gleam pub type Box(a, b, c) { AlmostFull(a, c) AlmostEmpty(b) Empty }三个变体分别持有不同位置、不同数量的类型参数AlmostFull使用a、cAlmostEmpty使用bEmpty不使用任何参数——这是对收窄逻辑最刁钻的考验。5.2 断言逻辑收窄前后的类型对比const almost_empty $box.Box$AlmostEmpty(1); almost_empty satisfies $box.Box$unknown, number, unknown; if ($box.Box$isAlmostEmpty(almost_empty)) { almost_empty satisfies $box.AlmostEmptynumber; }要点解读构造器返回的是完整的Box$a, b, c其中未使用的类型参数位置被填充为unknown如Box$unknown, number, unknown一旦通过Box$isAlmostEmpty(almost_empty)判定tsc必须能把变量收窄为具体变体类型AlmostEmptynumber并且b参数被正确保留为number三个断言组合起来同时验证了AlmostFullBox$string, unknown, number→AlmostFullstring, number、AlmostEmpty和EmptyBox$unknown, unknown, unknown→Empty三种收窄路径。如果生成的isX()重载签名中泛型参数位置写错、类型参数个数不对或缺少收窄重载if分支内的satisfies断言就会编译失败。5.3 源码侧的实现对应从 compiler-core/src/javascript/typescript.rs 的variant_check_definition函数可以看到声明生成器对每个记录构造器variant会生成两类东西一个宽泛的判定签名export function Box$isAlmostEmpty(value: any): value is Box$unknown, number, unknown——未参与该变体的类型参数位置显式输出为unknown当变体携带类型参数时额外生成一个重载签名export function Box$isAlmostEmptya, b, c(value: Box$a, b, c): value is AlmostEmptyb——判定通过后把值收窄为携带正确泛型实参的变体类型。从源码结构看第 748 行的overload分支、第 735744 行对unknown占位符的拼接逻辑重载签名会先于宽泛签名输出从而让 TypeScript 在类型收窄时优先匹配精确的重载版本。这正好解释了测试脚本中if分支内类型变为具体变体的行为。六、如何运行Makefile 与 tsc 参数6.1 测试命令项目级 Makefile 定义了test目标.PHONY: test test: cargo run --quiet -- build bunx tsc ./main.ts --strict --noEmit --skipLibCheck false --lib es2020,dom bunx tsc ./typescript_is_overload.ts --strict --noEmit --skipLibCheck false --lib es2020,dom命令拆解cargo run --quiet -- build调用当前仓库的 Gleam 编译器cargo直接运行gleam-bin执行build把src/下的 Gleam 模块编译为build/dev/javascript/...下的.mjs与.d.mtsbunx tsc ...通过bunxBun 自带的包执行器等价于npx临时拉取并运行 TypeScript 编译器无需在项目里安装typescript依赖./main.ts与./typescript_is_overload.ts分别代表两组独立的断言脚本。6.2 各 tsc 参数的作用参数含义在本测试中的意义--strict开启全部严格类型检查声明文件必须在最严格模式下通过杜绝恰好能用的宽松声明--noEmit只做类型检查不输出 JS测试只关心类型正确性--skipLibCheck false对.d.ts/.d.mts声明文件本身做检查关键让tsc直接检查 Gleam 生成的声明文件内部的类型一致性而不只是消费方视角--lib es2020,dom指定可用的标准库类型ES2020 DOM为脚本提供Promise、console等环境类型同时限定声明的兼容基线6.3 在仓库整体测试中的位置根目录 Makefile 提供了独立入口.PHONY: typescript-declarations-test typescript-declarations-test: ## Check that generated TypeScript declaration compile cd test/typescript_declarations make test同时根 Makefile 的test目标第 27 行cd test/typescript_declarations make test已把该项目纳入全量集成测试与test/language、test/javascript_prelude、test/project_javascript等项目并列执行。也就是说任何改动只要影响了声明生成器的输出运行根目录make test或单独的make typescript-declarations-test即可立刻发现回归。6.4 本地复现步骤在已安装 Rust 工具链与 Bun提供bunx的环境中# 方式一直接进入测试目录 cd test/typescript_declarations make test # 方式二通过仓库根 Makefile make typescript-declarations-testmake clean可清理build产物后重新构建见 Makefile。需要说明的是仓库中的test/与build/目录为运行期产物编译成功后自动生成无需手工准备。七、从测试反推的声明生成器设计要点综合上述测试与源码可以归纳出 Gleam TypeScript 声明生成器在设计上的几条可验证约束类型别名直接展开IntAlias在声明中解析为number不保留多余的中间别名层见 typescript_declarations.gleam 与 main.ts 的断言元组、列表等内建类型映射为 TS 原生结构元组 → 元组类型List→ 带泛型参数的ListT声明从gleam.d.mts导入List可见自定义类型的变体收窄依赖重载isX()通过重载 unknown占位双签名实现精确收窄typescript.rs字段访问符生成位置索引函数variant_fields_definitiontypescript.rs 起为每个变体字段生成Type$Variant$Index形式的取值函数保证添加标签字段不破坏既有访问方式标准库与项目声明同时被校验测试脚本同时 import 了gleam_stdlib与项目自身的.d.mts声明生成器对任意 Gleam 包一视同仁。八、总结typescript_declarations 项目虽小却是 Gleam JavaScript 目标类型安全承诺的关键一环它用tsc --strict --skipLibCheck false双重严格模式把生成的 TypeScript 声明必须正确从口头约定变成了可自动执行的集成测试。无论是常量、类型别名、泛型自定义类型还是isX()的类型收窄重载每一个声明细节都被satisfies断言钉死。对希望在自己的 Gleam TypeScript 混合工程中开启[javascript] typescript_declarations true的开发者而言这个测试项目既是最好的配置范例也是理解声明生成器行为边界的活文档。【免费下载链接】gleam⭐️ A friendly language for building type-safe, scalable systems!项目地址: https://gitcode.com/GitHub_Trending/gl/gleam创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考