TypeScrip的重新改造問題以及解決問題的方案

不言
發布: 2018-07-25 10:08:47
原創
2713 人瀏覽過

這篇文章分享給大家的內容是關於TypeScript改造問題與解決方案,內容很詳細,接下來我們就來看看具體的內容,希望可以幫助到大家。

概述

由於本次改造的專案為一個透過NPM進行發布的基礎服務包,因此本次採用TypeScript進行改造的目標是移除Babel全家桶,減小包體積,同時增加強類型約束從而避免今後開發時可能的問題。

這次改造使用的是TypeScript v2.9.2,採用Webpack v4.16.0進行打包編譯。開發工具使用的是VSCode,使用中文語言套件。預期目標是直接將TypeScript程式碼透過loader直接編譯為ES5的程式碼。

本文涉及的問題有部分是TypeScript配置和使用的問題,也有部分是VSCode本身配置相關問題。

改造問題記錄與分析

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同理。

TypeScript相關

物件屬性賦值報錯

在JavaScript中,我們常常會宣告一個空對象,然後再給這個屬性進行賦值。但是這個操作放在TypeScript中是會發生報錯的:

let a = {}; a.b = 1; // 终端编译报错:TS2339: Property 'b' does not exist on type '{}'. // 编辑器报错:[ts] 类型“{}”上不存在属性“b”。
登入後複製

這是因為TypeScript不允許增加沒有宣告的屬性。

因此,我們有兩個辦法來解決這個報錯:

  1. 在物件中增加屬性定義(建議)。具體方式為:let a = {b: void 0};。這個方法能夠從根本解決目前問題,也能夠避免物件被隨意賦值的問題。

  2. a物件增加any屬性(應急)。具體方式為:let a: any = {};。這個方法能夠讓TypeScript類型在檢查時忽略這個對象,從而編譯通過不報錯。這個方法適用於大量舊程式碼改造的情況。

Window物件屬性賦值報錯

#與上一個情況類似,我們給一個物件中賦值一個不存在的屬性,會出現編輯器和編譯報錯:

window.a = 1; // 终端编译报错:TS2339: Property 'a' does not exist on type 'Window'. // 编辑器报错:[ts] 类型“Window”上不存在属性“a”。
登入後複製

這也是因為TypeScript不允許增加沒有宣告的屬性所導致的。

由於我們沒有辦法宣告windows屬性的值(或者說很困難),因此我們需要透過下面這一種方式來解決:

  1. 我們在windows使用時增加一個型別轉換,即(window as any).a = 1;。這樣就能夠保證編輯器和編譯時不會出錯。不過此方法只建議用於舊專案改造,我們還是要盡量避免在window物件上面增加屬性,應該透過一個全域的資料管理器來進行資料存取。

ES2015 Object新增的原型鏈上的方法封包錯誤

在專案中,使用到了一些Object原型鏈上面的一些ES2015新增的方法,如Object.assignObject.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新增的資料結構Map初始化錯誤

將ES2015的程式碼改造成為TypeScript程式碼時,如果你使用了ES2015新增的Map類型,那麼在編輯器還是終端編譯中編譯時都會報錯:

终端编译报错:TS2693: 'Map' only refers to a type, but is being used as a value here. 编辑器报错报错:[ts] “Map”仅表示类型,但在此处却作为值使用。
登入後複製

這是因為TypeScript並沒有提供相關的資料型別,也沒有對應的polyfill。

因此,我們解決這個問題的想法有三種:

  1. tsconfig.json配置中的target属性改为es6,即输出符合ES2015规范的代码。因为ES2015存在全局的Promise对象,因此编译和编辑器都不会报错。该方法优点为配置简单,无需改动代码,缺点为需要高级浏览器的支持或者Babel全家桶的支持。

  2. 舍弃Map类型,改用Object进行替代。这种改造比较费时费力,适用于工作量较小和不愿意引入其他文件的场景。

  3. 自行实现或者安装一个Map包。这种方法改造成本较小,缺点就是会引入额外的代码或者包,并且代码效率无法保证。例如ts-maptypescript-map,这两个包的查找效率都是o(n),低于原生类型的Map。因此推荐自己使用Object实现一个简单的Map,具体实现方式可以去网上找相关的Map原理分析与实践(大致原理为使用多个Object,存储不同类型元素时使用不同容器,避免类型转换问题)。

ES2015新增的Promise使用报错

将ES2015的代码改造成为TypeScript代码时,如果你使用了ES2015的新增的Promise类型,那在编辑器还是终端编译编译时都会报错:

终端编译报错: TS2693: 'Promise' only refers to a type, but is being used as a value here. 编辑器报错:[ts] “Promise”仅表示类型,但在此处却作为值使用。
登入後複製

这是由于TypeScript并没有提供Promise数据类型,也没有对应的polyfill。

因此,我们解决这个问题的思路仍然有三种:

  1. tsconfig.json配置文件配置中的target属性改为es6,即输出符合ES2015规范的代码。因为ES2015存在全局的Promise对象,因此编译和编辑器都不会报错。该方法优点为配置简单,无需改动代码,缺点为需要高级浏览器的支持或者Babel全家桶的支持。

  2. 引入一个Promise库,如bluebird等比较知名的Promise库。在安装bluebird时需要同时安装@types/bluebird声明文件。缺点就是引入的Promise库较大,而且如果你的库作为一个基础库时,可能会与其他的调用方的Promise库产生冲突。

  3. tsconfig.json配置文件中增加lib。此方法的原理是让TypeScript编译时引用外部的Promise对象,因此在编译时不会报错。此方式优点是不会引入任何其他代码,但是缺点是一定要保证在引用此库的前提下,一定存在Promise对象。具体配置如下:

    "compilerOptions": { "lib": ["es2015.promise"] }
    登入後複製

SetTimeout使用报错

将ES2015代码改造成TypeScript代码时,如果使用了setTimeout和setInterval函数时,可能会出现无法找到该函数的报错:

终端编译报错:TS2304: Cannot find name 'setTimeout'. 编辑器报错:[ts] 找不到名称“setTimeout”。
登入後複製

这是由于编辑器和编译时不知道当前代码运行环境导致的。

因此,我们解决这个问题的思路有两种:

  1. tsconfig.json配置文件中增加lib。让TypeScript能够知道当前的代码容器。具体示例如下:

    "compilerOptions": { "lib": ["dom"] }
    登入後複製
  2. 安装@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] 应为声明或语句。
登入後複製

这是由于两者的模块语法不一样导致的。

因此,我们解决这个问题只需要用下面这一种方法:

  1. 将上面的export from的语法稍加调整来适配TypeScript语法。具体改造如下:

    export {default as Session} from '_models/session'; //调整后不报错 export * from '_models/read-item';// 之前不报错不需要调整
    登入後複製

总结

在做项目TypeScript改造的过程中,遇到了不少大大小小的坑。很多问题在网上都没有解决方案或者没有说明白具体的解决步骤,因此希望通过这一篇文章来帮助大家在进行TypeScript迁移时避免在我踩过的坑上再浪费时间。

相关推荐:

关于TypeScript在node项目中的实践分析

以上是TypeScrip的重新改造問題以及解決問題的方案的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章