TypeScript を使う

Deno は JavaScript と TypeScript の両方をランタイムの第一級の言語としてサポートしています。 この意味するところは、モジュール名に拡張子(または正確なメディアタイプを与えるサーバー)を含む完全修飾名を必要とするということです。 加えて、Deno は「魔法のような」モジュール解決を行いません。インポートされるモジュールは(拡張子を含む)ファイルとして指定するか、完全修飾 URL を指定します。 TypeScript モジュールをたとえば以下のように直接インポートできます。

import { Response } from "https://deno.land/std@$STD_VERSION/http/server.ts";
import { queue } from "./collections.ts";

--no-check オプション

deno rundeno testdeno cachedeno bundle を使うとき、TypeScript の型チェックを無効にする --no-check フラグを指定できます。 このオプションを使うとプログラムが起動する時間を大幅に削減できます。 型チェックをエディタが行ってくれて起動時間をできるだけ高速にしたい(たとえばファイル監視を使ってプログラムを自動的に再起動するなどの)場面で大きく役立ちます。

--no-check は TypeScript の型チェックを行わないため、型のみのインポート/エクスポートを自動的に除去することができません。それをするには型情報が必要だからです。 この目的のために TypeScript は import type および export type の構文を提供しています。 別のファイルに型をエクスポートするには export type { AnInterface } from "./mod.ts"; と書きます。 型をインポートするには import type { AnInterface } from "./mod.ts"; と書きます。 import typeexport type をどこかで使っていることを確認するには TypeScript のコンパイラオプション isolatedModulestrue に設定し、importsNotUsedAsValueserror に設定します。 このオプションを設定した tsconfig.json の例は標準ライブラリの設定で見ることができます。 これらの設定は将来、デフォルトで有効化されます。すでに Deno 1.4 以上では --unstable にするとデフォルトで有効化されます。

--no-check にすると型情報がないため 型指向の const enum はサポートされません。また、--no-check はレガシーな import =export = の構文もサポートしません。

外部の型定義を使う

組み込みの TypeScript コンパイラは JavaScript モジュールに型を適用するために拡張子なしのモジュールと Node.js モジュール解決ロジックに依存しています。

このギャップを埋めるため、Deno は「魔法のような」モジュール解決手段に訴えることなく型定義ファイルを参照する三つのやり方をサポートしています。

コンパイラヒント

JavaScript モジュールをインポートしていてそのモジュールの型定義がどこにあるかを知っている場合には、型定義をインポート時に指定できます。 これはコンパイラヒントの形をとります。コンパイラヒントは Deno に .d.ts ファイルの場所とそれに紐付いてインポートされる JavaScript コードを教えます。 このヒントは @deno-types で、それが指定されると JavaScript モジュールのかわりにその値がコンパイラで使用されます。 たとえば、foo.js があってそのファイルの型が foo.d.ts だとわかっている場合、次のようなコードになります。

// @deno-types="./foo.d.ts"
import * as foo from "./foo.js";

コンパイラヒントの値はモジュールのインポートと同じ解決ロジックが適用されます。つまりファイルに拡張子が必要で、現在のモジュールからの相対パスになります。リモートの指定も可能です。

コンパイラヒントが影響を与えるのは次に書かれた inport 文(または export ... from 文)で、指定されたモジュールをコンパイル時に @deno-types の値に置き換えます。上の例では Deno コンパイラは ./foo.js でなく ./foo.d.ts を読み込みます。プログラムの実行時には Deno は ./foo.js を読み込みます。

JavaScript ファイル内のトリプルスラッシュ参照ディレクティブ

Deno で使いたいモジュールをホストしていて Deno に型定義の場所を教えたいとします。この場合、実際のコード内にトリプルスラッシュディレクティブを利用できます。たとえば JavaScript モジュールがあってそのファイルに付随する型定義ファイルの場所を Deno に教えたい場合、foo.js という名前の JavaScript モジュールを以下のように書くことができます。

/// <reference types="./foo.d.ts" />
export const foo = "foo";

Deno はこれを見て、コンパイラが foo.d.ts をそのファイルの型チェックに使います。とはいえ foo.js は実行時に読み込まれます。 ディレクティブの値はモジュールのインポートを解決するのと同じロジックで解決されます。つまりファイル名には拡張子が必要で、現在のファイルからの相対パスになります。リモートの指定も可能です。

X-TypeScript-Types カスタムヘッダー

Deno で使いたいモジュールをホストしていて Deno に型定義の場所を教えたい場合、カスタム HTTP ヘッダーを使うことができます。 X-TypeScript-Types というヘッダーが Deno にそのファイルの場所を教えてくれます。

ヘッダーは上で説明したトリプルスラッシュ参照と同じように動作します。JavaScript ファイルの中身自体を書き換える必要はなく、サーバーのレスポンスで型定義の場所を決定できます。

すべての型定義がサポートされているわけではありません。

Deno はコンパイラヒントによって指示された .d.ts ファイルを読み込みますが、なかにはサポートされていない機能を含む .d.ts ファイルもあります。 具体的には、ある .d.ts ファイルはモジュール解決ロジックを使って他のパッケージから型定義を読み込み、参照することが可能であることを期待しています。 たとえば、ある型参照ディレクティブは node を含み、./node_modules/@types/node/index.d.ts のようなパスに解決されることを期待しています。 これは相対パスでない「魔法のような」モジュール解決に依存しているため、Deno は解決できません。

どうして TypeScript ファイル内のトリプルスラッシュ型参照を使わないのでしょうか?

TypeScript コンパイラは、型参照ディレクティブを含むトリプルスラッシュディレクティブをサポートしています。 Deno がこれを使用すると、TypeScript コンパイラの振る舞いを妨害してしまいます。 Deno は JavaScript (と JSX)ファイルのディレクティブだけを探すようにしています。

TypeScript のコンパイラオプションをカスタマイズする

Deno のエコシステムでは、デフォルトでで strict であるべきという TypeScript の理想に従うためにすべての strict フラグが有効化されています。 けれども、カスタマイズをサポートする方法を提供するために tsconfig.json のような設定ファイルをプログラムの実行時に Deno に与えることができます。

この設定ファイルの場所は Deno に明示的に教える必要があります。アプリケーションの実行時に -c (または --config)オプションを渡します。

deno run -c tsconfig.json mod.ts

以下は Deno が現在許可している設定とそのデフォルト値です。

{
  "compilerOptions": {
    "allowJs": false,
    "allowUmdGlobalAccess": false,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "alwaysStrict": true,
    "assumeChangesOnlyAffectDirectDependencies": false,
    "checkJs": false,
    "disableSizeLimit": false,
    "generateCpuProfile": "profile.cpuprofile",
    "jsx": "react",
    "jsxFactory": "React.createElement",
    "lib": [],
    "noFallthroughCasesInSwitch": false,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitUseStrict": false,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "preserveConstEnums": false,
    "removeComments": false,
    "resolveJsonModule": true,
    "strict": true,
    "strictBindCallApply": true,
    "strictFunctionTypes": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "suppressExcessPropertyErrors": false,
    "suppressImplicitAnyIndexErrors": false,
    "useDefineForClassFields": false
  }
}

指定可能な値とユースケースに関するドキュメントは TypeScript ドキュメントを確認してください。

注意: 上で列挙されていないオプションは Deno がサポートしていないか、あるいは TypeScript ドキュメントの中で非推奨/実験的なものです。