news 2026/9/13 16:41:52

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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_stdlib0.62.1(要求>= 0.44.0 and < 2.0.0)。测试模块function_option()中直接调用了gleam/optionoption.Some(0),其声明(Option$)正是从build/dev/javascript/gleam_stdlib/gleam/option.d.mts导入的——这意味着测试同时校验了标准库生成的声明与被测项目自身生成的声明。

四、声明正确性如何被验证:satisfies断言

4.1 校验脚本 main.ts

main.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 List<number> gleam.const_string_list satisfies List<string> 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别名在声明中正确展开为number
const_string_list/const_int_listList<string>/List<number>泛型容器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 GenericFn<T = 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.mtsOption$<number>一并被验证。

4.3 被测 Gleam 模块

typescript_declarations.gleam 是一个"麻雀虽小五脏俱全"的样例模块,刻意覆盖了声明生成器的主要语法面:

  • 类型别名:pub type IntAlias = Int
  • 各类常量:IntIntAliasList(String)List(Int)、元组#("hello", 0)
  • 函数及其别名:function_int_int_returns_intpub 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 后,每个变体都会生成一个is<Variant>()判定函数,用于在运行时区分记录属于哪个变体。为了让 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使用acAlmostEmpty使用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.AlmostEmpty<number>; }

要点解读:

  • 构造器返回的是完整的Box$<a, b, c>,其中未使用的类型参数位置被填充为unknown(如Box$<unknown, number, unknown>);
  • 一旦通过Box$isAlmostEmpty(almost_empty)判定,tsc必须能把变量收窄为具体变体类型AlmostEmpty<number>,并且b参数被正确保留为number
  • 三个断言组合起来,同时验证了AlmostFullBox$<string, unknown, number>AlmostFull<string, number>)、AlmostEmptyEmptyBox$<unknown, unknown, unknown>Empty)三种收窄路径。

如果生成的isX()重载签名中泛型参数位置写错、类型参数个数不对或缺少收窄重载,if分支内的satisfies断言就会编译失败。

5.3 源码侧的实现对应

从 compiler-core/src/javascript/typescript.rs 的variant_check_definition函数可以看到,声明生成器对每个记录构造器(variant)会生成两类东西:

  1. 一个"宽泛"的判定签名:export function Box$isAlmostEmpty(value: any): value is Box$<unknown, number, unknown>——未参与该变体的类型参数位置显式输出为unknown
  2. 当变体携带类型参数时,额外生成一个重载签名export function Box$isAlmostEmpty<a, b, c>(value: Box$<a, b, c>): value is AlmostEmpty<b>——判定通过后把值收窄为携带正确泛型实参的变体类型。

从源码结构看(第 748 行的overload分支、第 735~744 行对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.mts
  • bunx tsc ...:通过bunx(Bun 自带的包执行器,等价于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)为脚本提供Promiseconsole等环境类型,同时限定声明的兼容基线

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/languagetest/javascript_preludetest/project_javascript等项目并列执行。也就是说,任何改动只要影响了声明生成器的输出,运行根目录make test或单独的make typescript-declarations-test即可立刻发现回归。

6.4 本地复现步骤

在已安装 Rust 工具链与 Bun(提供bunx)的环境中:

# 方式一:直接进入测试目录 cd test/typescript_declarations make test # 方式二:通过仓库根 Makefile make typescript-declarations-test

make clean可清理build产物后重新构建(见 Makefile)。需要说明的是,仓库中的test/build/目录为运行期产物,编译成功后自动生成,无需手工准备。

七、从测试反推的声明生成器设计要点

综合上述测试与源码,可以归纳出 Gleam TypeScript 声明生成器在设计上的几条可验证约束:

  1. 类型别名直接展开IntAlias在声明中解析为number,不保留多余的中间别名层(见 typescript_declarations.gleam 与 main.ts 的断言);
  2. 元组、列表等内建类型映射为 TS 原生结构:元组 → 元组类型,List→ 带泛型参数的List<T>声明(从gleam.d.mts导入List可见);
  3. 自定义类型的变体收窄依赖重载isX()通过"重载 +unknown占位"双签名实现精确收窄(typescript.rs);
  4. 字段访问符生成位置索引函数variant_fields_definition(typescript.rs 起)为每个变体字段生成Type$Variant$Index形式的取值函数,保证添加标签字段不破坏既有访问方式;
  5. 标准库与项目声明同时被校验:测试脚本同时 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 16:39:55

CAN总线故障排查:先查物理层再抓报文

1. 大多数人查CAN总线故障&#xff0c;第一步就错了——不是看报文&#xff0c;而是先“听”物理层你有没有遇到过这样的场景&#xff1a;整车报“网关通信超时”&#xff0c;诊断仪读出一串UDS故障码&#xff08;比如U0100、U0121&#xff09;&#xff0c;工程师立刻打开CANoe…

作者头像 李华
网站建设 2026/9/13 16:38:04

Sa-Token 前后端分离鉴权实战:无 Cookie 模式下 Token 的下发、存储与提交

Sa-Token 前后端分离鉴权实战&#xff1a;无 Cookie 模式下 Token 的下发、存储与提交 【免费下载链接】Sa-Token ✨ 开源、免费、一站式 Java 权限认证框架&#xff0c;让鉴权变得简单、优雅&#xff01;—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单…

作者头像 李华
网站建设 2026/9/13 16:36:26

电商导购返利小程序实战:淘宝京东拼多多联盟API对接与uniapp开发

简介&#xff1a;首席省钱赚钱专家v1.9.18小程序源码&#xff0c;面向个人创业者、电商运营与小程序开发者&#xff0c;基于拼多多优惠商品接口&#xff0c;实现购物返利、推广分销、团队奖励等典型电商小程序功能&#xff0c;帮助快速搭建“自购省钱分享赚钱”的应用场景。资源…

作者头像 李华
网站建设 2026/9/13 16:36:17

基于JSP的银行预约管理系统:从Servlet原理到并发排错实战

简介&#xff1a;这是一份基于JSPSQLServerTomcat技术栈的银行预约管理系统毕业设计源码包&#xff0c;面向Java Web方向的毕业生或需要快速搭建预约类管理系统的开发者&#xff0c;解决银行业务预约、客户信息管理、后台审核等环节的一体化实现问题。资源共499个文件&#xff…

作者头像 李华
网站建设 2026/9/13 16:35:46

基于Simulink的OFDM瑞利信道估计与同步仿真指南

简介&#xff1a;OFDM系统在瑞利衰落信道下的信道估计&#xff0c;是无线通信领域的典型问题。这套Simulink仿真资源提供了完整的导频辅助估计实现方案&#xff0c;适合通信专业本科生、研究生以及进行课程设计、毕业设计的开发者&#xff0c;既能帮助理解导频插入与信道估计原…

作者头像 李华