Analogia 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 { ... }
}
Utworzyłeś tę konfigurację:
Każdy moduł (arkusz papieru) otrzymuje własną filiżankę o nazwie A
. Jest to bezużyteczne - tak naprawdę nie organizujesz tutaj swojego cukierka, po prostu dodajesz dodatkowy krok (wyjmując go z kubka) między tobą a smakołykami.
Wersja 2: Jeden kubek w zasięgu globalnym
Jeśli nie korzystasz z modułów, możesz napisać taki kod (zwróć uwagę na brak export
deklaracji):
global1.ts
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 zasięgu globalnym:
Ta konfiguracja jest przydatna, ale nie ma zastosowania w przypadku modułów (ponieważ moduły nie zanieczyszczają zasięgu globalnego).
Wersja 3: Going cupless
Wracając do pierwotnego przykład kubki A
, A
i A
nie robią 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 { ... }
aby utworzyć zdjęcie, które wygląda następująco:
Dużo lepiej!
Jeśli nadal zastanawiasz się, jak bardzo chcesz używać przestrzeni nazw w swoich modułach, czytaj dalej ...
To nie są koncepcje, których szukasz
Musimy przede wszystkim wrócić do początków istnienia obszarów nazw i zbadać, czy te przyczyny mają sens w przypadku modułów zewnętrznych.
Organizacja : Przestrzenie nazw są przydatne do grupowania logicznie powiązanych obiektów i typów. Na przykład w języku C # znajdziesz wszystkie typy kolekcji System.Collections
. Organizując nasze typy w hierarchiczne przestrzenie nazw, zapewniamy dobre wrażenia „odkrywcze” użytkownikom 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 o innej przestrzeni nazw. W języku, w którym wszystkie identyfikatory istnieją w tym samym zakresie głównym, a wszystkie zestawy ładują wszystkie typy, bardzo ważne jest, aby wszystko było w przestrzeni nazw.
Czy te powody mają sens w modułach zewnętrznych?
Organizacja : Moduły zewnętrzne są już koniecznie obecne w systemie plików. Musimy je rozwiązać według ścieżki i nazwy pliku, więc mamy do dyspozycji logiczny schemat organizacyjny. Możemy mieć /collections/generic/
folder z list
modułem.
Konflikty nazw : To nie dotyczy w ogóle modułów zewnętrznych. W module nie ma żadnego uzasadnionego powodu, aby mieć dwa obiekty o tej samej nazwie. Od strony konsumpcji konsument dowolnego modułu może wybrać nazwę, której będzie używał w odniesieniu do modułu, więc przypadkowe konflikty nazw są niemożliwe.
Nawet jeśli nie wierzysz, że przyczyny te są odpowiednio rozwiązane przez sposób działania modułów, „rozwiązanie” próby użycia przestrzeni nazw w modułach zewnętrznych nawet nie działa.
Pudełka w Pudełka w Pudełkach
Historia:
Twój przyjaciel Bob dzwoni do ciebie. „Mam świetny nowy program organizacyjny w moim domu”, mówi, „chodź, sprawdź to!”. Schludnie, zobaczmy, co wymyślił Bob.
Zaczynasz w kuchni i otwierasz spiżarnię. Istnieje 60 różnych pudełek, każde oznaczone jako „Spiżarnia”. Losowo wybierasz pudełko i je otwierasz. Wewnątrz znajduje się pojedyncze pudełko z napisem „Ziarna”. Otworzysz okno „Ziarna” i znajdziesz pojedyncze pudełko z etykietą „Makaron”. Otworzysz pudełko „Makaron” i znajdziesz pojedyncze pudełko z napisem „Penne”. Otworzysz to pudełko i znajdziesz, jak się spodziewasz, torebkę makaronu penne.
Nieco zdezorientowany podnosisz sąsiednie pudełko, oznaczone również jako „Spiżarnia”. Wewnątrz znajduje się pojedyncze pudełko, ponownie oznaczone jako „Ziarna”. Otworzysz okno „Ziarna” i ponownie znajdziesz pojedyncze pudełko z etykietą „Makaron”. Otworzysz pudełko „Makaron” i znajdziesz jedno pudełko, które ma etykietę „Rigatoni”. Otwierasz to pudełko i znajdujesz ... torebkę makaronu rigatoni.
"Wspaniale!" 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, a znalezienie czegoś nie jest wygodniejsze, niż gdybyś po prostu umieścił wszystko w jednym pudełku zamiast trzech . W rzeczywistości, ponieważ spiżarnia jest już posortowana półka po półce, w ogóle nie potrzebujesz pudełek. Dlaczego nie po prostu położyć makaronu na półce i podnieść go, gdy jest potrzebny? ”
„Nie rozumiesz - muszę się upewnić, że nikt inny nie umieści czegoś, co nie należy do przestrzeni nazw„ Spiżarnia ”. I bezpiecznie zorganizowałem cały mój makaron w Pantry.Grains.Pasta
przestrzeni nazw, aby łatwo go znaleźć”
Bob jest bardzo zdezorientowanym mężczyzną.
Moduły są ich własnym pudełkiem
Prawdopodobnie zdarzyło Ci się coś podobnego w prawdziwym życiu: zamawiasz kilka rzeczy na Amazon, a każdy przedmiot pojawia się we własnym pudełku, z mniejszym pudełkiem w środku, z przedmiotem zapakowanym we własne opakowanie. Nawet jeśli pudełka wewnętrzne są podobne, przesyłki nie są użytecznie „łączone”.
Idąc za analogią skrzynki, kluczową obserwacją jest to, że moduły zewnętrzne są ich własną skrzynką . Może to być bardzo złożony przedmiot z dużą ilością funkcji, ale każdy dany moduł zewnętrzny jest własnym pudełkiem.
Wskazówki dotyczące modułów zewnętrznych
Teraz, gdy zorientowaliśmy się, że nie musimy używać „przestrzeni nazw”, w jaki sposób powinniśmy organizować nasze moduły? Poniżej przedstawiono niektóre zasady przewodnie i przykłady.
Eksportuj tak blisko najwyższego poziomu, jak to możliwe
- 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, jak chcą ( t
w tym przypadku) i nie muszą wykonywać żadnych dodatkowych kropek, aby znaleźć twoje obiekty.
- Jeśli eksportujesz wiele obiektów, umieść je wszystkie na najwyższym poziomie:
MyThings.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ć słowa kluczowego
module
/ namespace
:
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();
Czerwone flagi
Wszystkie poniższe są czerwonymi flagami dla struktury modułu. Sprawdź dokładnie, czy nie próbujesz przestrzeni nazw zewnętrznych modułów, jeśli któryś 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ę” o poziom)
- Plik, który ma jeden
export class
lub export function
który nie jestexport default
- Wiele plików, które mają to samo
export module Foo {
na najwyższym poziomie (nie myśl, że zostaną połączone w jeden Foo
!)
Nie ma nic złego w odpowiedzi Ryana, ale dla osób, które tu przyjechały i szukają sposobu na utrzymanie struktury jednej klasy na plik przy jednoczesnym prawidłowym korzystaniu z przestrzeni nazw ES6, zapoznaj się z tym pomocnym zasobem firmy Microsoft.
Jedną z rzeczy, które są dla mnie niejasne po przeczytaniu dokumentu, jest: jak zaimportować cały (scalony) moduł za pomocą jednego
import
.Edytuj Krążenie w tył, aby zaktualizować tę odpowiedź. Kilka podejść do przestrzeni nazw pojawia się w TS.
Wszystkie klasy modułów w jednym pliku.
Zaimportuj pliki do przestrzeni nazw i ponownie przypisz
Beczki
Ostateczne rozważanie. Państwo mogłoby przestrzeni nazw każdego pliku
Ale ponieważ jedna importuje dwie klasy z tej samej przestrzeni nazw, TS skarży się, że istnieje duplikat identyfikatora. Jedynym rozwiązaniem, ponieważ tym razem jest alias przestrzeni nazw.
To aliasing jest absolutnie odrażające, więc nie rób tego. Lepiej jest z powyższym podejściem. Osobiście wolę „beczkę”.
źródło
const fs = require('fs')
,fs
to przestrzeń nazw.import * as moment from 'moment'
,moment
to przestrzeń nazw. To jest ontologia, a nie specyfikacja.require
przykład nie ma do nich zastosowania z wielu powodów, w tym dlatego, że przestrzenie nazw ES6 nie mogą być wywoływane, arequire
zwraca zwykły obiekt, który może być wywoływalny.Spróbuj uporządkować według folderów:
baseTypes.ts
dog.ts
tree.ts
LivingThings.ts
main.ts
Chodzi o to, że sam moduł nie powinien się przejmować / wiedzieć, że uczestniczy w przestrzeni nazw, ale to ujawnia twoje API konsumentowi w zwarty, rozsądny sposób, który jest niezależny od typu systemu modułów, którego używasz w projekcie.
źródło
tree.ts
skoro nie ma ono w ogóle eksportowanego członka?import
irequire
razem w jednym zdaniu.Małe ulepszenie odpowiedzi Albinofrenchy:
base.ts
dog.ts
Things.ts
main.ts
źródło
OP Jestem z tobą stary. znowu, nie ma nic złego w tej odpowiedzi z ponad 300 głosami, ale moim zdaniem jest:
co jest złego w indywidualnym umieszczaniu zajęć w przytulnych, ciepłych plikach? Mam na myśli, że dzięki temu wszystko będzie wyglądać znacznie lepiej, prawda? (lub ktoś taki jak plik linii 1000 dla wszystkich modeli)
więc jeśli pierwszy zostanie osiągnięty, musimy zaimportować import import ... importuj tylko w każdym pliku modelu, takim jak man, srsly, plik modelu, plik .d.ts, dlaczego jest tak wiele * jest tam? powinno być po prostu proste, schludne i to wszystko. Dlaczego potrzebuję tam importu? czemu? C # ma przestrzenie nazw bez powodu.
Do tego czasu dosłownie używasz „filenames.ts” jako identyfikatorów. Jako identyfikatory ... Chodź teraz w 2017 roku i nadal to robimy? Wrócę na Marsa i śpię jeszcze przez 1000 lat.
Niestety moja odpowiedź brzmi: nie, nie możesz sprawić, by „przestrzeń nazw” działała, jeśli nie używasz wszystkich tych importów lub używasz 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żyj
owiń je, aby nie próbowały uzyskać dostępu do innych klas o tej samej nazwie, gdy po prostu starają się uzyskać referencję od klasy, usiąść nad nimi.
źródło
Kilka pytań / komentarzy, które widziałem wokół tego tematu, brzmi dla mnie tak, jakby osoba używała
Namespace
tam, gdzie mają na myśli „alias modułu”. Jak wspomniał Ryan Cavanaugh w jednym ze swoich komentarzy, moduł Wrapper może ponownie eksportować kilka modułów.Jeśli naprawdę chcesz zaimportować wszystko z tej samej nazwy / aliasu modułu, połącz moduł opakowania z mapowaniem ścieżek w swoim
tsconfig.json
.Przykład:
./path/to/CompanyName.Products/Foo.ts
./path/to/CompanyName.Products/Bar.ts
./path/to/CompanyName.Products/index.ts
tsconfig.json
main.ts
Uwaga : Rozdzielczość modułu w wyjściowych plikach .js będzie musiała być jakoś obsługiwana, na przykład za pomocą tego https://github.com/tleunen/babel-plugin-module-resolver
Przykład
.babelrc
obsługi rozdzielczości aliasu:źródło
Wypróbuj ten moduł przestrzeni nazw
namespaceModuleFile.ts
bookTreeCombine.ts
--- część kompilacji ---
źródło
dog.ts
tree.ts
źródło
Właściwym sposobem organizacji kodu jest użycie oddzielnych katalogów zamiast przestrzeni nazw. Każda klasa będzie we własnym pliku, w odpowiednim folderze przestrzeni nazw. index.ts ponownie wyeksportuje tylko każdy plik; w pliku index.ts nie powinien znajdować się żaden rzeczywisty kod. Zorganizowanie kodu w ten sposób znacznie ułatwia nawigację i samo dokumentuje się na podstawie struktury katalogów.
Użyłbyś go jako takiego:
źródło