這篇文章分享給大家的內容是關於TypeScript改造問題與解決方案,內容很詳細,接下來我們就來看看具體的內容,希望可以幫助到大家。
由於本次改造的專案為一個透過NPM進行發布的基礎服務包,因此本次採用TypeScript進行改造的目標是移除Babel全家桶,減小包體積,同時增加強類型約束從而避免今後開發時可能的問題。
這次改造使用的是TypeScript v2.9.2,採用Webpack v4.16.0進行打包編譯。開發工具使用的是VSCode,使用中文語言套件。預期目標是直接將TypeScript程式碼透過loader直接編譯為ES5的程式碼。
本文涉及的問題有部分是TypeScript配置和使用的問題,也有部分是VSCode本身配置相關問題。
在專案中,如果我們使用了webpack.alias,可能會提示找不到模組。
具體錯誤如下:
终端编译报错:TS2307: Cannot find module '_utils/index'. 编辑器报错:[ts]找不到模块“_utils/index”。
這是由於編輯器無法讀取對應的別名資訊所導致的。
此時我們需要檢查對應的模組是否存在。如果確認模組存在,且終端編譯編譯時不報錯,而只是編輯器報錯,則是因為編輯器無法讀取webpack配置,我們需要增加另外的配置。
解決方法:除了設定webpack.alias,還需要設定對應的tsconfig.json
,具體設定如下所示:
"compilerOptions": { "baseUrl": ".", "paths": { "_util/*": [ "src/core/utils/*" ] } }
註:如果配置了tsconfig.json
以後還是報錯的話,需要重新啟動下VSCode,猜測是由於VSCode只在專案載入時讀取相關設定資訊。在JavaScript專案中的jsconfig.json
同理。
在JavaScript中,我們常常會宣告一個空對象,然後再給這個屬性進行賦值。但是這個操作放在TypeScript中是會發生報錯的:
let a = {}; a.b = 1; // 终端编译报错:TS2339: Property 'b' does not exist on type '{}'. // 编辑器报错:[ts] 类型“{}”上不存在属性“b”。
這是因為TypeScript不允許增加沒有宣告的屬性。
因此,我們有兩個辦法來解決這個報錯:
在物件中增加屬性定義(建議)。具體方式為:let a = {b: void 0};。
這個方法能夠從根本解決目前問題,也能夠避免物件被隨意賦值的問題。
為a
物件增加any屬性(應急)。具體方式為:let a: any = {};
。這個方法能夠讓TypeScript類型在檢查時忽略這個對象,從而編譯通過不報錯。這個方法適用於大量舊程式碼改造的情況。
#與上一個情況類似,我們給一個物件中賦值一個不存在的屬性,會出現編輯器和編譯報錯:
window.a = 1; // 终端编译报错:TS2339: Property 'a' does not exist on type 'Window'. // 编辑器报错:[ts] 类型“Window”上不存在属性“a”。
這也是因為TypeScript不允許增加沒有宣告的屬性所導致的。
由於我們沒有辦法宣告windows屬性的值(或者說很困難),因此我們需要透過下面這一種方式來解決:
我們在windows使用時增加一個型別轉換,即(window as any).a = 1;
。這樣就能夠保證編輯器和編譯時不會出錯。不過此方法只建議用於舊專案改造,我們還是要盡量避免在window物件上面增加屬性,應該透過一個全域的資料管理器來進行資料存取。
在專案中,使用到了一些Object原型鏈上面的一些ES2015新增的方法,如Object.assign
和Object.values
等,此時編譯會失敗,同時VSCode會提示報錯:
终端编译报错:TS2339: Property 'assign' does not exist on type 'ObjectConstructor'. 编辑器报错:[ts] 类型“ObjectConstructor”上不存在属性“assign”。
這是由於我們在tsconfig.json
中指定的target
是ES5,而TypeScript並沒有相關的polyfill,因此我們無法使用ES2015新增的方法。
解決方法:可以使用lodash工具集中的相關方法,安裝時需要安裝lodash.assign
和@types/lodash.assign
。並且lodash.assign
是一個CMD規範的包,需要透過import _assign = require('lodash.assing');
方式引入。
將ES2015的程式碼改造成為TypeScript程式碼時,如果你使用了ES2015新增的Map類型,那麼在編輯器還是終端編譯中編譯時都會報錯:
终端编译报错:TS2693: 'Map' only refers to a type, but is being used as a value here. 编辑器报错报错:[ts] “Map”仅表示类型,但在此处却作为值使用。
這是因為TypeScript並沒有提供相關的資料型別,也沒有對應的polyfill。
因此,我們解決這個問題的想法有三種:
将tsconfig.json
配置中的target
属性改为es6
,即输出符合ES2015规范的代码。因为ES2015存在全局的Promise对象,因此编译和编辑器都不会报错。该方法优点为配置简单,无需改动代码,缺点为需要高级浏览器的支持或者Babel全家桶的支持。
舍弃Map类型,改用Object进行替代。这种改造比较费时费力,适用于工作量较小和不愿意引入其他文件的场景。
自行实现或者安装一个Map包。这种方法改造成本较小,缺点就是会引入额外的代码或者包,并且代码效率无法保证。例如ts-map
和typescript-map
,这两个包的查找效率都是o(n),低于原生类型的Map。因此推荐自己使用Object实现一个简单的Map,具体实现方式可以去网上找相关的Map原理分析与实践(大致原理为使用多个Object,存储不同类型元素时使用不同容器,避免类型转换问题)。
将ES2015的代码改造成为TypeScript代码时,如果你使用了ES2015的新增的Promise类型,那在编辑器还是终端编译编译时都会报错:
终端编译报错: TS2693: 'Promise' only refers to a type, but is being used as a value here. 编辑器报错:[ts] “Promise”仅表示类型,但在此处却作为值使用。
这是由于TypeScript并没有提供Promise数据类型,也没有对应的polyfill。
因此,我们解决这个问题的思路仍然有三种:
将tsconfig.json
配置文件配置中的target
属性改为es6
,即输出符合ES2015规范的代码。因为ES2015存在全局的Promise对象,因此编译和编辑器都不会报错。该方法优点为配置简单,无需改动代码,缺点为需要高级浏览器的支持或者Babel全家桶的支持。
引入一个Promise库,如bluebird等比较知名的Promise库。在安装bluebird时需要同时安装@types/bluebird声明文件。缺点就是引入的Promise库较大,而且如果你的库作为一个基础库时,可能会与其他的调用方的Promise库产生冲突。
在tsconfig.json
配置文件中增加lib。此方法的原理是让TypeScript编译时引用外部的Promise对象,因此在编译时不会报错。此方式优点是不会引入任何其他代码,但是缺点是一定要保证在引用此库的前提下,一定存在Promise对象。具体配置如下:
"compilerOptions": { "lib": ["es2015.promise"] }
将ES2015代码改造成TypeScript代码时,如果使用了setTimeout和setInterval函数时,可能会出现无法找到该函数的报错:
终端编译报错:TS2304: Cannot find name 'setTimeout'. 编辑器报错:[ts] 找不到名称“setTimeout”。
这是由于编辑器和编译时不知道当前代码运行环境导致的。
因此,我们解决这个问题的思路有两种:
在tsconfig.json
配置文件中增加lib。让TypeScript能够知道当前的代码容器。具体示例如下:
"compilerOptions": { "lib": ["dom"] }
安装@types/node
。该方法适用于node环境下或者采用webpack打包时可以引入node代码。该方法直接通过npm install @types/node
即可安装完成,解决报错问题。
在ES2015的代码中,我们可以通过@babel/plugin-proposal-export-default-from
插件来直接导出引入的文件,具体示例如下:
export Session from './session'; // 报错 export * from '_models/read-item'; // 不报错
而在TypeScript中,这种写法是会报错的:
终端编译报错:TS1128: Declaration or statement expected. 编辑器报错:[ts] 应为声明或语句。
这是由于两者的模块语法不一样导致的。
因此,我们解决这个问题只需要用下面这一种方法:
将上面的export from
的语法稍加调整来适配TypeScript语法。具体改造如下:
export {default as Session} from '_models/session'; //调整后不报错 export * from '_models/read-item';// 之前不报错不需要调整
在做项目TypeScript改造的过程中,遇到了不少大大小小的坑。很多问题在网上都没有解决方案或者没有说明白具体的解决步骤,因此希望通过这一篇文章来帮助大家在进行TypeScript迁移时避免在我踩过的坑上再浪费时间。
相关推荐:
以上是TypeScrip的重新改造問題以及解決問題的方案的詳細內容。更多資訊請關注PHP中文網其他相關文章!