這篇僅為自己工作中在 js 和 ts 交叉鬥智鬥勇的實踐中留下的經驗,不一定完全、合理,僅供參考,有錯漏難免,有則評論區指出。 前置知識 - JavaScript 的各種模塊化情況 全局模塊,在 globalThis 上可以訪問,一般是 iife 庫程式 ES 模塊 CommonJS 模塊 前置知識 ...
目錄
- 前置知識 - JavaScript 的各種模塊化情況
- 前置知識2 - 讓你寫的 d.ts 在工程中生效
- 1. 全局模塊的定義
- 2. ES 模塊的定義
- 3. CommonJS 模塊定義
- 4. 聲明類型(TypeScript 中的 interface 或 type)和其它
這篇僅為自己工作中在 js 和 ts 交叉鬥智鬥勇的實踐中留下的經驗,不一定完全、合理,僅供參考,有錯漏難免,有則評論區指出。
前置知識 - JavaScript 的各種模塊化情況
-
全局模塊,在
globalThis
上可以訪問,一般是 iife 庫程式 -
ES 模塊
-
CommonJS 模塊
前置知識2 - 讓你寫的 d.ts 在工程中生效
-
確保當前工程目錄中使用的 TypeScript 是
node_modules
下的開發依賴,快捷命令Ctrl
+Shift
+P
,選擇 TypeScript 版本即可 -
在
tsconfig.json
中配置include
項,使得你寫的d.ts
文件在include
的路徑中即可
1. 全局模塊的定義
假設我有一個定義在 globalThis
上的庫,名叫 WebCC
,它很簡單:
window.WebCC = (function(){
const foo = () => {
console.log('foo')
}
const bar = 'bar'
const NAME = 'WebCC'
return {
foo,
bar,
NAME
}
})()
那麼,它應該使用 namespace
來定義:
declare namespace WebCC {
function foo(): void
const bar: string
const NAME: string
}
2. ES 模塊的定義
仍以上述 WebCC
這個名字為例,但是這次是 ES 模塊:
// webcc.js
export const bar = 'bar'
export const NAME = 'WebCC'
export const foo = () => {
console.log('foo')
}
那麼,它應該使用 module
來定義:
// webcc.d.ts
declare module 'webcc' {
export const bar: string
export const NAME: string
export const foo: () => void
}
module
關鍵字後面的模塊名即 import 時的模塊名:
import { foo } from 'webcc'
2.1. 預設導出
declare module 'webcc' {
const XXX: string
export default XXX
}
2.2. 導出類
declare module 'webcc' {
export class Foo {
/** 構造器 */
constructor()
/** 欄位成員,類型為函數 */
foo: () => void
/** 欄位成員,類型為 string */
NAME: string
/** 函數成員 */
bar(): void
/** 靜態欄位成員,類型為 number */
static VERSION: number
}
}
2.3. 註意事項
在模塊聲明的 d.ts
文件中,想引入其他模塊的定義,不能像模塊一樣使用 import
指令,而是要使用 import()
。例如,想在 parser.d.ts
中引入別人已經定義好的數據類型,來自 @types/foo
的 Foo
類型,那麼要寫成:
declare module 'my-parser' {
export parse(val: import('foo').Foo): string
}
這是因為一旦在代碼文件頂部寫了 import
就會被當作模塊文件,而不是類型聲明文件。這個特性來自 TS 2.9
版本。
3. CommonJS 模塊定義
CommonJS 的模塊聲明與 ES 模塊聲明大同小異,即 module.exports.foo
(或簡寫 exports.foo
)對應 export foo
,module.exports = foo
對應 export default foo
。
3.1. 挨個導出
module.exports = {
foo: function() {
console.log('foo')
},
bar: "bar"
}
類型聲明為:
declare module 'webcc' {
export const foo: () => void
export const bar: string
}
3.2. 預設導出
module.exports = class WebCC {
foo() {
console.log('foo')
}
}
類型聲明為:
declare module 'webcc' {
export default class WebCC {
foo(): void
}
}
4. 聲明類型(TypeScript 中的 interface 或 type)和其它
4.1. type 和 interface
滿足前置知識2 的前提下,在任意 d.ts
中書寫的 interface
、type
定義均可被整個項目使用:
declare type WebCCOptions = {
foo: string
bar: boolean
}
declare interface WebCCResponse {
foo: string
}
4.2. 全局變數(非 namespace)
全局變數也可以如法炮製:
declare const WebCC: {
foo: () => void
}
4.3. 補充功能
例如,想為原生數組補充一個新的函數成員 foo
,先在某些地方實現:
// somefile.js
Array.prototype.foo = function() {
console.log('foo')
}
這個時候需要補齊這個類型:
// somefile.d.ts
declare interface Array<T> {
foo(): void
}
有的讀者可能不知道為什麼 Array 是 interface,那是因為這個是官方的定義,我只是點了點 F12 ... 畢竟 interface 才能繼續補充定義,官方的 d.ts 更完善、強大,建議自學。