比較 JavaScript 與範例 DTS
常見 CommonJS 模式
使用 CommonJS 模式的模組使用 module.exports
來描述所匯出的值。例如,以下是匯出函式和數字常數的模組
js
const maxInterval = 12;function getArrayLength(arr) {return arr.length;}module.exports = {getArrayLength,maxInterval,};
這可以用以下 .d.ts
來描述
ts
export function getArrayLength(arr: any[]): number;export const maxInterval: 12;
TypeScript 遊樂場可以顯示 JavaScript 程式碼的 .d.ts
等效版本。你可以在此親自嘗試。
.d.ts
語法故意看起來像ES 模組語法。ES 模組在 2015 年由 TC39 批准為 ES2015 (ES6) 的一部分,雖然它已經透過轉譯器提供很長一段時間了,但是如果你有使用 ES 模組的 JavaScript 程式碼庫
js
export function getArrayLength(arr) {return arr.length;}
這將具有下列等效的 .d.ts
ts
export function getArrayLength(arr: any[]): number;
預設匯出
在 CommonJS 中,您可以將任何值匯出為預設匯出,例如這裡是一個正規表示式模組
js
module.exports = /hello( world)?/;
可以用下列 .d.ts 來描述
ts
declare const helloWorld: RegExp;export default helloWorld;
或一個數字
js
module.exports = 3.142;
ts
declare const pi: number;export default pi;
CommonJS 中的一種匯出樣式是匯出一個函式。由於函式也是一個物件,因此可以新增額外欄位,並包含在匯出中。
js
function getArrayLength(arr) {return arr.length;}getArrayLength.maxInterval = 12;module.exports = getArrayLength;
可以用下列來描述
ts
export default function getArrayLength(arr: any[]): number;export const maxInterval: 12;
請注意,在 .d.ts 檔案中使用 export default
需要 esModuleInterop: true
才能運作。如果您無法在專案中使用 esModuleInterop: true
,例如當您提交 PR 到 Definitely Typed 時,您必須改用 export=
語法。這個較舊的語法較難使用,但可以在所有地方運作。以下是使用 export=
編寫上述範例的方式
ts
declare function getArrayLength(arr: any[]): number;declare namespace getArrayLength {declare const maxInterval: 12;}export = getArrayLength;
請參閱 模組:函式,以了解其運作方式的詳細資訊,以及 模組參考 頁面。
處理大量使用 import
在現代使用代碼中,有許多方法可以匯入模組
ts
const fastify = require("fastify");const { fastify } = require("fastify");import fastify = require("fastify");import * as Fastify from "fastify";import { fastify, FastifyInstance } from "fastify";import fastify from "fastify";import fastify, { FastifyInstance } from "fastify";
涵蓋所有這些情況需要 JavaScript 代碼實際支援所有這些模式。為了支援這些模式中的許多模式,CommonJS 模組需要看起來像
js
class FastifyInstance {}function fastify() {return new FastifyInstance();}fastify.FastifyInstance = FastifyInstance;// Allows for { fastify }fastify.fastify = fastify;// Allows for strict ES Module supportfastify.default = fastify;// Sets the default exportmodule.exports = fastify;
模組中的類型
您可能想要提供不存在的 JavaScript 代碼類型
js
function getArrayMetadata(arr) {return {length: getArrayLength(arr),firstObject: arr[0],};}module.exports = {getArrayMetadata,};
這可以用以下方式描述
ts
export type ArrayMetadata = {length: number;firstObject: any | undefined;};export function getArrayMetadata(arr: any[]): ArrayMetadata;
此範例是 使用泛型 提供更豐富類型資訊的良好案例
ts
export type ArrayMetadata<ArrType> = {length: number;firstObject: ArrType | undefined;};export function getArrayMetadata<ArrType>(arr: ArrType[]): ArrayMetadata<ArrType>;
現在陣列的類型會傳播到 ArrayMetadata
類型。
然後,匯出的類型可以透過使用 TypeScript 代碼中的 import
或 import type
或 JSDoc 匯入 來重複使用模組的使用者。
模組代碼中的命名空間
嘗試說明 JavaScript 程式碼的執行時間關係可能會很棘手。當類似 ES 模組的語法無法提供足夠的工具來描述匯出時,您可以使用命名空間
。
例如,您可能有足夠複雜的類型來描述,您選擇在.d.ts
中對它們命名。
ts
// This represents the JavaScript class which would be available at runtimeexport class API {constructor(baseURL: string);getInfo(opts: API.InfoRequest): API.InfoResponse;}// This namespace is merged with the API class and allows for consumers, and this file// to have types which are nested away in their own sections.declare namespace API {export interface InfoRequest {id: string;}export interface InfoResponse {width: number;height: number;}}
若要了解命名空間在.d.ts
檔案中的運作方式,請閱讀.d.ts
深入探討。
選擇性全域使用
您可以使用export as namespace
宣告模組在 UMD 背景中會在全域範圍內提供。
ts
export as namespace moduleName;
參考範例
為了讓您了解所有這些部分如何結合在一起,以下是製作新模組時可以參考的 .d.ts
ts
// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]// Project: [~THE PROJECT NAME~]// Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>/*~ This is the module template file. You should rename it to index.d.ts*~ and place it in a folder with the same name as the module.*~ For example, if you were writing a file for "super-greeter", this*~ file should be 'super-greeter/index.d.ts'*//*~ If this module is a UMD module that exposes a global variable 'myLib' when*~ loaded outside a module loader environment, declare that global here.*~ Otherwise, delete this declaration.*/export as namespace myLib;/*~ If this module exports functions, declare them like so.*/export function myFunction(a: string): string;export function myOtherFunction(a: number): number;/*~ You can declare types that are available via importing the module */export interface SomeType {name: string;length: number;extras?: string[];}/*~ You can declare properties of the module using const, let, or var */export const myField: number;
函式庫檔案配置
宣告檔案的配置應反映函式庫的配置。
函式庫可以包含多個模組,例如
myLib +---- index.js +---- foo.js +---- bar +---- index.js +---- baz.js
可以匯入為
js
var a = require("myLib");var b = require("myLib/foo");var c = require("myLib/bar");var d = require("myLib/bar/baz");
因此,您的宣告檔案應為
@types/myLib +---- index.d.ts +---- foo.d.ts +---- bar +---- index.d.ts +---- baz.d.ts
測試您的類型
如果您計畫將這些變更提交至 DefinitelyTyped 以供所有人使用,我們建議您
- 在
node_modules/@types/[libname]
中建立一個新資料夾- 在該資料夾中建立一個
index.d.ts
,並複製範例中的內容- 查看您使用模組時中斷的地方,並開始填寫 index.d.ts
- 當您滿意時,複製 DefinitelyTyped/DefinitelyTyped 並遵循自述檔案中的說明。
否則
- 在來源樹的根目錄中建立一個新檔案:
[libname].d.ts
- 新增
declare module "[libname]" { }
- 在宣告模組的大括號內新增範本,並查看使用中發生中斷的地方