{: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