TypeScript外部モジュールで名前空間を使用するにはどうすればよいですか?


233

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

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。これはまったく機能しないようです-では表示さAnimalれませんdogs.ts。私は、完全な名前空間名を記述する必要がb.Living.Things.Planttree.ts。ファイル全体で同じ名前空間にある複数のオブジェクトを結合することはできません。どうすればよいですか?

回答:


860

キャンディカップの類推

バージョン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宣言がないことに注意してください)。

global1.ts

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

global2.ts

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

global3.ts

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

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

ここに画像の説明を入力してください

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


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

元の例に行く、カップはAAAあなたに好意をやっていません。代わりに、コードを次のように書くことができます。

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の異なるボックスがあり、それぞれに「パントリー」というラベルが付いています。あなたはランダムに箱を選んでそれを開きます。内部には、「穀物」というラベルの付いた1つのボックスがあります。「穀物」ボックスを開くと、「パスタ」というラベルの付いた1つのボックスが見つかります。「パスタ」ボックスを開き、「ペンネ」というラベルの付いた1つのボックスを見つけます。あなたはこの箱を開けて、予想通り、ペンネパスタの袋を見つけます。

少し混乱して、「パントリー」というラベルが付いた隣接するボックスを選択します。中には「穀物」というラベルの付いた1つのボックスがあります。「Grains」ボックスを開き、再び「Pasta」というラベルの付いた1つのボックスを見つけます。「パスタ」ボックスを開いて1つのボックスを見つけます。このボックスには「リガトーニ」というラベルが付いています。あなたはこの箱を開けて…リガトーニのパスタの袋を見つけます。

"それは素晴らしい!" ボブは言います。「すべてが名前空間にあります!」

「でもボブ...」と答える。「あなたの組織スキームは役に立たない。何かに到達するためにはたくさんの箱を開ける必要があり、3つではなく1つの箱にすべてを入れただけの場合よりも、実際に何かを見つけるのは便利ではありません。実際、パントリーはすでに棚ごとに並べ替えられています。箱はまったく必要ありません。パスタを棚に置いて、必要なときにそれを受け取るのはどうですか?」

「理解できません。「パントリー」名前空間に属さないものを他の人が入れないようにする必要があります。すべてのパスタをPantry.Grains.Pasta名前空間に安全に整理して、簡単に見つけられるようにしました」

ボブはとても混乱している人です。

モジュールは独自のボックスです

おそらく、実際に似たようなことが起こったでしょう。Amazonでいくつか注文すると、それぞれのアイテムが専用のボックスに、小さなボックスが中にあり、独自のパッケージに包まれて現れます。内部の箱が似ていても、積荷は有効に「結合」されていません。

ボックスの類推で言えば、重要な観察は、外部モジュールは独自のボックスであるということです。多くの機能を備えた非常に複雑なアイテムかもしれませんが、特定の外部モジュールは独自のボックスです。


外部モジュールのガイダンス

「名前空間」を使用する必要がないことがわかったので、モジュールをどのように整理したらよいでしょうか。次に、いくつかの指針となる例と例を示します。

可能な限りトップレベルに近いエクスポート

  • 単一のクラスまたは関数のみをエクスポートする場合は、次を使用します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 {トップレベルで同じである複数のファイル(これらが1つに結合されるとは思わないでくださいFoo!)

80
これは答えではありません。外部モジュールの名前空間を必要としない、または必要としないという前提は誤りです。ファイルシステムは、あなたがすることができ、組織体系の一種ですがちょっとこれらの目的のために使用し、それはほとんど与えられたプロジェクトからn個のクラスや関数を使用するための持っている消費者のnインポート文のための素晴らしいようではありません。特に、実際のコードにダウンしているときは、命名規則を混乱させるためです。
Albinofrenchy 2015

12
いくら望んでも、それはまだ不可能です。
Ryan Cavanaugh、2015

26
わかりません。パスカルはもう書かれていません。ファイルシステムを使用して整理するのはいつからですか?
デビッド

9
ライブラリの利用者が関心のあるすべてのものをインポートおよび再エクスポートする「ラッパー」モジュールを用意することで、これを実現できます。しかし、やはり「名前空間」を使用すると、コードを使用する人に別のレベルの間接参照を強制する以外の値は提供されません。
Ryan Cavanaugh 2016年

13
素晴らしい記事、ありがとうございます。あなたはwww.typescriptlang.org/docs/handbook/namespaces.htmlからこれにリンクするべきだと思います。typescriptlang.orgのリンクを3〜4回読んだことがあるはずですが、C#開発者として、すべてを名前空間に入れたいと思っています。私はそうではないと言っているいくつかの提案を読んだことがありますが、理由はなく、これほど決定的な(そして十分に説明された)理由は何もありません。さらに、typescript docsでこのAFAIKについて言及しているものはありません
Adam Plocher 2017年

53

Ryanの答えに問題はありませんが、ES6名前空間を正しく使用しながら、ファイルごとに1クラスの構造を維持する方法を探してここに来た人は、Microsoftのこの役立つリソースを参照してください。

ドキュメントを読んだ後、私には不明確なことの1つは、(マージされた)モジュール全体を単一 のでインポートする方法importです。

Circlingを編集してこの回答を更新します。名前空間のいくつかのアプローチがTSに登場しました。

1つのファイル内のすべてのモジュールクラス。

export namespace Shapes {
    export class Triangle {}
    export class Square {}      
}

名前空間にファイルをインポートして再割り当て

import { Triangle as _Triangle } from './triangle';
import { Square as _Square } from './square';

export namespace Shapes {
  export const Triangle = _Triangle;
  export const Square = _Square;
}

バレル

// ./shapes/index.ts
export { Triangle } from './triangle';
export { Square } from './square';

// in importing file:
import * as Shapes from './shapes/index.ts';
// by node module convention, you can ignore '/index.ts':
import * as Shapes from './shapes';
let myTriangle = new Shapes.Triangle();

最後の考慮事項。各ファイルに名前空間を付けることができます

// triangle.ts
export namespace Shapes {
    export class Triangle {}
}

// square.ts
export namespace Shapes {
    export class Square {}
}

しかし、同じ名前空間から2つのクラスをインポートすると、TSは重複した識別子があると文句を言うでしょう。今回の唯一の解決策は、名前空間に別名を付けることです。

import { Shapes } from './square';
import { Shapes as _Shapes } from './triangle';

// ugh
let myTriangle = new _Shapes.Shapes.Triangle();

このエイリアシングは絶対に忌まわしいので、行わないでください。上記のアプローチを使用する方がよいでしょう。個人的には「バレル」が好きです。


6
「ES6名前空間」とは何ですか?
アルアンハダッド

@AluanHaddad es2015 +をインポートすると、インポートされるものはデフォルト、非構造化、または名前空間のいずれかになります。const fs = require('fs')fs名前空間です。import * as moment from 'moment'moment名前空間です。これはオントロジーであり、仕様ではありません。
Jefftopia 2018

私はそれを知っていますが、あなたの答えでそれを説明するのはうまくいくでしょう。ただし、ES6名前空間は実際にはものであり、requireES6名前空間が呼び出されないrequire可能性があるなど、呼び出し可能なプレーンオブジェクトを返すなど、いくつかの理由でこの例はそれらに適用されません。
Aluan Haddad 2018

1
インポートしたものが呼び出し可能かどうかにかかわらず、論理的に言えば名前空間として機能します。上記の私の答えには警告は重要ではないと思います。
Jefftopia

7

フォルダで整理してみてください:

baseTypes.ts

export class Animal {
    move() { /* ... */ }
}

export class Plant {
    photosynthesize() { /* ... */ }
}

dog.ts

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

export class Dog extends b.Animal {
    woof() { }
}   

tree.ts

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

class Tree extends b.Plant {
}

LivingThings.ts

import dog = require('./dog')
import tree = require('./tree')

export = {
    dog: dog,
    tree: tree
}

main.ts

import LivingThings = require('./LivingThings');
console.log(LivingThings.Tree)
console.log(LivingThings.Dog)

モジュール自体が名前空間に参加していることをモジュール自体が気にする必要はありませんが、これにより、プロジェクトに使用しているモジュールシステムのタイプにとらわれない、コンパクトで賢明な方法でAPIがコンシューマに公開されます。


8
LivingThings.dog.Dogは、ここにあるものです。
Corey Alix

「ツリー」をエクスポートしてから「ツリー」ではなく「ツリー」をインポートする場合は、大文字と小文字を区別することをお勧めします。
demisx 2017

1
また、tree.tsエクスポートされたメンバーがまったくない場合、どのようにインポートできますか?
demisx 2017

Man TSは確かに、1つのステートメントのようにimportrequire一緒にいくつかのばかげた古い構文を持っています。
アンディ

3

Albinofrenchy回答の小さな改善:

base.ts

export class Animal {
move() { /* ... */ }
}

export class Plant {
  photosynthesize() { /* ... */ }
}

dog.ts

import * as b from './base';

export class Dog extends b.Animal {
   woof() { }
} 

things.ts

import { Dog } from './dog'

namespace things {
  export const dog = Dog;
}

export = things;

main.ts

import * as things from './things';

console.log(things.dog);

2
これをありがとう!既存の回答への変更は、新しい回答として投稿しないでください:既存の回答へのコメントとして追加するか、(より良い)希望する回答への編集を提案することで提案する必要があると言いたかっただけです。改善する。
a3nm 2016

3

OP君と一緒だよ。繰り返しますが、300以上の投票でこの回答に問題はありませんが、私の意見は次のとおりです。

  1. クラスを居心地の良い暖かい独自のファイルに個別に配置することの何が問題になっていますか?これは物事がより良く見えるようになるということですか?(またはすべてのモデルの1000行のファイルのようなもの)

  2. それで、最初のものが達成される場合は、インポートインポートインポート... man、srsly、モデルファイル、.d.tsファイルなどの各モデルファイルにインポートする必要があります。そこにいる?シンプルで整然としているだけで十分です。なぜそこに輸入品が必要なのですか?どうして?C#には、理由のために名前空間がありました。

  3. そしてそれまでに、あなたは文字通り「filenames.ts」を識別子として使用しています。識別子として... 2017年に来て、私たちはまだそうしていますか?イマは火星に戻り、さらに1000年間寝ます。

残念なことに、私の答えは次のとおりです。いいえ、すべてのインポートを使用しないか、識別子としてファイル名を使用しないと、「名前空間」を機能させることができません(これは本当にばかげていると思います)。別のオプションは次のとおりです。それらすべての依存関係をfilenameasidentifier.tsというボックスに入れて、

export namespace(or module) boxInBox {} .

それらをラップして、クラスから参照を取得しようとしているだけのときに、同じ名前の他のクラスにアクセスしようとしないようにします。


3

この話題に関して私が見たいくつかの質問/コメントは、Namespace「モジュールのエイリアス」を意味するところを使用しているように聞こえます。Ryan Cavanaughがコメントの1つで述べたように、「ラッパー」モジュールで複数のモジュールを再エクスポートすることができます。

本当に同じモジュール名/エイリアスからすべてインポートしたい場合は、ラッパーモジュールをのパスマッピングと組み合わせますtsconfig.json

例:

./path/to/CompanyName.Products/Foo.ts

export class Foo {
    ...
}


./path/to/CompanyName.Products/Bar.ts

export class Bar {
    ...
}


./path/to/CompanyName.Products/index.ts

export { Foo } from './Foo';
export { Bar } from './Bar';



tsconfig.json

{
    "compilerOptions": {
        ...
        paths: {
            ...
            "CompanyName.Products": ["./path/to/CompanyName.Products/index"],
            ...
        }
        ...
    }
    ...
}



main.ts

import { Foo, Bar } from 'CompanyName.Products'

:出力.jsファイルのモジュール解決は、このhttps://github.com/tleunen/babel-plugin-module-resolverなどのように、何らかの方法で処理する必要があります。

.babelrcエイリアス解決を処理する例:

{
    "plugins": [
        [ "module-resolver", {
            "cwd": "babelrc",
            "alias": {
                "CompanyName.Products": "./path/to/typescript/build/output/CompanyName.Products/index.js"
            }
        }],
        ... other plugins ...
    ]
}

1

この名前空間モジュールを試してください

namespaceModuleFile.ts

export namespace Bookname{
export class Snows{
    name:any;
    constructor(bookname){
        console.log(bookname);
    }
}
export class Adventure{
    name:any;
    constructor(bookname){
        console.log(bookname);
    }
}
}





export namespace TreeList{
export class MangoTree{
    name:any;
    constructor(treeName){
        console.log(treeName);
    }
}
export class GuvavaTree{
    name:any;
    constructor(treeName){
        console.log(treeName);
    }
}
}

bookTreeCombine.ts

---コンパイルパート---

import {Bookname , TreeList} from './namespaceModule';
import b = require('./namespaceModule');
let BooknameLists = new Bookname.Adventure('Pirate treasure');
BooknameLists = new Bookname.Snows('ways to write a book'); 
const TreeLis = new TreeList.MangoTree('trees present in nature');
const TreeLists = new TreeList.GuvavaTree('trees are the celebraties');

0

dog.ts

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

export module Living.Things {
    // Error, can't find name 'Animal', ??
    // Solved: can find, if properly referenced; exporting modules is useless, anyhow
    export class Dog extends b.Living.Things.Animal {
        public woof(): void {
            return;
        }
    }
}

tree.ts

// Error, can't use the same name twice, ??
// Solved: cannot declare let or const variable twice in same scope either: just use a different name
import b = require('./baseTypes');
import d = require('./dog');

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

-1

コードを編成する適切な方法は、名前空間の代わりに別のディレクトリを使用することです。各クラスは、それぞれの名前空間フォルダー内の独自のファイルにあります。index.tsは各ファイルのみを再エクスポートします。実際のコードをindex.tsファイルに含めることはできません。このようにコードを編成すると、ナビゲートがはるかに簡単になり、ディレクトリ構造に基づいて自己文書化されます。

// index.ts
import * as greeter from './greeter';
import * as somethingElse from './somethingElse';

export {greeter, somethingElse};

// greeter/index.ts
export * from './greetings.js';
...

// greeter/greetings.ts
export const helloWorld = "Hello World";

次に、そのように使用します。

import { greeter } from 'your-package'; //Import it like normal, be it from an NPM module or from a directory.
// You can also use the following syntax, if you prefer:
import * as package from 'your-package';

console.log(greeter.helloWorld);

これは誤解を招くものであり、完全に正しくありません。それは名前空間が機能する方法ではありません。また、opsの質問には回答しません。
AndrewMcLagan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.