Jak używać przestrzeni nazw z zewnętrznymi modułami TypeScript?

Mam jakiś kod:

Basetypy.ts

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

Pies.ts

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

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

Drzewo.ts

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

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

  }
}
To wszystko jest bardzo mylące. Chcę mieć kilka zewnętrznych modułów wszystkich typów contribute do tej samej przestrzeni nazw, Living.Things. Wygląda na to, że to w ogóle nie działa ... nie widzę Animal w dogs.ts. Muszę wpisać pełną nazwę przestrzeni nazw b.Living.Things.Plant w tree.ts. Nie działa łączenie wielu obiektów w tej samej przestrzeni nazw w całym pliku. How do I do to?
Author: Ryan Cavanaugh, 2015-05-20

8 answers

Candy Cup

Wersja 1: kubek na każdy cukierek

Powiedzmy, że napisałeś taki kod:

Mod1.ts

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

Mod2.ts

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

Mod3.ts

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

Stworzyłeś tę konfigurację: Tutaj wpisz opis obrazka

Każdy moduł (arkusz papieru) otrzymuje swój własny kubek o nazwie A. To jest bezużyteczne - nie jesteś faktycznie organizowanie swoje cukierki tutaj, tylko dodanie dodatkowy krok (wyjęcie go z kubka) między tobą a smakołykami.

Wersja 2: jeden kubek w zasięgu globalnym

Jeśli nie używałeś modułów, możesz napisać kod w ten sposób (zwróć uwagę na brakexport deklaracji):

Global1ts

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

Global2.ts

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

Global3.ts

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

ten kod tworzy scaloną przestrzeń nazw A w globalnej zakres:

Tutaj wpisz opis obrazka

Ta konfiguracja jest przydatna, ale nie ma zastosowania w przypadku modułów(ponieważ moduły nie zanieczyszczają globalnego zakresu).


Version 3: Going cupless

Wracając do oryginalnego przykładu, kubki A, A, i nie wyświadczają Ci żadnych przysług. Zamiast tego możesz napisać kod jako:

Mod1.ts

export class Twix { ... }

Mod2.ts

export class PeanutButterCup { ... }

Mod3.ts

export class KitKat { ... }

To Utwórz zdjęcie, które wygląda tak:

Tutaj wpisz opis obrazka

O wiele lepiej!

Teraz, jeśli nadal myślisz o tym, jak bardzo chcesz używać przestrzeni nazw ze swoimi modułami, Czytaj dalej...


To nie są pojęcia, których szukasz

Musimy wrócić do początków, dlaczego przestrzenie nazw istnieją w pierwszej kolejności i sprawdzić, czy te powody mają sens dla zewnętrznych modułów.

Organization: Przestrzenie nazw są przydatne do grupowania logicznie powiązanych obiektów i typów. Na przykład w C# znajdziesz wszystkie typy kolekcji w System.Collections. Organizując nasze typy w hierarchiczne przestrzenie nazw, zapewniamy dobre "odkrywanie" dla użytkowników tych typów.

konflikty nazw: Przestrzenie nazw są ważne, aby uniknąć kolizji nazw. Na przykład, możesz mieć My.Application.Customer.AddForm i My.Application.Order.AddForm -- dwa typy o tej samej nazwie, ale z inną przestrzenią nazw. W języku, w którym wszystkie identyfikatory istnieje w tym samym zakresie głównym, a wszystkie zespoły ładują wszystkie typy, ważne jest, aby wszystko było w przestrzeni nazw.

Czy te powody mają sens w zewnętrznych modułach?

Organizacja : zewnętrzne moduły są już obecne w systemie plików, koniecznie. Musimy rozwiązać je według ścieżki i nazwy pliku, więc istnieje logiczny schemat organizacji, którego możemy użyć. Możemy mieć folder /collections/generic/ z modułem list w nim.

konflikty nazw : To nie zastosowanie w ogóle w modułach zewnętrznych. wewnątrz modułu nie ma wiarygodnego powodu, aby mieć dwa obiekty o tej samej nazwie. Od strony konsumpcji, konsument danego modułu wybiera nazwę, której będzie używał, aby odnieść się do modułu, więc przypadkowe konflikty nazw są niemożliwe.


Nawet jeśli nie uważasz, że te powody są odpowiednio rozwiązane przez sposób działania modułów, "rozwiązanie" próby użycia przestrzeni nazw w zewnętrznych modułach nie nawet praca.

Pudełka w pudełkach w pudełkach

Historia:

Twój przyjaciel Bob cię wzywa. "Mam świetny nowy schemat organizacyjny w moim domu", mówi, " przyjdź sprawdzić to!". Schludnie, zobaczmy, co Bob wymyślił. Zaczynasz w kuchni i otwierasz spiżarnię. Istnieje 60 różnych pudełek, każde oznaczone jako "spiżarnia". Wybierasz losowo pudełko i otwierasz je. Wewnątrz znajduje się pojedyncze pudełko z napisem "ziarno". Otwierasz pudełko "ziaren" i znajdujesz pojedyncze pudełko z napisem "makaron". Otwierasz pudełko z makaronem i znajdujesz pojedyncze pudełko z etykietą "Penne". Otwierasz to pudełko i znajdujesz, jak się spodziewasz, torbę makaronu penne.

Lekko zdezorientowany, podnosisz sąsiadujące pudełko, również oznaczone jako "spiżarnia". W środku znajduje się pojedyncze pudełko, ponownie oznaczone jako "ziarna". Otwierasz pudełko "ziarna" i ponownie znajdujesz pojedyncze pudełko z napisem "makaron". Otwierasz pudełko "makaron" i znajdujesz pojedyncze pudełko, to jest oznaczone jako "Rigatoni". Otwórz to pudełko i znajdź... torba z makaron rigatoni.

"to jest świetne!"mówi Bob. "Wszystko jest w przestrzeni nazw!".

"Ale Bob..."odpowiadasz. "Twój schemat organizacyjny jest bezużyteczny. Musisz otworzyć kilka pudełek, aby dostać się do czegokolwiek, i nie jest tak naprawdę wygodniej znaleźć cokolwiek, niż gdybyś po prostu włożył wszystko do jednegopudełka zamiast trzech {64]}. W rzeczywistości, ponieważ twoja spiżarnia jest już posortowana półka po półce, w ogóle nie potrzebujesz pudełek. Dlaczego po prostu nie ustawić makaron na półkę i podnieść ją, kiedy jej potrzebujesz?"

"nie rozumiesz -- muszę się upewnić, że nikt inny nie umieści czegoś, co nie należy do przestrzeni nazw 'spiżarni'. I bezpiecznie zorganizowałem wszystkie moje makarony w przestrzeni nazw Pantry.Grains.Pasta, dzięki czemu mogę je łatwo znaleźć]} Bob jest bardzo zdezorientowany.

Moduły są własnym pudełkiem

Prawdopodobnie zdarzyło ci się coś podobnego w prawdziwym życiu: zamawiasz kilka rzeczy na Amazonie, a każdy przedmiot pojawia się w własne pudełko, z mniejszym pudełkiem w środku, z Twoim przedmiotem zapakowanym we własne opakowanie. Nawet jeśli wewnętrzne pudełka są podobne, przesyłki nie są użytecznie "połączone".

Idąc za analogią pudełkową, kluczową obserwacją jest to, że zewnętrzne moduły są własnymi pudełkami. Może to być bardzo złożony element z wieloma funkcjami, ale każdy dany zewnętrzny moduł jest własnym pudełkiem.


Wytyczne dla modułów zewnętrznych

Teraz, gdy odkryliśmy, że nie potrzebujesz użyć 'przestrzeni nazw' , jak powinniśmy zorganizować nasze moduły? Poniżej przedstawiono kilka zasad przewodnich i przykładów.

Eksportuj jak najbliżej najwyższego poziomu

  • jeśli eksportujesz tylko jedną klasę lub funkcję, użyj export default:

MyClass.ts

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

MyFunc.ts

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

Konsumpcja

import t from './MyClass';
import f from './MyFunc';
var x = new t();
console.log(f());
Jest to optymalne dla konsumentów. Mogą nazwać Twój typ, co chcą (t w tym przypadku) i nie musisz robić żadnych obcych poszukiwań, aby znaleźć swoje przedmioty.
  • jeśli eksportujesz wiele obiektów, umieść je wszystkie na najwyższym poziomie:

Mity.ts

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

Konsumpcja

import * as m from './MyThings';
var x = new m.SomeType();
var y = m.someFunc();
  • jeśli eksportujesz dużą liczbę rzeczy, tylko wtedy powinieneś użyć module/namespace słowo kluczowe:

MyLargeModule.ts

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

Konsumpcja

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

Czerwony Flagi

Wszystkie z poniższych elementów są czerwonymi flagami dla struktury modułów. Sprawdź dokładnie, czy nie próbujesz przestrzeni nazw zewnętrznych modułów, jeśli któreś z nich dotyczy twoich plików:]}

  • plik, którego jedyną deklaracją najwyższego poziomu jest export module Foo { ... } (Usuń Foo I przenieś wszystko "w górę" poziomu)
  • plik, który ma pojedynczy export class lub export function, który nie jest export default
  • wiele plików, które mają ten sam export module Foo { na najwyższym poziomie (nie myśl, że będą one łączyć w jeden Foo!)
 589
Author: Ryan Cavanaugh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-16 17:10:47

Nie ma nic złego w odpowiedzi Ryana, ale dla osób, które przyjechały tutaj, szukając sposobu utrzymania struktury jednej klasy na plik , wciąż poprawnie używającej przestrzeni nazw ES6, zapoznaj się z tym pomocnym zasobem firmy Microsoft.

Jedna rzecz, która jest dla mnie niejasna po przeczytaniu doc, to: jak zaimportować cały (scalony) moduł z pojedynczym import.

Edit / Align = "left" / Kilka podejść do przestrzeni nazw pojawia się w TS.

Wszystkie klasy modułów w jednym pliku.

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

Importuj pliki do przestrzeni nazw i ponownie przypisuj

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

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

Beczki

// ./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();

Ostateczne rozważenie. Możesz przestrzeń nazw KAŻDEGO pliku

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

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

Ale gdy jedna importuje dwie klasy z tej samej przestrzeni nazw, TS będzie narzekać, że istnieje duplikat identyfikatora. Jedynym rozwiązaniem Jak tym razem jest wtedy alias przestrzeni nazw.

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

// ugh
let myTriangle = new _Shapes.Shapes.Triangle();
To aliasing jest absolutnie odrażające, więc nie róbcie tego. You ' re better wyłącz podejście powyżej. Osobiście wolę "beczkę".
 41
Author: Jefftopia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-12 13:58:45

Spróbuj uporządkować według folderu:

Basetypy.ts

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

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

Pies.ts

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

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

Drzewo.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)

Chodzi o to , że Twój moduł nie powinien się przejmować / wiedzieć, że uczestniczy w przestrzeni nazw, ale to naraża twoje API na konsumenta w Kompaktowy, rozsądny sposób, który jest niezależny od typu systemu modułów, którego używasz w projekcie.

 5
Author: Albinofrenchy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-09 12:02:58

Mała impovement Albinofrenchy odpowiedź:

Baza.ts

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

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

Pies.ts

import * as b from './base';

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

Rzeczy.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
Author: Mike Vitik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-27 08:28:19

Jestem z Tobą. znowu też nie ma nic złego w tej odpowiedzi z głosami 300 + up, ale moja opinia jest taka:

  1. Co jest złego w umieszczaniu zajęć w przytulnych, ciepłych plikach indywidualnie? To sprawi, że będzie lepiej, prawda? (lub ktoś taki jak plik 1000 linii dla wszystkich modeli)

  2. Tak więc, jeśli pierwszy zostanie osiągnięty, musimy zaimportować import import... Importuj tylko do każdego z plików modelu, takich jak man, srsly, model / align = "left" / plik d. ts, dlaczego jest tam tyle * s ? powinno być proste, schludne i tyle. Po co mi import? dlaczego? C# ma przestrzenie nazw nie bez powodu.

  3. Do tego czasu dosłownie używasz "nazw plików.ts " jako identyfikatory. Jako identyfikatory... Przyjdź na jego 2017 teraz i nadal to robimy? Ima wrócić na Marsa i spać przez kolejne 1000 lat.

Więc niestety, moja odpowiedź brzmi: nop, nie możesz sprawić, że "przestrzeń nazw" będzie funkcjonalna, jeśli nie używasz tych wszystkich importuje lub używa tych nazw plików jako identyfikatorów (co moim zdaniem jest naprawdę głupie). Inną opcją jest: umieść wszystkie te zależności w polu o nazwie filenameasidentifier.ts i użycie

export namespace(or module) boxInBox {} .

Owiń je tak, aby nie próbowały uzyskać dostępu do innych klas o tej samej nazwie, gdy po prostu próbują uzyskać odniesienie od klasy siedzącej tuż nad nimi.

 2
Author: NO... Bugs...,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-25 06:29:10

Kilka pytań / komentarzy, które widziałem wokół tego tematu, brzmi dla mnie tak, jakby osoba używała Namespace, gdzie oznaczają "alias modułu". Jak wspomniał Ryan Cavanaugh w jednym ze swoich komentarzy, moduł "Wrapper" można ponownie wyeksportować kilka modułów.

Jeśli naprawdę chcesz zaimportować to wszystko z tej samej nazwy/aliasu modułu, połącz moduł wrappera z mapowaniem ścieżek w Twoim tsconfig.json.

Przykład:

./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'

Uwaga : rozdzielczość modułu na wyjściu .Pliki js będą musiały być jakoś obsługiwane, np. za pomocą tego https://github.com/tleunen/babel-plugin-module-resolver

Przykład .babelrc do obsługi rozdzielczości aliasu:

{
    "plugins": [
        [ "module-resolver", {
            "cwd": "babelrc",
            "alias": {
                "CompanyName.Products": "./path/to/typescript/build/output/CompanyName.Products/index.js"
            }
        }],
        ... other plugins ...
    ]
}
 2
Author: Ryan Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-01 02:28:34

Pies.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;
        }
    }
}

Drzewo.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
Author: Alessandro Lendaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-16 05:20:59

Nekromancja.
Jeśli dobrze rozumiem, pytasz, Jak mieć wszystkie swoje klasy w jednym osobnym pliku, zachowując jedną przestrzeń nazw dla nich wszystkich.

Ponieważ wydaje się, że nikt nie ma dobrego rozwiązania - oto pomysł na proste rozwiązanie, które nawet nie obejmuje maszynopisu: To rozwiązanie nazywa się Gulp .

Po prostu umieść wszystkie swoje klasy, które muszą znajdować się w jednej przestrzeni nazw w tym samym folderze (przydatne dla organizacji kodu). Następnie Dodaj zadanie, które Scala wszystkie pliki w tym katalogu w jeden plik (gulp-concat). Następnie dodaj przestrzeń nazw o tej samej nazwie co katalog górny, następnie dodaj skonkatenowane pliki, następnie dodaj nawias zamykający i zapisz do jednego pliku.
Załatwione.
Następnie dodaj gulp-task, które obserwuje zmiany (i uzupełnienia/usunięcia) w tym samym katalogu. Przy zmianie / dodaniu uruchom funkcję concat.

Teraz masz wszystkie klasy w jednym pliku i jeden plik, który zawiera wszystkie klasy w jednej przestrzeni nazw.
Przykładowy kod - wzdłuż linii:

gulp.task("js:min:all", function ()
{
    return gulp.src(["./wwwroot/app/**/*.js", "!" + "./wwwroot/app/**/*.min.js"
        , "./wwwroot/GeneratedScripts/**/*.js", "!" + "./wwwroot/GeneratedScripts/**/*.min.js"], { base: "." })
        .pipe(concat("./wwwroot/js/myscripts.min.js"))
        .pipe(uglify())
        .pipe(gulp.dest("."));
});



gulp.task('watch:js', function ()
{
    gulp.watch('js/**/*.js', ['js:min:all']);
});

Jest tu moduł gulp append-prepend: https://www.npmjs.com/package/gulp-append-prepend

var gap = require('gulp-append-prepend');

gulp.task('myawesometask', function(){
    gulp.src('index.html')
    .pipe(gap.prependFile('header.html'))
    .pipe(gap.prependText('<!-- HEADER -->'))
    .pipe(gap.appendText('<!-- FOOTER -->'))
    .pipe(gap.appendFile('footer.html'))
    .pipe(gulp.dest('www/'));
});

Na koniec ustaw obserwator na start przy ładowaniu rozwiązania i gotowe.

 -3
Author: Stefan Steiger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-14 09:51:23