TypeScript 中的多種 import 解義
JavaScript 中有多種 export
的方式,而 TypeScript 中針對這種情況做了多種 import
語法,最常見的就是 import * as path from 'path'
這種。這篇文章主要來講解 TypeScript 中不同的 import 具有什麼意義。
原文首發於我的個人網站:聽說 - https://tasaid.com ,推薦在我的網站閱讀更多技術文章。
前端開發 QQ 群:377786580
從 export 說起
有很多朋友都問過我關於 TypeScript 中不同 import
的含義,最典型的就是下面的 import
語法:
import * as path from 'path'
不少人疑問這句程式碼究竟是什麼意思,這裡我們要先從 js 的 export
開始說。
首先,JavaScript 的模組化方案,在歷史的演進中,有多種匯出模組的方式: exports
、 module.exports
、 export
、 export default
。
在 nodejs 中內建的模組遵循的都是 CommonJS 規範,語法為 module.exports
和 exports
。
// 模組中的 exports 變數指向 module.exports // 這篇文章不會深入講解 module.exports 和 exports 的關係 module.exports = function () { } exports.site = 'https://tasaid.com' module.exports.name = 'linkFly'
例如 nodejs
內建的 events 模組的原始碼:
在 ECMAScript 6 中又新增了語法 export
和 export default
:
export default function () { } export const site = 'https://tasaid.com' export const name = 'linkFly'
到這裡畫風還比較正常,而大名鼎鼎的 JavaScript 轉碼編譯器 babel 針對 ECMAScript 6 新增的 export default
語法,搞了個 babel-plugin-transform-es2015-modules-commonjs 的轉換外掛,用於將 ECMAScript 6 轉碼為 CommonJs 規範的語法:
原始碼:
export default 42;
編譯後:
Object.defineProperty(exports, "__esModule", { value: true }); exports.default = 42;
到這裡,我們看到有三種 export
預設值的語法:
// commonjs module.exports = function () {} // babel 轉碼 exports.default = function () {} // es6 export default function () {}
TypeScript 中的 import
在 TypeScript 中,也有多種 import
的方式。
// commonjs 模組 import * as xx from 'xx' // es6 模組 import xx from 'xx' // commonjs 模組,型別宣告為 export = xx import xx = require('xx') // 沒有型別宣告,預設匯入 any 型別 const xx = require('xx')
在 tsconfig.json
中, allowSyntheticDefaultImports
會影響到 import 語法的型別檢查規則,這個下面再說。
import * as xx from 'xx'
import * as xx from 'xx'
的語法來一般都是用來匯入使用 module.exports
匯出的模組。
import * as path from 'path'
因為 nodejs 中的模組大部分都是通過 module.exports
、 exports.xx
語法進行匯出的。
import xx from 'xx'
預設情況下, import xx from 'xx'
的語法只適用於 ECMAScript 6 的 export default
匯出:
模組 foo:
export default function () { console.log('https://tasaid.com') }
ES6 模組的匯入:
import foo from './foo' foo()
而前面我們說了, babel
會將 es6 的模組的 export default
語法編譯為 exports.default
語法。
而 TypeScript 預設是不識別這種語法的,如果一個模組的匯出是 exports.default
匯出,如果使用 import xx from 'xx'
的語法匯入是會報錯的。
所以在 tsconfig.json
中,有個 allowSyntheticDefaultImports
選項,就是針對這種語法做相容。
如果設定 allowSyntheticDefaultImports
為 true
,則檢測匯入的模組是否是 ES6 模組,如果不是,則查詢模組中是否有 exports.default
匯出。
從而達到針對 exports.default
的相容。
效果參見這個動畫:
allowSyntheticDefaultImports
選項的,一般情況下我採取的方式是將 deafult 重新命名:
import { default as foo } from 'foo'
import xx = require('xx')
import xx = require('xx')
是用來匯入 commonjs 模組的庫,特殊的地方在於這個庫的型別宣告是 export = xx
這種方式匯出的:
foo.js 原始碼:
module.exports = () => { console.log('https://tasaid.com') }
foo.d.ts 型別宣告檔案原始碼:
declare function foo(): void; export = foo
bar.ts 引用:
import foo = require('./foo') foo()
我在 《[JavaScript 和 TypeScript 交叉口 —— 型別定義檔案(*.d.ts)
]( https://tasaid.com/blog/20171... 》中講述過 TypeScript 型別宣告檔案對匯入匯出的影響。
const xx = require('xx')
當一個模組沒有型別宣告檔案的時候,可以使用 commonjs 原始的 require()
方式來匯入模組,這樣會預設該模組為 any。
總結
最後我們整體總結下,在 TypeScript 中,有多種 import 的方式,分別對應了 JavaScript 中不同的 export。
// commonjs 模組 import * as xx from 'xx' // 標準 es6 模組 import xx from 'xx' // commonjs 模組,型別宣告為 export = xx import xx = require('xx') // 沒有型別宣告,預設匯入 any 型別 const xx = require('xx')
針對 babel
編譯出來的 exports.default
語法,ts 提供了 allowSyntheticDefaultImports
選項可以支援,只不過個人不太推薦。
個人建議將 default
重新命名。
import { default as foo } from 'foo'
關於 TypeScript 中型別宣告檔案(*.d.ts) 對 import 和 export 的影響,可以參考我之前寫的 《[JavaScript 和 TypeScript 交叉口 —— 型別定義檔案
]( https://tasaid.com/blog/20171... 》。
原文首發於我的個人網站:聽說 - https://tasaid.com ,推薦在我的網站閱讀更多技術文章。
前端開發 QQ 群:377786580