22

Hegel:励志成为最好的 JavaScript 静态类型检查器

 3 years ago
source link: https://www.infoq.cn/article/6CmzywrCIsdkjGKycrwh
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Hegel 作为 JavaScript 类型检查器中的新秀,励志要成为最好的 JavaScript 静态类型检查器。它宣称提供了一个具备强类型推断的可靠的类型系统。目前 Hegel 还在 alpha 测试阶段,大家可以在其提供的 专用在线 练习场 进行功能体验。

Hegel 是一个类型注解可选的 JavaScript 类型检查器,同时它和 TypeScript 一样,使用者不需要重新学习一门新的语言结构,只需要掌握注解的语法。Hegel 希望通过强大的、稳定的类型系统,尽量避免程序在运行时由于类型错误产生的异常。下面的代码展示了其强大的类型检查能力:

复制代码

// 定义 numbers 的类型为 Array<number>
constnumbers:Array<number> = [];
// 将 numbers 变量赋值给 numbersOrStrings, 但其类型为 Array<string | number>
// Hegel 会检查出类型错误:
// HegelError:类型 "Array<number>" 和类型 "Array<number | string>" 不兼容
constnumbersOrStrings:Array<string | number> = numbers;
// 给 numbersOrStrings 第二个元素赋值
numbersOrStrings[1] ="Hello, TypeError!";
// 下面的代码 Hegel 会检查出类型异常:
// HegelError: 属性 "toFixed" 在 "Number | undefined" 中不存在
numbers[1].toFixed(1);

上面的代码使用 TypeScript(v3.8.3)进行编译时没有任何错误 ,但是在实际运行时确会抛出异常。

译者注:

JS 运行时,numbers 和 numbersOrStrings 都是引用类型。他们在相互赋值的时候,属于引用赋值。因此 numbersOrStrings 修改元素内容的时候,对 numbers 也同样有效果。所以在执行 numbers[1].toFixed(1) 时候,就会报错,因为字符串没有 toFixed 函数。上面的代码体现了 Hegel 可靠的类型系统。

除了可靠的类型系统,健壮的类型推断也是 Hegel 的主要设计目标。示例代码如下:

复制代码

// Hegel 会推断 "promisify" 是 "<_q, _c>((_c) => _q) => (_c) => Promise<_q>"
const  promisify =fn=>arg=>Promise.resolve(fn(arg));
// 这里,Hegel 会推断为 "<_c>(_c) => Promise<_c>"
constid = promisify(x=>x)
// 同样,"upperStr" 会被推断为 "Promise<string>"
constupperStr = id("It will be inferred").then(str=>str.toUpperCase()
// 最后 "twiceNum" 将会被推断为 "Promise<number>"
consttwicedNum = id(42).then(num  =>num ** 2);

在TypeScript(测试版本:3.7.5)中运行相同的代码 时,TS 会识别3 个异常,同时会将变量 result 推断为 Promise<any> 类型。 因此健壮的类型推断允许开发人员尽量少的编写代码注释,这样反而更有利于代码的可读性。

Hegel 将异常也纳入了类型检查中,示例代码如下:

复制代码

functionassert(age){
if(typeofage !=="number") {
thrownewTypeError("Age is not number.");
}
if(age <=0) {
thrownewReferenceError("Age can't be less or equals zero.");
}
}
try{
assert(0);
}catch(error) {
// 这里的 "error" 变量,被推断为 "ReferenceError | TypeError | unknown"
}

Hegel 的缺点是不支持强制类型转换和 any 类型,示例如下:

复制代码

// Error: Hegel 中不存在 any 类型
constsomething: any =null;
// Error: Hegel 中不支持类型转换
(null: any).call();

在 Hegel 文档中说明了其与主流类型检查器(TypeScript 和 Flow)的 对比 。除了支持标准类型(基础类型、函数、对象、类、数组)之外,Hegel 的类型系统还支持了未知类型(例如由 JSON.parse() 返回的类型)、可选类型、联合类型、元祖类型、类型别名、泛类型和可变类型。这也是 Hegel 的特性之一。

可变类型可以帮助我们从现有类型提取或者创建新类型。因此,可变类型可以理解为从一个类型生成另一个类型的函数。在 Hegel 中,定义了 17 个可变类型

下面是可变类型 $Exclude (语义上和 TypeScript 中的 Exclude 类型类似)的示例代码:

复制代码

// 定义类型
type Status ="Ok"|"Failed"|"Pending"|"Canceled";
// 使用 可变类型之后
// IntermediateStatuses = "Canceled" | "Panding"
type IntermediateStatuses = $Exclude<Status,"Ok"|"Failed">;
// 赋值
constintermediateStatuses:Array<$Exclude<Status,"Ok"|"Failed">> = ["Pending","Canceled"];
// Hegel 类型检查异常:
// Error: 类型 "['Failed']" 和类型 "...Array<'Canceled' | 'Pending'>" 不兼容
intermediateStatuses.push("Failed");

parceljs 的作者,Devon Govett 在 Twitter 上感慨说:

Hegel 看上去很有趣,它比 TS 更接近 Flow,而且是使用 JS 实现的。专注于类型推断和健壮类型,只有 JS 和类型 (没有额外的功能)。支持.d.ts、vscode 集成,等等…。

Hegel 发布在 npm 上,提供了一个命令行工具和 一个可交互的在线体验区 。同时,其在 GitHub 仓库 提供了对应的安装命令,要求 node.js 最低版本为 12。

Hegel 是基于 MIT 协议的。欢迎大家在该项目的 GitHub 上进行反馈和贡献。另外,Hegel 的作者也声明:

Hegel 源于社区,奉献社区;因此,你的任何 PRs 和 issues 都不会被忽略和遗忘。

原文链接:

https://www.infoq.com/news/2020/05/hegel-type-checking-javascript/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK