{:main my-project.core
:output-to "out/main.js"
:output-dir "out"
:optimizations :none
:infer-externs true})
本指南需要 ClojureScript 1.10.238 或更高版本,并假设您熟悉 快速入门.
本页介绍如何为不符合 Google Closure Compiler 规范的第三方 JavaScript 库编写外部声明 https://developers.google.com/closure/compiler/docs/limitations.
许多有用的库无法通过 Google Closure Compiler 高级编译。因此,它们不能成为构建的一部分,被视为“外来”库。但 Closure 必须了解这些库的一些信息,否则可能会意外地重命名属性。不幸的是,这种意外的重命名通常在最不合适的时候才会显现出来,例如在生产环境中。
对于已经拥有成熟外部声明的库来说,这种错误很容易避免。然而,这种要求为采用更新或不太流行但同样有用的库带来了极大的阻力。随着外部声明推断功能的出现,ClojureScript 编译器现在可以自动生成缺失的外部声明,并且可以极大地帮助编写全面外部声明的过程。
假设我们已经指定了一个外来库 some.fooLib
。我们希望针对该库编写互操作代码,并确信编译器会自动生成正确的外部声明,或者会提醒我们必须额外提供的外部声明。
为了启用外部声明推断,我们在编译器配置中指定 :infer-externs true
。
创建一个包含以下内容的 build.edn
文件
{:main my-project.core
:output-to "out/main.js"
:output-dir "out"
:optimizations :none
:infer-externs true})
但是这还不够让编译器生成有关外部声明的警告。由于在该功能出现之前编写了大量的库,因此我们无法以全局方式启用该功能。相反,有一个新的文件级编译器标志 *warn-on-infer*
,它类似于 Clojure 中的 *warn-on-reflection*
。一旦设置,编译器会在文件剩余部分的任何时候发出警告,只要它无法确定点形式(无论是属性访问还是方法调用)中涉及的类型。
(ns my-project.core
(:require [some.fooLib]))
(set! *warn-on-infer* true)
(defn wrap-baz [x]
(.baz x))
上面的代码将触发警告消息
Cannot infer target type in expression (.baz x) ...
我们只需要用外来类型对 x
进行类型提示,以进行此互操作调用
(ns my-project.core
(:require [some.fooLib]))
(set! *warn-on-infer* true)
(defn wrap-baz [^js/Foo.Bar x]
(.baz x))
现在,编译器拥有足够的信息来自动生成所需的外部声明。运行构建时,您将在输出目录中看到一个新文件 inferred_externs.js
。如果您检查其内容,它可能类似于以下内容
var Foo = {};
Foo.Bar = function() {};
Foo.Bar.prototype.baz = function() {};
现在,无需完整外部声明即可轻松集成外来 JavaScript 库,并且减少了错误的可能性。
在某些情况下,您可能仍然希望编写外部声明,或者您可能是使用成熟外部声明的流行 JavaScript 库的用户,并且希望进行更多验证。下一部分将介绍外部声明推断提供的另一个有用功能。
局部类型提示对于自动化编写外部声明的过程非常有用。但是,对于互操作性很强的代码,这会导致大量的类型提示,特别是对于常用函数的返回值。在这种情况下,最好提供外部声明文件。即使在这种情况下,ClojureScript 编译器也可以简化该过程
(ns my-project.core
(:require [some.fooLib]))
(set! *warn-on-infer* true)
(defn my-fn [^js/Foo.Bar x]
(let [z (.baz x)]
(.-wozz z)))
假设我们的外部声明文件类似于以下内容
var Foo = {};
/**
* @constructor
*/
Foo.Bar = function() {};
Foo.Bar.prototype.baz = function() {};
/**
* @constructor
*/
Foo.Boo = function() {};
Foo.Boo.prototype.woz = function() {};
但是,这不足以知道 ClojureScript 程序中 z
的类型。ClojureScript 编译器将发出以下警告
WARNING: Adding extern to Object for property wozz due to ambiguous expression (. z -wozz) ...
我们需要在外部声明文件中添加返回值类型信息
var Foo = {};
/**
* @constructor
*/
Foo.Bar = function() {};
/**
* @return {Foo.Boo} <-- CHANGED
*/
Foo.Bar.prototype.baz = function() {};
/**
* @constructor
*/
Foo.Boo = function() {};
Foo.Boo.prototype.woz = function() {};
修改源文件并重新运行构建将导致不同的警告
WARNING: Cannot resolve property wozz for inferred type js/Foo.Boo in expression (. z -wozz)
如我们所见,ClojureScript 使用返回值类型信息来澄清问题。
原作者:David Nolen