Cocos Creator 3.0 TypeScript 使用答疑
3.0 合并了原有 2D 和 3D 兩套產(chǎn)品的所有功能,不僅延續(xù)了 Cocos 在 2D 品類上輕量高效的優(yōu)勢,并且為 3D 重度游戲提供高效的開發(fā)體驗。
我們很高興看到 3.0 在發(fā)布后引發(fā)了熱烈的討論,也收到許多同學的反饋,這當中也包括了許多不一樣的聲音,有人覺得我們想太多,也有人覺得我們想太少,等等等等。
我們也知道大家都希望我們第一個版本就非常完美、沒有 bug,我們呢當然是希望大家不要有這種希望,開玩笑開玩笑哈哈哈。
其實社區(qū)的帖子我們都有看到,始終非常感謝大家一直以來的默默支持,和耐心等待。這次我們也集結(jié)引擎組所有功能開發(fā)的一線大佬,開辟《Cocos Creator 3.0 技術(shù)專欄》,希望能幫助大家絲滑升級,減少開發(fā)阻礙。
我們也會在 3.0 的基礎(chǔ)上繼續(xù)深耕,用心踏踏實實完善產(chǎn)品體驗,普惠所有開發(fā)者。希望大家可以感受到我們真誠的態(tài)度和敬業(yè)的精神,放心地給 3.0 一個機會。
本篇為專欄第一篇文章,作者 Leslie+Jare,希望能給大家?guī)韼椭?,歡迎閱讀。
雖然 Creator 很早就支持了 TypeScript,直到 3.0 我們才正式廢棄了對 JavaScript 的支持。但隨著這次 3.0 的全面升級,很多第一次使用 TypeScript 的開發(fā)者仍然遇到了不少阻礙。
本文將對這次調(diào)整做一次簡要回顧,并且解答開發(fā)者在語言升級的過程中容易遇到的一些問題。
我們在 TS 上的鋪墊
我們對 TypeScript 的支持由來已久,最早可追溯到 17 年 5 月發(fā)布的 v1.5.0,此后我們一直在聽取 TS 開發(fā)者的反饋,逐步完善 TS 的支持。
在 18 年的時候,3D 項目組開始用 TS 重寫整個 3D 引擎和編輯器,驗證了 TS 處理超大規(guī)模項目時的能力,讓我們對 TS 有了更大的信心和第一手的使用經(jīng)驗。
到了 19 年,我們在 Cocos Creator 3D 正式發(fā)布時,第一次正式廢棄了對 JavaScript 的支持,而后持續(xù)聽取開發(fā)者們對這一改變的反饋。
要采用哪一門語言做為引擎的開發(fā)語言,不是一個能輕易做出的決定,對開發(fā)者、對生態(tài)都影響巨大。
Cocos Creator 當初選擇 JS 時,已經(jīng)歷過一次飽受非議,這次如果不是有前面幾年在 TS 上的鋪墊,我們也沒有勇氣在 3.0 做出這個關(guān)鍵調(diào)整。
為什么要轉(zhuǎn)向 TS
在 Cocos Creator 14 年剛立項之初,由于熟悉的語法,TypeScript 成為了我們?nèi)腴T JavaScript 的良好橋梁。但由于當時的 TS 剛剛正式發(fā)布,生態(tài)仍未成熟,因此我們只能先使用最原生的 JavaScript。
經(jīng)過了這么多年的發(fā)展,今天 TS 已經(jīng)成為一門非常成熟的語言,有豐富的生態(tài)和群眾基礎(chǔ),可以勝任任何類型的游戲開發(fā)需要!
從我這幾年跟研發(fā)團隊的接觸來看,大多數(shù)團隊都逃不過 TS 的真香定律,網(wǎng)上也有大量對 TS 的安利文章,我就不再贅述 TS 的優(yōu)勢,直接從我們的角度分享下,我們是怎么想的。
我們始終相信,良好的編程語言是項目成功的基石。
誠然,任何語言和框架都只是工具,優(yōu)秀的程序員不論用什么工具都能把項目做好。不過良好的語言更能降低項目整體風險,讓開發(fā)人員心情愉悅,提升協(xié)作效率,從長遠來看更能降低整個團隊的研發(fā)成本!
我們堅信,Cocos Creator 不僅僅是一個小游戲引擎,它必定要能支撐大型重度游戲的開發(fā)。
JavaScript 或許能滿足高手自己做一些小項目的需要,能快速做點小東西玩玩,但是達不到大型項目的工業(yè)標準。
我們意識到,JavaScript 做為弱類型語言,遲早會成為 Cocos Creator 項目的優(yōu)化瓶頸,只有強類型語言才有機會徹底提升整個項目的性能表現(xiàn)。
所以我們持續(xù)在探索 JavaScript 編譯為原生語言的可能性,這離不開 TypeScript 的支持。
今天已經(jīng)有 AssemblyScript 這樣成熟的項目支持將 TypeScript 方言編譯為 WebAssembly,事實上同類項目還有很多。
只有從生態(tài)、社區(qū)的角度統(tǒng)一大家的開發(fā)語言,將來我們才有機會為大家送上這份大禮。
語言的割裂會阻礙生態(tài)的發(fā)展。
今天在 Cocos Creator 社區(qū)已經(jīng)擁有了一幫 TypeScript 的簇擁,很多優(yōu)質(zhì)的教程、插件、帖子都使用 TypeScript 發(fā)布。如果官方文檔、官方案例都使用一門語言編寫,將不利于另一門語言的學習。
如果社區(qū)長期擁有兩門語言,更不利于大家復用前人的工作成果。JavaScript 開發(fā)者無法適應 TypeScript 項目的維護,TypeScript 開發(fā)者在復用原有的 JavaScript 組件時也會遇到阻礙。
常見的 TypeScript 認知誤區(qū)
誤區(qū):Cocos Creator 僅支持 TypeScript,不支持 JavaScript
TypeScript 是 JavaScript 的超集并且 TypeScript 緊緊依賴 JavaScript。Cocos Creator 3.0 仍支持 TypeScript 和 JavaScript 并用。
然而,Cocos Creator 鼓勵用戶使用 TypeScript 以獲得更好的開發(fā)體驗,提高開發(fā)質(zhì)量,因此在編輯器中僅支持創(chuàng)建 TypeScript 腳本。
如果你確定一定要使用 JavaScript,以其他方式(資源管理器、訪達等)創(chuàng)建 JavaScript 文件仍然是允許的。
誤區(qū):import/export 是 TypeScript 專有的,我無法在 JavaScript 中使用 require
import/export 是 JavaScript 規(guī)范 ECMAScript 2015 引入的對模塊支持的語法,因此 JavaScript 中是可以使用的,并不是 TypeScript 專有。
另一方面,require/exports/module 是 CommonJS 模塊系統(tǒng)中的變量。它們不屬于 JavaScript 標準。
盡管,在 3.0 中提供了對 CommonJS 模塊的有限支持
將 JavaScript 代碼
遷移為 TypeScript
上述有提到 JavaScript 可以直接使用。但只要了解 TypeScript 的用法,將 JavaScript 代碼遷移為 TypeScript 也是一件輕松的事。
最簡單的遷移:改后綴名
可以直接將 .js 文件重命名為 .ts 完成最簡單的遷移——只要 JavaScript 在運行時沒問題,那么如此改為 TypeScript 也一定是沒問題的。
改名后,在 IDE 中會有一大堆報錯,這是因為缺少類型信息。如果你暫時不想解決這些類型問題,大可以在整個文件的頭部加一句注釋:
// @ts-nocheck來跳過對該文件的類型檢查。
在代碼中補充類型信息
對于比較簡單的 JavaScript 代碼,只要稍加補充一些類型信息就可以。例如:
function fn(a, b, c) {// ...}
為 fn 指定參數(shù)類型就 OK:
function fn(a: string, b: number, c: boolean) {// ...}
TypeScript 會自動根據(jù)函數(shù)體內(nèi)的代碼推斷出函數(shù)的返回值。你當然也可以顯式指定:
function fn(a: string, b: number, c: boolean): string {// ...}
在代碼外部聲明類型信息
有些時候,JavaScript 代碼不是我們自己寫的,而是由第三方提供的庫代碼。這時候我們可以在不編輯它的情況下為它補充類型信息。
例如,有一個第三方的 JavaScript 文件 foo.js:
module.exports = function foo (a, b, c) {// ...}
我們可以在同目錄下創(chuàng)建一個同名但擴展名為 .d.ts 的 foo.d.ts:
module.exports = function foo (a, b, c) {// ...}
嚴格模式:“null” 問題
從 JavaScript 轉(zhuǎn)入 TypeScript 的同學可能被一些“類型問題”所困擾。
看這樣一個問題:
class C {material: Material = null;}
這段代碼在 IDE 中會報錯,報錯源頭是屬性 material 的聲明。
有一種情況是,material 屬性僅在初始化時是空值,但是后續(xù)任何時候訪問都是有值的。例如,給該屬性附加 @property 裝飾器時,就可以在編輯器中編輯該字段,拖拖拉拉之后由 Creator 幫我們賦值該字段。
那我們?nèi)绾蜗?TypeScript 傳達這種信息呢?
我們來分析一下報錯的原因,material: Material 將 material 字段聲明為 Material 類型,這個意思就是在任何時候拿到 material 它都是 Material 類型。
初始化式 = null 告訴它將 material 初始化為 空值 null,與上條說法違背。
這便是,TypeScript 的嚴格類型檢查。它要求你將類型對上號。TypeScript 編輯器是默認開啟該選項的,Creator 也不例外。
既然知道了出錯原因,那么我們就可以有以下幾種思路去解決。
正確描述它的類型為:既可能是 Material,也可能是 null
向 TypeScript 類型系統(tǒng)表達:我只是初始化為 null,后續(xù)使用時候其實都是 Material
關(guān)閉 TypeScript 對空值的檢查。
可空類型
我們可以將 material 的類型描述為既可能是 Material,也可能是 null:
material: Material | null = null;這樣初始化為 null 就順理成章了。
然而,這樣的缺陷是當你后續(xù)訪問 material 時,TypeScript 要求你處理空值的情況。例如:
console.log(this.material.name);TypeScript 類型系統(tǒng)會提示你:this.material 可能是空值 null,無法訪問 null 的屬性。
但是,只要 TypeScript 在此處知道它一定是不為空的,它就會收起這條錯誤:
if (this.material) {console.log(this.material.name);}
因為你做了判斷,當運行到 if 語句塊內(nèi)的時候,this.material 一定是不為空的,因此可以訪問 name 屬性。
表達式非空斷言
每次使用的時候都要用 if 語句判斷一下實在有些繁瑣,況且 if 語句在運行時是會去執(zhí)行的。
如果你一定能確?!霸谶\行到這里的時候 this.material 一定非空”,那么我們可以用TypeScript 非空斷言語法來表達:
console.log(this.material!.name);感嘆號 ! 稱作 非空斷言操作符,它斷言前面的表達式是非空的。
注意此感嘆號是 TypeScript 特有的,僅為類型目的;編譯后,會直接移除。
我們還可在初始化時就作此斷言:
material: Material = null!; // 相當于將 `null` 強制轉(zhuǎn)換為了 `Material` 類型還一個經(jīng)常用于非空斷言的地方是 Node.getComponent(),此方法返回的是可能為空的組件。如果能確保組件一定存在,則可以通過非空斷言來避免 if 判斷。
顯式賦值斷言
在標準的 JavaScript 語法里,是可以不給初始化式的,這樣的字段將被初始化為 undefined:
class C {@property(Material)material; // undefined}
在 TypeScript 里也允許這么做,不過你得提示一下 TypeScript,以讓它跳過這里的類型檢查:
material!: Material;屬性名后的感嘆號稱為 顯式賦值斷言(Definite Assignment Assertion),它告訴 TypeScript 此字段在別處初始化。
OK,這里你將得到一個雖然聲明為 Material,但是初始化為 undefined 的字段。
禁用空值檢查
當然了,如果你是非常不喜歡 TypeScript 的類型系統(tǒng),你更喜歡所有事靠自己確保,那么你可以關(guān)閉嚴格類型檢測。在 <項目目錄>/tsconfig.json 里,加上選項:
"extends": "./temp/tsconfig.cocos.json","compilerOptions": {"strictNullChecks": false // 關(guān)閉它}
需要提醒的是,我們并不鼓勵這種做法,因為嚴格空值檢查能夠減少 JS 代碼運行時的一些低級報錯。放一張最近我們收到的用戶反饋做為旁證:

總結(jié)
以上就是此次跟大家介紹的全部內(nèi)容。感謝大家在論壇里將遇到的實際問題與我們反饋交流,才讓我們一路以來越來越好。
過去一年我們助力開發(fā)者創(chuàng)作出了許多精彩游戲,未來我們?nèi)詴?3.0 的基礎(chǔ)上大力深耕,突破引擎技術(shù)的天花板,普惠廣大開發(fā)者。大家可以放心給 3.0 一個機會,多多使用 3.0 用于實際項目制作。
后續(xù)本專欄將不定期更新,為大家?guī)?span style="color: rgb(0, 0, 0);font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, "Open Sans", "Helvetica Neue", sans-serif;font-size: 16px;text-align: left;">包括但不限于以下內(nèi)容:
《Cocos Creator 3.0 里如何玩轉(zhuǎn) npm 海量資源》by 放空
《深入Cocos Creator 3.0 的插件擴展系統(tǒng)》by sijie
《Cocos Creator 3.0 的 3D 物理講解》by Jayce Lai
《Cocos Creator 3.0 的資源系統(tǒng)》by Santy Wang
......
讓我們在此先隆重感謝各位作者,并以此道德綁架他們,希望他們盡快交稿哈哈哈。
如果大家有關(guān)于 3.0 其他想看的內(nèi)容或是使用上的問題,或是其他想說的,歡迎在評論處留言告訴我們。
我們將在本周五(3月5日)為評論區(qū)點贊最高的同學送出 Cocos 周邊大禮包,并隨機抽出 5 位童靴隨機送出周邊,希望能聽到更多大家的聲音,也歡迎大家將本文轉(zhuǎn)給有需要的同學。

最后,再次感謝大家一路陪我們到這里,一起去到更遠的地方吧
。
