web-dev-qa-db-ja.com

TypeScript外部モジュールで名前空間を使用する方法

私はいくつかのコードがあります:

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に型を提供することを望みます。これはまったく機能しないようです - 私はdogs.tsAnimalを見ることができません。私はb.Living.Things.Plantに完全な名前空間名tree.tsを書く必要があります。ファイル間で同じ名前空間内の複数のオブジェクトを結合することは機能しません。どうやってこれをするの?

204
Ryan Cavanaugh

キャンディカップアナロジー

バージョン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 { ... }
}

この設定を作成しました: enter image description here

各モジュール(一枚の紙)は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 { ... }
}

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

enter image description here

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


バージョン3:行き詰まって

元の例に戻ると、カップAA、およびAはあなたに有利なことをしていません。代わりに、コードを次のように書くことができます。

Mod1.ts

export class Twix { ... }

Mod2.ts

export class PeanutButterCup { ... }

Mod3.ts

export class KitKat { ... }

このような写真を作成します。

enter image description here

ずっといい!

それでは、モジュールで名前空間を本当にどれだけ使いたいのかをまだ考えているのであれば、次にお読みください。


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

なぜ名前空間がそもそも存在するのかの原因に戻り、それらの理由が外部モジュールにとって意味があるかどうかを調べる必要があります。

組織:名前空間は論理的に関連したオブジェクトとタイプをグループ化するのに便利です。たとえば、C#では、すべてのコレクション型がSystem.Collectionsにあります。私たちの型を階層的な名前空間にまとめることによって、それらの型のユーザーに良い "発見"体験を提供します。

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

これらの理由は外部モジュールでは意味がありますか?

組織:外部モジュールはすでにファイルシステムに存在しています。パスとファイル名でそれらを解決する必要があるので、使用する論理的な体系化スキームがあります。 listモジュールを含む/collections/generic/フォルダーを作成できます。

Name Conflicts:これは外部モジュールには全く当てはまりません。 Withinモジュールで、同じ名前の2つのオブジェクトを持つことにもっともな理由はありません。消費側から見ると、与えられたモジュールの消費者はそれらがモジュールを参照するのに使う名前を選ぶようになるので、偶然の命名の衝突は不可能です。


モジュールがどのように機能するかによってこれらの理由が適切に対処されているとは思わないとしても、外部モジュールで名前空間を使用しようとする「解決策」はうまくいきません。

ボックス内のボックスボックス内のボックス

ストーリー:

あなたの友人ボブがあなたに電話をかけます。 「私の家には素晴らしい新しい組織体系があります」と彼は言います。きちんとして、ボブが何を考え出したか見に行きましょう。

あなたは台所から始めてパントリーを開けます。 60個の異なるボックスがあり、それぞれ「パントリー」というラベルが付いています。あなたは無作為に箱を選んでそれを開けます。内側は "Grains"というラベルの付いた単一のボックスです。あなたは "Grains"ボックスを開き、 "Pasta"というラベルの付いた単一のボックスを見つけます。 "Pasta"ボックスを開くと、 "Penne"というラベルの付いた単一のボックスが見つかります。あなたはこの箱を開けて、あなたが予想しているように、ペンネパスタの袋を見つけます。

やや混乱して、あなたは同じく「Pantry」と名付けられた隣の箱を拾います。内側は1つの箱で、ここでも "Grains"と表示されています。あなたは "Grains"ボックスを開くと、やはり "Pasta"というラベルの付いた単一のボックスが見つかります。あなたは "Pasta"ボックスを開いて一つのボックスを見つける、これは "Rigatoni"とラベルされている。あなたはこの箱を開けて...リガトーニパスタの袋を見つけます。

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

「しかし、ボブ…」あなたは答えます。 「あなたの組織計画は役に立たない。何かを手に入れるためにたくさんの箱を開かなければならない。そして実際には、_の代わりにone _ boxに入れるだけの場合よりも、何かを探すほうが便利ではない。three。実際、あなたのパントリーはすでに棚ごとに仕分けされているので、箱はまったく必要ありません。 「

「わかりません - 'Pantry'名前空間に属さないものを他のユーザーが置かないようにする必要があります。すべてのパスタを安全に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を削除し、すべてを「上」のレベルに移動)
  • export classではない単一のexport functionまたはexport defaultを持つファイル
  • トップレベルで同じexport module Foo {を持つ複数のファイル(これらが1つのFooにまとめられるとは思わないでください)
722
Ryan Cavanaugh

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

この文書を読んだ後で私にはわからないことの1つは、(マージされた)モジュール全体を単一のimportでインポートする方法です。

編集回転してこの回答を更新します。名前空間に対するいくつかのアプローチが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();

このエイリアスは絶対に嫌なので、しないでください。あなたは上記のアプローチで得意です。個人的には「バレル」が好きです。

52
Jefftopia

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

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をコンパクトで賢明な方法で消費者に公開します。

6
Albinofrenchy

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);
3
Mike Vitik

OP私はあなたと一緒です。繰り返しになりますが、300以上の賛成票がある回答には何も問題ありませんが、私の意見は次のとおりです。

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

  2. それで、最初のものが達成されるならば、我々は輸入輸入しなければなりません輸入輸入...ちょうど人、srsly、モデルファイル、.d.tsファイルのようなモデルファイルの各々で輸入してください、なぜそんなに多くがあります*そこにいる?それはただ単純できちんとしているべきです、そしてそれはそれです。インポートが必要なのはなぜですか。どうして? C#は理由で名前空間を得た。

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

とても悲しいことに、私の答えはこうです。nop、これらのインポートをすべて使用したり、ファイル名を識別子として使用したりしないと、「名前空間」を機能させることはできません。もう1つの選択肢は、これらの依存関係すべてをfilenameasidentifier.tsというボックスに入れて、

export namespace(or module) boxInBox {} .

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

2
NO... Bugs...

このテーマに関して私が見たいくつかの質問/コメントは、あたかもその人がNamespaceを使っているかのように私には聞こえます。 Ryan Cavanaughが彼のコメントの1つで述べたように、あなたは 'Wrapper'モジュールにいくつかのモジュールを再エクスポートさせることができます。

同じモジュール名/別名からすべてインポートしたい場合は、ラッパーモジュールと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 ...
    ]
}
2
Ryan Thomas

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

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

コードを整理する適切な方法は、ネームスペースの代わりに別々のディレクトリを使用することです。各クラスは、それぞれのネームスペースフォルダ内の独自のファイルにあります。 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);
0
NolePTR

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 {
    }
}
0