JavaScript と .d.ts の例を比較する
一般的な 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 でのエクスポートの1つのスタイルは、関数をエクスポートすることです。関数はオブジェクトでもあるため、追加のフィールドを追加してエクスポートに含めることができます。
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` をプロジェクトで使用できない場合(Definitely Typed に PR を送信する場合など)、代わりに `export=` 構文を使用する必要があります。この古い構文は使いにくいですが、どこでも動作します。上記の例を `export=` を使用して記述する方法は次のとおりです。
ts
declare function getArrayLength(arr: any[]): number;declare namespace getArrayLength {declare const maxInterval: 12;}export = getArrayLength;
モジュール: 関数 でその動作の詳細、および モジュールリファレンス ページを参照してください。
多くのインポートを処理する
最新の利用コードでモジュールをインポートする方法はたくさんあります。
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モジュールのような構文がエクスポートを記述するための十分なツールを提供しない場合は、namespaces
を使用できます。
たとえば、記述する必要がある型が非常に複雑で、.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をクローンして、READMEの手順に従ってください。
それ以外の場合は
- ソースツリーのルートに新しいファイル
[libname].d.ts
を作成します。declare module "[libname]" { }
を追加します。declare module
の中括弧の中にテンプレートを追加し、使用箇所がどこで壊れているかを確認します。