6時だョ!!全員集合!!

Rails・JavaScrictを中心にアウトプットします。

2022年4月11日 りあクト! 第4章 モジュールと型定義 TypeScript のインポート/エクスポート (p.207~)

TypeScriptのimport/export

JavaScriptとほぼ同じで異なる点は2つあります。

  • TypeScriptのimportは拡張子を書くとエラーになる
  • 同名の型を扱った場合の挙動

importで拡張子を書くとエラーになる理由

モジュールを解決する際に独自のルールで探索するからです。

独自の探索の7パターン

  1. src/bar.ts
  2. src/bar.tsx
  3. src/bar.d.ts
  4. src/bar/package.json の types または typings プロパティで設定されている型定義ファイル
  5. src/bar.index.ts
  6. src/bar.index.tsx
  7. src/bar/index.d.ts

上から探索していって最初に見つかったものが読み込まれ、最後までヒットしなかった場合はその時点でエラーになります。

TypeScriptでのimportは拡張子はつけない!と覚えておきましょう。

.d.ts の型定義ファイルの説明は後に説明があるようです。

同名の型を扱った場合の挙動

TypeScriptでは、インターフェースや型エイリアスもimportと exportの対象になります。 以下のコードは、type, interface, 関数をそれぞれエクスポート出来ることを示唆しています。

type Species = 'rabbit' | 'bear' | 'fox' | 'dog';

interface Resident {
    name: string;
    age: number;
    species: Species;
}
const isCanine = (resident: Resident): boolean =>
  ['dog', 'fox'].includes(resident.species);

export { Species, Resident, isCanine };

りあクト! 【Ⅰ. 言語・環境編】p.209の文章を引用します。

TypeScriptでは、同じ名前空間の中に『変数宣言空間(Variable Declaration Space)』と『型宣言空間(Type Declaration Space)』という 2 つの宣言空間が存在していて、名前の管理が別々になっている

上記から変数宣言と型宣言は同名の変数と型は共存出来ることがわかります。 export、importをした場合、以下のようになります。

module/currency-export.ts
const rate: { [unit: string]: number } = {
    USD: 1,
    EUR: 0.9,
    JPY: 108,
    GBP: 0.8,
};

type Unit = keyof typeof rate;
type Currency = {
    unit: Unit;
    amount: number;
};

const Currency = {
    exchange: (currency: Currency, unit: Unit): Currency => {
        const amount = currency.amount / rate[currency.unit] * rate[unit];

        return { unit, amount };
    },
};

export { Currency };
module/currency-import.ts
import { Currency } from './currency-export';

const dollars: Currency = {
    unit: 'USD',
    amount: 100,
};

console.log(dollars);
console.log(Currency.exchange(dollars, 'JPY'));
ターミナル
cd 04-typescript/05-advanced/module/
ts-node currency-import.ts
{ unit: "USD", amount: 100 }
{ unit: "JPY", amount: 10800 }
  • 一つのimport文で型エイリアスとオブジェクトのCurrency が使えています。
  • 同じ名前空間の中に変数宣言空間と型宣言空間が存在しているので同じ名前が持たせられています。
  • コンビネーションと呼びます。

型のみのimportとexport

import/export時にtype をつけると型のみで行えます。

type Species = 'rabbit' | 'bear' | 'fox' | 'dog';
class Resident {
  name = '';
  age=0;
  species: Species | null = null;
}
export type { Species, Resident };
  • クラスは宣言時にインスタンスのインターフェース型とコンストラクタ関数の宣言を同じ名前で同時に行っています。
  • この場合、Residentクラスは型のみをエクスポートしています。
  • import先ではクラスの機能を使うことが出来ないため、new演算子を使うとコンパイルエラーになります。
import type { Foo } from 'bar'
  • Fooはクラスです。
  • コンストラクタはimportされないのでnewは使えません。
  • クラスをインターフェースとして扱う場合の型がimportされます。

内部の挙動として型のみのimport/exportはTypeScriptの中だけで完結しコンパイル後のJavaScriptに残らないので、パフォーマンスで多少有利な可能性があるようです。 コードを読み取りやすくする目的でも使用される事もあると書籍で書かれていました。

参考

りあクト! 【Ⅰ. 言語・環境編】 p.207