Screeps 使用 TypeScript 進行靜態(tài)類型檢查

screeps 系列教程

前言

作為使用 javascript 作為基礎(chǔ)語言的游戲钠绍,screeps 自然支持使用 typescript(簡稱 ts)進行編程扎即。本文就將介紹一下在游戲中使用 ts 的優(yōu)缺點以及如何使用 ts 進行游戲鬓梅。如果你有 ts 的相關(guān)基礎(chǔ)的話對于理解本文會非常有幫助供置。

TypeScript 三分鐘簡介

首先簡單普及一下 typescript:ts 是基于 js 的拓展型語言,它和 js 最大的區(qū)別就是 加入了靜態(tài)類型檢查 (像 java 那樣)绽快。除此之外 ts 的語法和 js 幾乎完全一致芥丧。

在你完成代碼編寫后,可以使用 ts 編譯器將.ts文件編譯成原生的.js文件坊罢。是的续担,我們不能直接向 screeps 服務(wù)器提交你的 ts 代碼。這個階段 ts 編譯器只做了如下兩件事:

  • 根據(jù).ts文件中的靜態(tài)類型檢查代碼是否有問題活孩,檢查通過后 移除所有的靜態(tài)類型代碼物遇。
  • 將移除了靜態(tài)類型的代碼轉(zhuǎn)化成目標(biāo)版本(如es5)的原生js代碼。

可以看到憾儒,ts 并不會讓你的代碼變的更高級询兴,也不會讓你的代碼運行的更快。它做的只是增加了一個類型檢查階段起趾。讓大多數(shù) “運行時錯誤” 轉(zhuǎn)化成 “編譯時錯誤”蕉朵,從而提高代碼在運行時的穩(wěn)定性。

簡單介紹就到這里阳掐,你可以訪問 ts 官方文檔 來獲得更多內(nèi)容始衅。

ts 官方介紹

在 Screeps 中使用 TS 的優(yōu)缺點

這一小節(jié)我們來了解一下在游戲里使用 ts 會帶來什么影響,在動手將你的項目遷移至 ts 之前缭保,你需要好好考慮下面的內(nèi)容汛闸,然后再做出選擇。

優(yōu)點

  • 讓代碼運行的更穩(wěn)定:這個是 ts 的主要作用艺骂,有一些隱晦的問題(如空值或者類型轉(zhuǎn)換問題)不太容易發(fā)現(xiàn)诸老,但是會引起大問題。相信每個人都經(jīng)歷過一覺起來 creep 死完了的情況钳恕。而 ts 的靜態(tài)類型檢查可以輕松的發(fā)現(xiàn)這些問題并告訴你:嗨别伏,這代碼有問題蹄衷,如果你不解決就別想用它。
  • 更強的自動補全和代碼提示:實際上厘肮,我們在之前教程里使用的代碼補全功能就是來自于 ts 的聲明文件愧口,但是由于我們寫的還是 js 代碼,所以編輯器的類型補全并不完整类茂。你應(yīng)該遇到過把一段代碼抽象出來之后類型補全就消失了的問題耍属。通過引入 ts 本體,我們將徹底告別沒有類型補全的日子巩检。
  • 更加清晰的代碼邏輯:通過添加類型厚骗,可以讓你快速的理解某個變量究竟是代表著什么意思,從而提高代碼的可讀性兢哭。
  • 更低的重構(gòu)成本:重構(gòu)幾乎是每個玩家都會遇到的事情领舰,而由于很多代碼之間的依賴關(guān)系,你要重構(gòu)的代碼越底層迟螺,那么要修改的代碼就越多提揍,在使用 js 時,你需要費很大功夫把可疑的代碼讀一遍然后手動修改煮仇,哪怕你讀的再仔細劳跃,都有可能遺漏一些比較隱晦的問題從而降低代碼穩(wěn)定性。而使用 ts 之后浙垫,ts 編譯器會自動識別出你重構(gòu)后的類型接口變更刨仑,進而發(fā)現(xiàn)因代碼修改造成的問題。這樣我們只需要修改編譯器給出的報錯即可輕松完成重構(gòu)夹姥。

缺點

  • 更高的學(xué)習(xí)成本:是的杉武,學(xué)習(xí)新的 ts 語法!沒人覺得這是個有趣的事情吧辙售。當(dāng)然如果你曾經(jīng)學(xué)習(xí)過 java 之類帶有靜態(tài)類型的語言轻抱,ts 的語法對你來說不成問題。
  • 陷入編寫類型聲明的泥潭:實際上旦部,編寫 ts 代碼的大多數(shù)時間不是在寫游戲代碼祈搜,而是編寫描述這些代碼的類型聲明。并不是引入 ts 之后就可以白嫖上面的好處士八,你需要寫很多的類型聲明容燕,你的類型聲明寫的越完整(復(fù)雜),ts 的代碼檢查 / 提示能力就越強婚度。并且 ts 嚴格的檢查規(guī)則也會讓剛接觸的同學(xué)無從下手蘸秘。你可能會遇到無論怎么修改代碼,ts 的報錯都一直在的絕望情境。

更簡單的類型檢查

其實除了使用 ts 之外醋虏,還有一種成本極低的方式來實現(xiàn)對代碼的檢查寻咒,只需要安裝一些依賴和對 VSCode 進行一些配置,就可以通過代碼中的注釋完成類型檢查颈嚼。雖然效果沒有 ts 好毛秘,但是相對與需要進行侵入式修改的 ts 來說,這種方式的使用成本更低粘舟。如果你有興趣的話,可以通過下面文章來深入了解:

引入 TypeScript

ok佩研!現(xiàn)在讓我們開始升級我們的項目柑肴!請確保你已經(jīng)完成了 上篇教程 中的內(nèi)容,本篇內(nèi)容會在其基礎(chǔ)上進行升級旬薯。

和 rollup 類似晰骑,ts 也需要一個配置文件來了解我們的構(gòu)建需求:在項目根目錄中創(chuàng)建文件 tsconfig.json,并填入如下內(nèi)容:

{
    "compilerOptions": {
        "target": "es2017",
        "moduleResolution": "Node", 
        "outDir": "dist/",
        "baseUrl": "./",
        "sourceMap": true,
        "allowSyntheticDefaultImports": true,
        "paths": {
            "@/*": ["./src/*"]
        }
    },
    "exclude": [
        "node_modules"
    ],
    "include": [
        "src/**/*.ts"
    ]
}

來簡單介紹一下绊序,這個文件里包含如下三大塊:

  • compilerOptions:這個對象里包含了我們的 ts 編譯需求硕舆,絕大多數(shù)配置項都在這個對象里。
  • exclude:將哪些目錄排除出 ts 的類型檢查骤公,因為 node_modules 是我們的項目依賴目錄抚官,所以我們沒必要檢查這些內(nèi)容。
  • include:啟用類型檢查的目錄阶捆,我們的 ts 代碼都要包含進來凌节,不然會出現(xiàn)各種奇奇怪怪找不到類型的錯誤。

新建完后你可能會發(fā)現(xiàn)你的 tsconfig 紅了洒试,還給了你一個下面這樣的報錯倍奢,不用擔(dān)心,這是因為我們還沒有創(chuàng)建任何 ts 文件垒棋,稍后我們改造了 src 目錄后重啟 VSCode 即可解決這個問題卒煞。

tsconfig.json 報錯信息

配置文件創(chuàng)建好了之后我們就可以安裝 ts 的編譯器了,得益于我們的項目已經(jīng)使用了 rollup 進行代碼構(gòu)建叼架,我們可以非常輕松的完成這個步驟畔裕,首先在終端中執(zhí)行如下命令來安裝依賴:

npm install --save-dev typescript rollup-plugin-typescript2 

其中 typescript 就是實際的 ts 編譯器,而 rollup-plugin-typescript2 則是一個插件乖订,用于在 rollup 構(gòu)建流程中集成 ts 編譯柴钻。安裝完成后我們來升級構(gòu)建流程,打開 rollup.config.js垢粮,總共需要修改三個地方:

1贴届、在開頭引入安裝的插件:

// ...
import screeps from 'rollup-plugin-screeps'
import copy from 'rollup-plugin-copy'
import typescript from 'rollup-plugin-typescript2' // <== 新增這一行

2、將打包入口文件改為 main.ts

export default {
    input: 'src/main.ts', // <== 把這里的 main.js 改為 main.ts
    output: {
        file: 'dist/main.js', // <== 這里不用修改,因為我們的輸出還是 js 文件
        // ...
    },
    // ...
};

3毫蚓、在構(gòu)建流程中引入 ts 編譯:

export default {
    input: 'src/main.ts',
    // ...
    plugins: [
        // ...
        // 模塊化依賴
        commonjs(),
        // 編譯 ts
        typescript({ tsconfig: "./tsconfig.json" }), // <== 新增這一行占键,注意先后順序不要搞錯了
        // 執(zhí)行上傳或者復(fù)制
        pluginDeploy
    ]
};

至此,我們的配置已經(jīng)結(jié)束了元潘,其實配置還是挺簡單的畔乙,復(fù)雜的是接下來的工作:把整個項目中的 js 文件都改造為 ts 文件

將 js 項目改造為 ts 項目

首先翩概,找到我們的入口文件 src/main.js牲距,將其更名為 main.ts

更改入口文件后綴名

說起來你可以不信钥庇,我們的項目改造已經(jīng)可以告一段落了牍鞠,現(xiàn)在在終端執(zhí)行 npm run build 就可以看到我們的構(gòu)建已經(jīng)可以跑起來了:

項目已經(jīng)可以從 ts 構(gòu)建到 js

是的,項目改造是可以慢慢來的评姨,ts 項目中允許存在 js 文件难述,我們可以慢慢的將整個項目遷移到完全 ts。不過我還是推薦找個空閑時間一次性把所有的文件都改造為 ts 文件吐句。在正式開始改造之前胁后,我們先來了解一下 ts 中最重要的概念:類型

認識 ts 中的類型

在 js 中,每個變量都有類型嗦枢,例如如下代碼攀芯,在 vscode 中我們可以通過把鼠標(biāo)懸停在指定變量上的方式查看他的類型:

類型為 Room 的 room 變量

這個冒號后面的綠色名字就是這個對象的類型,它的意思是變量 room 的類型為 Room文虏,而明確了類型之后敲才,vscode 就可以通過我們之前安裝的聲明文件來進行代碼補全。然而择葡,隨著我們代碼的開發(fā)和文件拆分紧武,越來越多變量類型將變得模糊起來,例如如下代碼:

·./src/role.harvester.js

const harvester = {
    run: function (creep) {
        // harvester 執(zhí)行的邏輯
    }
}

export default harvester;

其中 harvester.run 函數(shù)接受一個參數(shù) creep敏储,雖然從名字上我們知道他應(yīng)該是個 creep阻星,但是實際上 這個參數(shù)的類型完全取決與調(diào)用它的代碼,哪怕我們給他傳入一個 room 對象已添,代碼依舊可以執(zhí)行妥箕,不過由于讓 room 對象執(zhí)行了 creep 的邏輯,這段代碼將會有很大概率報錯更舞。而我們的項目復(fù)雜之后畦幢,一個變量的實際類型將越來越難以確定。

我們可以通過鼠標(biāo)懸停的方式查看當(dāng)前這個 creep 參數(shù)的類型:

未知類型 any

any 也是一個類型缆蝉,它代表這個變量有可能是任何類型宇葱。而這就是導(dǎo)致 run 函數(shù)里的 creep 變量沒有類型補全的罪魁禍首:vscode 不知道這個變量的確切類型瘦真,就無法得知這個變量包含什么屬性。而我們引入 ts 以及接下來要對項目進行的改造黍瞧,就是要讓每個變量都有確切的類型诸尽,而不是模糊的 any

無法進行類型補全印颤,當(dāng)前存在的補全前綴 abc 代表這是從本文件中提取的文本關(guān)鍵字

ok您机,接下來我們把這個文件的后綴改造為 .ts./src/role.harvester.ts),改完后綴名我們再次懸停之后可以發(fā)現(xiàn)代碼補全仍然沒有出現(xiàn)年局。所以接下來我們要通過添加 ts 特有的類型聲明代碼來顯式聲明 creep 參數(shù)的類型:

通過在 creep 參數(shù)后添加 : Creep际看,這樣就代表這個參數(shù)的類型必須是 Creep,添加之后我們就可以發(fā)現(xiàn)類型補全列表中已經(jīng)出現(xiàn)了 creep 應(yīng)該存在的屬性矢否。

類型推導(dǎo)列表中的紫色小方塊代表這是一個方法仲闽,藍色長方體代表這是一個屬性,而方法名被劃掉代表這個方法處于依舊可用兴喂,但是馬上要被廢棄的不推薦狀態(tài)蔼囊。

并且如果我們調(diào)用這個方法時錯誤的傳入了不是 Creep 類型的變量焚志,ts 編譯器就會立刻報錯并打斷代碼構(gòu)建:

傳參錯誤衣迷,ts 拋出的異常
構(gòu)建錯誤,ts 拋出的異常

這也就是我們引入 ts 的意義所在:通過顯式指定變量類型酱酬,讓編譯器自動進行類型推導(dǎo)壶谒,并在推導(dǎo)出現(xiàn)矛盾時報錯,讓我們可以在代碼上線之前解決問題膳沽。

接下來汗菜,我們就來介紹一下在項目改造中可能會遇到的常見問題:

問題1:類型上不存在屬性 xxx

如下所示,這個問題常見于訪問 memory 上的屬性挑社,例如 creep.memory陨界、room.memoryspawn.memory 以及 Memory

出現(xiàn)這個問題的原因是:這些屬性都是我們自定義的痛阻,ts 編譯器并不知道它們的存在菌瘪,為了安全起見(不允許訪問未定義的屬性 ),ts 將認為這段代碼是有問題的阱当。解決方法如下:

新建 src/index.d.ts 文件俏扩,并填入如下內(nèi)容:

interface CreepMemory {
    /**
     * 該 creep 的角色
     */
    role: string
}

再回來就可以發(fā)現(xiàn)報錯消失了,并且還顯示了我們的注釋信息:

這里的 CreepMemory 是一個預(yù)設(shè)的類型弊添,代表了 creep 的內(nèi)存格式录淡。你可以通過按住 ctrl 并點擊代碼的形式查看對應(yīng)類型的聲明位置。

screeps 聲明文件

可以看到這是一個非常長的文件油坝,位于 node_modules/@types/screeps 中嫉戚,這個不就是我們在一開始安裝的 @types/screeps 依賴么刨裆?沒錯,這個幾千行的文件描述了 screeps 出現(xiàn)的所有 api彼水、屬性崔拥、以及常量。你完全可以把它當(dāng)作一個本地的交互式 screeps 文檔來查閱凤覆。

這個文件中出現(xiàn)的代碼和你之后要寫的類型聲明的語法完全一樣链瓦,當(dāng)你不知道什么寫的時候就可以來這個文件里學(xué)習(xí)一下。

說的有點遠盯桦,我們書接上文慈俯,ts 會默認根據(jù)變量的類型來這個文件里進行查找,而我們在 index.d.ts 里定義的 interface CreepMemory 其實在這個文件里有一個同名的定義拥峦。ts 編譯器在找到我們的聲明后會和已經(jīng)出現(xiàn)過的同名類型進行合并贴膘,所以我們就可以借助 @types/screeps 中已經(jīng)存在的聲明開發(fā)我們的項目。除此之外略号,我們在后面的開發(fā)中還會遇到如下幾個類型刑峡,這里簡單介紹一下:

類型名 介紹
Creep 游戲中 creep 的類型
Room 游戲中房間的類型
Memory 游戲中的全局內(nèi)存,除此之外還有 RoomMemory玄柠、CreepMemory
Game 游戲主對象類型
Structure 游戲中所有建筑的基礎(chǔ)類型突梦,在此基礎(chǔ)上派生出了具體的建筑類型,如:StructureSpawn羽利、StructureExtension... 所有建筑都是以 Structure 開頭的
Source 房間中我們采集的能量礦類型
Mineral 房間中的元素礦類型

問題2:找不到定義在游戲?qū)ο笊系姆椒?/h2>

如果你了解過 js 的原型拓展的話宫患,那么你可能在游戲?qū)ο罄?creep 或者 room 上已經(jīng)寫了不少自己的拓展方法了,但是切換到 ts 之后你會發(fā)現(xiàn)這些方法都沒法使用了这弧,如下:

找不到拓展的方法

這個問題的原因和上面一樣娃闲,也是因為我們自定義的方法 ts 編譯器并不知道,所以認為這是不安全操作匾浪,解決的方法也很簡單皇帮,直接在對應(yīng)的類型上定義即可:

interface Creep {
    /**
     * 打印 hello world
     */
    sayHello(): void
}

但是這種操作是有缺陷的,因為這個函數(shù)的定義(上面這一段 )和實現(xiàn)(函數(shù)拓展的實際代碼)并沒有強相關(guān)蛋辈。如果我們有這個定義属拾,但是函數(shù)沒有實現(xiàn)或者沒有掛載到原型上的話,ts 編譯器并不會報錯梯浪,但是在實際執(zhí)行時這個調(diào)用就會報錯說該函數(shù)未定義捌年。所以,如果拓展的入?yún)⒒蛘叻祷刂涤凶兓脑捁衣澹覀兙托枰謩有薷穆暶魑募锏暮瘮?shù)定義礼预,這樣才不會讓 ts 的類型推導(dǎo)出現(xiàn)問題。

實際上虏劲,我們進行的原型拓展是一種比較 hack 的操作托酸,ts 并不推薦褒颈,所以才會出現(xiàn)這種讓人不舒服的寫法。更優(yōu)雅的寫法是我們新建一個類励堡,并用這個類來承載我們的自定義屬性谷丸,如下:

/**
 * 自定義的 creep
 */
class MyCreep {
    readonly creep: Creep

    /**
     * 接受一個 creep 進行實例化
     */
    constructor(creep: Creep) {
        this.creep = creep
    }

    /**
     * 打印 hello world
     */
    public sayHello(): void {
        console.log('hello world', this.creep.name)
    }
}

// 實例化自定義 creep
const creep = new MyCreep(Game.creeps.creepA)

// 可以正常使用
creep.sayHello()

不過要想使用這種方式,你可能需要更加完善的基礎(chǔ)代碼建設(shè)來解決如下問題:

  • 更啰嗦的寫法:在每次訪問前都要 import MyCreep 并進行實例化
  • 更高的性能消耗:如果每次新建 creep 變量時都要實例化的話应结,必然會導(dǎo)致重復(fù)的實例化消耗刨疼,所以我們需要有一套邏輯來緩存 creep 以提供更加簡單的訪問方式并解決重復(fù)實例化的性能浪費。
  • 警惕過期變量:游戲?qū)ο罄?Game.creeps 是會 在每個 tick 開始時全部銷毀并進行重建鹅龄,由此來保證變量里的數(shù)據(jù)都是最新的揩慕,這個操作是游戲自動進行的,而我們實例化的 myCreep 并不會被銷毀重建扮休,也就是說迎卤,如果你開始對這些自定義 creep 進行緩存的話,那你的緩存就可能會存在過期數(shù)據(jù)玷坠,所以還需要另外一套邏輯來解決變量的過期問題蜗搔。

所以說,對于那些對游戲運行機制還不太熟悉的新手同學(xué)來說八堡,我依舊推薦你使用原型拓展并手動維護拓展的聲明樟凄,因為你花費在升級新框架的時間會多的多,并且由于對機制的不熟悉秕重,還有可能會導(dǎo)致很多奇奇怪怪的問題(沒錯不同,絕大多數(shù)新手遇到的無法解決的問題都是自己的“框架”導(dǎo)致的厉膀,而不是游戲存在 bug )溶耘。

不過如果你想借機嘗試一下也不是不可以,或許這就是你變強的契機呢服鹅。不過在此之前請做好代碼備份凳兵,給自己留下反悔的機會。

ok企软,講完了常見問題庐扫,我們來隨便聊一下 ts,希望下面的內(nèi)容會對你的開發(fā)有所啟發(fā):

ts 的類型推導(dǎo)

我們從剛才就在講仗哨,ts 會自動根據(jù)已有的類型聲明進行推導(dǎo)形庭,如果你的代碼是類型自洽的,那么 ts 就會認為檢查通過厌漂。但是這并不意味著你要給每個變量指定類型(這是新手最容易進入的誤區(qū)之一 )萨醒,例如如下代碼:

const creep: Creep = Game.creeps.creepA

如果我們把上述代碼中的 :Creep 去掉,你會發(fā)現(xiàn) creep 的類型依舊為 Creep

為什么呢苇倡?因為在我們下載的 @type/screeps 中已經(jīng)聲明了:Game.creeps 里的所有子屬性的類型都為 Creep富纸,所以就算我們不給 creep 變量指定類型囤踩,ts 也會自動的將其推導(dǎo)為 Creep 類型。這也是推薦的寫法:盡可能的讓變量的類型都由 ts 編譯器自行推導(dǎo)得出晓褪,如果你發(fā)現(xiàn)新建的變量類型為 any堵漱,那么你應(yīng)該去檢查是不是之前的類型聲明不夠完善導(dǎo)致賦值語句的右值類型為 any:

// ?? 不好的寫法
// getCreep 方法的返回值可能為 any,需要手動指定
const getCreep = function () { /** 代碼實現(xiàn) */ }
const creep: Creep = getCreep()

// ?? 好的寫法
// 顯式定義 getCreep 方法的返回值涣仿,讓函數(shù)調(diào)用結(jié)果更明確
const getCreep = function (): Creep { /** 代碼實現(xiàn) */ }
const creep = getCreep()

解耦類型聲明

我們剛才在寫自定義的類型聲明時都是寫在 index.d.ts 中的勤庐,但是當(dāng)我們的類型聲明越來越多時,這個文件就會變得非常的龐大好港。那么該如何進行解耦呢埃元?首先要明確一下 ts 的類型聲明查找規(guī)則:

  • 如果一個 ts 文件中沒有任何 export / import 語句的話,這個文件中類型聲明將被放在全局作用域(GlobalThis)里媚狰,如果有的話岛杀,那么類型聲明的作用域?qū)⒈幌拗圃诋?dāng)前文件里。
  • ts 會根據(jù) tsconfig.jsonincludeexclude 的定義找到所有范圍內(nèi)的類型聲明崭孤,并根據(jù)其作用域進行聲明合并聪黎。

可以發(fā)現(xiàn)荆陆,類型聲明的作用域和它的文件夾層級是沒有關(guān)系的,所以我們可以 在每個模塊(文件夾)下都新建一個 types.ts 文件,然后把這個模塊包含的聲明都寫在里邊捶枢,注意不要包含任何導(dǎo)出導(dǎo)出語句。

或者屋吨,你也可以 直接在代碼文件里進行類型聲明并導(dǎo)出针贬,并在需要這個類型的文件中像引入普通變量一樣進行導(dǎo)入,如下:

module.ts:定義類型和函數(shù)

export interface MyStorage {
    memory: Memory
}

export const setMyStorage = function(storage: MyStorage) {
    // ...
}

main.ts:導(dǎo)入并使用類型

import { setMyStorage, MyStorage } from './module';

const storage: MyStorage = {
    memory: Memory
}

setMyStorage(storage)

由于這種方式有著更嚴格的作用域限制赋兵,所以更推薦用這種方式進行開發(fā)笔咽。但是要注意如果一個文件里有導(dǎo)入導(dǎo)出的話,這個文件中 CreepMemory霹期、Room 之類的聲明就不會合并到 GlobalThis 上的 @types/screeps 里叶组,從而導(dǎo)致在使用時依舊找不到我們聲明的屬性,你需要進行如下修改才可以:

// 導(dǎo)入了一個模塊
import RoomWorkTaskController from './module/roomWorkTask'

// ?? 錯誤的寫法
// Room 接口的作用域在本文件內(nèi)历造,其他文件無法訪問該定義
interface Room {
    /** 工作任務(wù)模塊 */
    work: RoomWorkTaskController
}

// ?? 正確的寫法
// 包裹一層 global甩十,表明這個接口位于全局作用域
declare global {
    interface Room {
        /** 工作任務(wù)模塊 */
        work: RoomWorkTaskController
    }
}

ts 的嚴格模式

在本文開頭的配置里我們并沒有啟用嚴格模式,所以在有 js 文件存在時也不會報錯吭产。當(dāng)我們想要更加強大的類型檢查時(當(dāng)然也更加的嚴格)侣监,就可以通過在 tsconfig.jsoncompilerOptions 里添加 "strict": true 來啟動嚴格模式。

當(dāng)啟用了嚴格模式后臣淤,最主要的區(qū)別就是代碼里將不允許出現(xiàn)隱式 any 類型橄霉,你可以由此發(fā)現(xiàn)代碼里可能存在的隱患,并且嚴格模式還會啟用其他限制荒典,直接百度 ts 嚴格模式即可酪劫,這里不再贅述吞鸭。

需要注意的是,ts 嚴格模式是一把雙刃劍覆糟,在讓你代碼更加穩(wěn)定的情況下也會限制你使用一些奇淫巧計刻剥,并且需要你投入更多的精力在類型完善上,這對于新手來說不一定是好事滩字,所以我更推薦你根據(jù)自身水平和項目情況選擇是否開啟嚴格模式造虏。

路徑別名

當(dāng)我們的文件夾層級比較深了之后,可能會遇到如下問題:

// 獲取 src/utils.ts 中的函數(shù)
import { utilsFun } form '../../../../utils'

你可能需要很多 ../ 來訪問外層的模塊麦箍,這會降低代碼可讀性漓藕,并且在代碼層級發(fā)生變化時也會導(dǎo)致出現(xiàn)問題。為此挟裂,ts 內(nèi)建了路徑別名來解決這個問題享钞,還記得我們開頭配置的 tsconfig.json 么?你可以在其中找到 compilerOptions.paths 這個對象诀蓉。他就是路徑別名:

{
    "compilerOptions": {
        ...
        "paths": {
            "@/*": ["./src/*"]
        }
    },
    ...
}

很好理解栗竖,上面這個配置的含義就是 @/ 路徑就代表了 ./src/ 路徑,所以我們就可以把開頭那個導(dǎo)出改寫成如下形式:

import { utilsFun } form '@/utils'

這樣是不是就舒服多了渠啤,除此之外你也可以根據(jù)自己需要自行定義路徑別名狐肢。

結(jié)尾

至此,我們已經(jīng)將 ts 引入了項目沥曹,并了解了一些 ts 的常見概念與問題份名。不過作為 js 的主要方言,ts 的內(nèi)容還是非常復(fù)雜的妓美,本文也只是對 ts 和 screeps 的結(jié)合做了一些介紹僵腺,如果你之前沒有接觸過 ts 的話,還是更推薦將其 官方教程 大致讀一遍部脚,隨著對 ts 學(xué)習(xí)的不斷深入想邦,相信你可以對自己的項目有著更加強大的掌控力裤纹。

想要查看更多教程委刘?歡迎訪問 《Screeps 中文教程》!或者訪問 《Screeps 搭建開發(fā)環(huán)境 - 導(dǎo)言》 來繼續(xù)升級你的項目鹰椒!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锡移,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子漆际,更是在濱河造成了極大的恐慌淆珊,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奸汇,死亡現(xiàn)場離奇詭異施符,居然都是意外死亡往声,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門戳吝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浩销,“玉大人,你說我怎么就攤上這事听哭÷螅” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵陆盘,是天一觀的道長普筹。 經(jīng)常有香客問我,道長隘马,這世上最難降的妖魔是什么太防? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮酸员,結(jié)果婚禮上杏头,老公的妹妹穿的比我還像新娘。我一直安慰自己沸呐,他們只是感情好醇王,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著崭添,像睡著了一般寓娩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呼渣,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天棘伴,我揣著相機與錄音,去河邊找鬼屁置。 笑死焊夸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蓝角。 我是一名探鬼主播阱穗,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼使鹅!你這毒婦竟也來了揪阶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤患朱,失蹤者是張志新(化名)和其女友劉穎鲁僚,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡冰沙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年侨艾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拓挥。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蒋畜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出撞叽,到底是詐尸還是另有隱情姻成,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布愿棋,位于F島的核電站科展,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏糠雨。R本人自食惡果不足惜才睹,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望甘邀。 院中可真熱鬧琅攘,春花似錦、人聲如沸松邪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逗抑。三九已至剧辐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間邮府,已是汗流浹背荧关。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留褂傀,地道東北人忍啤。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像仙辟,于是被迫代替她去往敵國和親同波。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容