1. ホーム
  2. ジャバスクリプト

[解決済み】TypeScriptの外部モジュールで名前空間を使用するにはどうすればいいですか?

2022-04-01 10:15:43

質問

私はいくつかのコードを持っています。

baseTypes.ts

export namespace Living.Things {
  export class Animal {
    move() { /* ... */ }
  }
  export class Plant {
    photosynthesize() { /* ... */ }
  }
}

dog.ts

import b = require('./baseTypes');

export namespace Living.Things {
  // Error, can't find name 'Animal', ??
  export class Dog extends Animal {
    woof() { }
  }
}

tree.ts

// Error, can't use the same name twice, ??
import b = require('./baseTypes');
import b = require('./dogs');

namespace Living.Things {
  // Why do I have to write b.Living.Things.Plant instead of b.Plant??
  class Tree extends b.Living.Things.Plant {

  }
}

これは非常に分かりにくいですね。私は、たくさんの外部モジュールに同じ名前空間に型を提供させたいと思っています。 Living.Things . これはまったくうまくいかないようです。 Animaldogs.ts . 完全な名前空間名を書かなければなりません。 b.Living.Things.Planttree.ts . ファイルをまたいで同じ名前空間にある複数のオブジェクトを結合するのはうまくいきません。どうすればいいのでしょうか?

解決方法は?

キャンディーカップの例え

バージョン1:お菓子の数だけカップがある

例えば、こんなコードを書いたとしよう。

Mod1.ts

export namespace A {
    export class Twix { ... }
}

Mod2.ts

export namespace A {
    export class PeanutButterCup { ... }
}

Mod3.ts

export namespace A {
     export class KitKat { ... }
}

こんな設定がありましたね。

各モジュール(紙)には 専用カップ 名前 A . これは無駄です。 オーガナイズ お菓子と自分の間に新たなステップ(カップからお菓子を取り出す)が加わるだけです。


バージョン2:グローバルスコープに1つのカップ

もし、モジュールを使っていなかったら、次のようなコードを書くかもしれません。 export の宣言があります)。

グローバル1.ts

namespace A {
    export class Twix { ... }
}

グローバル2.ts

namespace A {
    export class PeanutButterCup { ... }
}

グローバル3.ts

namespace A {
     export class KitKat { ... }
}

これは のコードは、マージされた名前空間を作成します。 A をグローバルスコープで表示します。

この設定は便利ですが、モジュールの場合には適用されません(モジュールはグローバルスコープを汚さないからです)。


バージョン3:カップレス化

元の例に戻ると、カップは A , A および A は、あなたのためにならない。その代わり、次のようにコードを書くことができます。

Mod1.ts

export class Twix { ... }

Mod2.ts

export class PeanutButterCup { ... }

Mod3.ts

export class KitKat { ... }

を使うと、このような絵になります。

ずっといい!

さて、もしあなたがまだ、自分のモジュールでどれだけ名前空間を使いたいか考えているのなら、続きを読んでください...。


これらはあなたが探している概念ではありません

そもそもなぜ名前空間が存在するのかという原点に立ち返り、その理由が外部モジュールに意味を持つかどうかを検証する必要があります。

組織 : 名前空間は、論理的に関連するオブジェクトや型をグループ化するのに便利です。例えば、C#では、すべてのコレクション型は System.Collections . このように、型を階層的な名前空間に整理することで、その型を利用するユーザーに対して、より良い「発見」の体験を提供することができるのです。

名前の衝突 : 名前空間は、名前の衝突を避けるために重要です。たとえば My.Application.Customer.AddFormMy.Application.Order.AddForm -- 同じ名前だが名前空間が異なる2つの型。すべての識別子が同じルートスコープに存在し、すべてのアセンブリがすべての型を読み込む言語では、すべてが名前空間内にあることが重要です。

そのような理由は、外部モジュールでも意味を持つのでしょうか?

組織 : 外部モジュールは、必然的にファイルシステムに既に存在している。パスとファイル名で解決しなければならないので、論理的な組織化スキームがある。私たちは /collections/generic/ フォルダに list モジュールが含まれています。

名前の衝突 : 外部モジュールでは全く適用されません。 モジュールで、同じ名前のオブジェクトを2つ持つもっともな理由はありません。消費側からは 消費者 は、そのモジュールを参照するために使用する名前を選ぶことができるので、偶発的な名前の衝突はあり得ません。


これらの理由がモジュールの仕組みによって適切に対処されているとは思えないとしても、外部モジュールで名前空間を使おうという "解決策"は機能さえしません。

箱の中の箱の中の箱

ある物語

友人のボブから電話があり、「僕の家に素晴らしい整理術があるんだ」と言う。すてきですね、ボブが何を考え出したのか見に行きましょう。

まずキッチンから始めて、パントリーを開ける。60種類の箱があり、それぞれに「パントリー」と書かれています。あなたはランダムに箱を選び、それを開きます。中には「Grains」と書かれた箱が1つ入っています。Grains"の箱を開けると、Pasta"と書かれた箱が1つあります。Pastaの箱を開けると、Penneと書かれた箱が1つあります。この箱を開けると、期待通り、ペンネパスタが1袋入っていました。

少し混乱して、隣の箱を手に取ると、これまた「Pantry"」と書かれています。中には1つの箱があり、やはり「Grains"」と書かれています。Grains"の箱を開けると、またまた「Pasta"」と書かれた箱が1つ入っています。Pastaの箱を開けると、「Rigatoni」と書かれた箱が1つあります。この箱を開けると...リガトーニパスタが1袋入っていました。

とボブは言っています。

でも、ボブ......」とあなたは答えます。「あなたの構成法は役に立たない。何かを見つけるにはたくさんの箱を開けなければならないし、実際に何かを見つけるのに便利というわけでもない。 一つ ボックスではなく スリー . 実は、パントリーはすでに棚ごとに分類されているので、箱はまったく必要ないのです。パスタを棚にセットして、必要な時に手に取ればいいじゃないですか"。

パントリー」という名前空間にふさわしくないものを誰も入れないようにする必要があるんです。そして、私はすべてのパスタを安全に Pantry.Grains.Pasta という名前空間があるので、簡単に見つけることができます。

ボブはとても混乱した男です。

モジュールは自分たちの箱

皆さんも、同じようなことが現実に起こったことがあるのではないでしょうか。Amazonでいくつかの商品を注文すると、それぞれの商品が専用の箱に入って現れ、その中にさらに小さな箱があり、商品が独自の包装で包まれている。内箱が同じようなものであったとしても、配送はquot;combined"にはなりません。

箱の例えで行くと、重要な観察は以下の通りです。 外部モジュールはそれ自体が箱である . 多くの機能を持つ非常に複雑なアイテムかもしれませんが、与えられた外部モジュールはそれ自体が箱なのです。


外部モジュールに関するガイダンス

さて、「名前空間」を使う必要がないことがわかったところで、どのようにモジュールを整理すればいいのでしょうか?いくつかの指針や例を以下に示します。

できるだけトップレベルに近い形でエクスポートする

  • 単一のクラスや関数をエクスポートするだけなら export default :

MyClass.ts

export default class SomeType {
  constructor() { ... }
}

MyFunc.ts

function getThing() { return 'thing'; }
export default getThing;

消費量

import t from './MyClass';
import f from './MyFunc';
var x = new t();
console.log(f());

これは消費者にとって最適です。彼らはあなたのタイプに好きな名前をつけることができます ( t この場合、オブジェクトを見つけるために余計なドット打ちをする必要はありません。

  • 複数のオブジェクトをエクスポートする場合、それらをすべてトップレベルに配置します。

MyThings.ts

export class SomeType { ... }
export function someFunc() { ... }

消費量

import * as m from './MyThings';
var x = new m.SomeType();
var y = m.someFunc();

  • 大量のものをエクスポートする場合は、そのときだけ module / namespace というキーワードがあります。

MyLargeModule.ts

export namespace Animals {
  export class Dog { ... }
  export class Cat { ... }
}
export namespace Plants {
  export class Tree { ... }
}


消費量

import { Animals, Plants} from './MyLargeModule';
var x = new Animals.Dog();


レッドフラッグ

以下はすべて、モジュールの構造化に関するレッドフラグです。もし、これらのうちのどれかがあなたのファイルに当てはまるなら、外部モジュールの名前空間を使おうとしていないかどうか再確認してください。

  • トップレベルの宣言が export module Foo { ... } (削除 Foo を追加し、すべてを一段階上に移動させます。)
  • を1つ持つファイルは export class または export function でない場合は export default
  • 複数のファイルで、同じ export module Foo { をトップレベルにしています(これらがひとつにまとまると思ってはいけません)。 Foo !)