TypeScript harici modülleriyle ad alanlarını nasıl kullanırım?


233

Bazı kod var:

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 {

  }
}

Bunların hepsi çok kafa karıştırıcı. Aynı ad alanına türlerine katkıda bulunan bir grup harici modüle sahip olmak istiyorum Living.Things. Hiç işi yok gibi görünüyor - Göremiyorum Animaliçinde dogs.ts. Ben tam ad adını yazmak zorunda b.Living.Things.Plantiçinde tree.ts. Aynı ad alanındaki birden çok nesneyi dosya üzerinde birleştirmek işe yaramaz. Bunu nasıl yaparım?

Yanıtlar:


860

Şeker Kupası Analojisi

Sürüm 1: Her şeker için bir fincan

Diyelim ki böyle bir kod yazdınız:

Mod1.ts

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

Mod2.ts

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

Mod3.ts

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

Bu kurulumu oluşturdunuz: resim açıklamasını buraya girin

Her bir modül (kağıt levha) alır , kendi fincan adında A. Bu işe yaramaz - aslında şekerinizi burada organize etmiyorsunuz, sadece ikramlar arasında ek bir adım (fincandan çıkarıyorsunuz) ekliyorsunuz.


Sürüm 2: Küresel kapsamda bir fincan

Modül kullanmıyorsanız, böyle bir kod yazabilirsiniz ( export beyan ):

global1.ts

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

global2.ts

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

global3.ts

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

Bu kod A, genel kapsamda birleştirilmiş bir ad alanı oluşturur :

resim açıklamasını buraya girin

Bu kurulum kullanışlıdır, ancak modüller için geçerli değildir (çünkü modüller genel kapsamı kirletmez).


Sürüm 3: Kupasız gitmek

Orijinal örneğe dönersek A, bardaklar A, veA size iyilik yapmıyorlar. Bunun yerine, kodu şu şekilde yazabilirsiniz:

Mod1.ts

export class Twix { ... }

Mod2.ts

export class PeanutButterCup { ... }

Mod3.ts

export class KitKat { ... }

şuna benzer bir resim oluşturmak için:

resim açıklamasını buraya girin

Çok daha iyi!

Şimdi, hala ad alanınızı modüllerinizle gerçekten ne kadar kullanmak istediğinizi düşünüyorsanız, okumaya devam edin ...


Bunlar Aradığınız Kavramlar Değil

Ad alanlarının neden ilk başta var olduklarının kökenine geri dönmeli ve bu nedenlerin dış modüller için anlamlı olup olmadığını incelemeliyiz.

Organizasyon : Ad alanları, mantıksal olarak ilgili nesneleri ve türleri gruplandırmak için kullanışlıdır. Örneğin, C # 'da, tüm koleksiyon türleriniSystem.Collections . Türlerimizi hiyerarşik ad alanlarına düzenleyerek, bu tür kullanıcılar için iyi bir "keşif" deneyimi sunarız.

İsim Çatışmaları : Çarpışmaların adlandırılmasını önlemek için ad alanları önemlidir. Örneğin, My.Application.Customer.AddFormve My.Application.Order.AddForm- aynı ada, ancak farklı bir ad alanına sahip iki tür olabilir . Tüm tanımlayıcıların aynı kök kapsamda bulunduğu ve tüm derlemelerin tüm türleri yüklediği bir dilde, her şeyin bir ad alanında olması çok önemlidir.

Bu nedenler dış modüllerde anlamlı mı?

Organizasyon : Harici modüller, zorunlu olarak bir dosya sisteminde zaten mevcuttur. Bunları yol ve dosya adına göre çözümlememiz gerekiyor, bu yüzden kullanmamız için mantıklı bir organizasyon şeması var. Bir /collections/generic/klasöre sahip olabiliriz.list modül bulunan .

İsim Anlaşmazlıkları : Bu, harici modüllerde hiç geçerli değildir. Bir modül içinde , aynı ada sahip iki nesneye sahip olmanın makul bir nedeni yoktur. Tüketim taraftan, tüketici herhangi bir modülün kazara adlandırma çatışmaları imkansız bu yüzden, modülün başvurmak için kullanacağı ismini almaya alır.


Bu nedenlerin modüllerin nasıl çalıştığına yeterince değindiğine inanmasanız bile, harici modüllerde ad alanlarını kullanmaya çalışmanın "çözümü" bile işe yaramaz.

Kutular Kutular Kutular

Bir hikaye:

Arkadaşın Bob seni çağırıyor. “Evimde yeni bir organizasyon planım var” diyor, “gel bakalım!”. Güzel, hadi gidip Bob'un ne bulduğunu görelim.

Mutfakta başlayıp kileri açıyorsunuz. Her biri "Kiler" etiketli 60 farklı kutu vardır. Rastgele bir kutu seçip açarsınız. İçeride "Taneler" etiketli tek bir kutu var. "Tahıllar" kutusunu açar ve "Makarna" etiketli tek bir kutu bulursunuz. "Makarna" kutusunu açar ve "Penne" etiketli tek bir kutu bulursunuz. Bu kutuyu açıp beklediğiniz gibi bir torba penne makarna buluyorsunuz.

Biraz karışık, "Kiler" olarak da adlandırılan bitişik bir kutu alırsınız. İçeride yine "Tahıl" olarak etiketlenmiş tek bir kutu var. "Tahıllar" kutusunu açarsınız ve yine "Makarna" etiketli tek bir kutu bulursunuz. "Makarna" kutusunu açın ve tek bir kutu bulmak, bu "Rigatoni" etiketli. Bu kutuyu açıp ... bir çanta rigatoni makarna bul.

"Bu harika!" diyor Bob. "Her şey bir isim alanında!".

"Ama Bob ..." diye cevap ver. "Organizasyon şemanız işe yaramaz. Herhangi bir şeye ulaşmak için bir grup kutu açmanız gerekir ve her şeyi üç yerine bir kutuya koymuş olmanızdan daha uygun değildir . . Aslında, kiler zaten rafa göre sıralanmıştır, kutulara hiç ihtiyacınız yoktur. Neden sadece makarnayı rafa yerleştirip ihtiyaç duyduğunuzda almıyorsunuz? "

"Anlamıyorsun - kimsenin 'Kiler' ad alanına ait olmayan bir şey koymadığından emin olmalıyım. Ve tüm makarnamı güvenli bir şekilde Pantry.Grains.Pastaad alanına yerleştirdim, böylece kolayca bulabilirim"

Bob çok şaşkın bir adam.

Modüller Kendi Kutularıdır

Muhtemelen gerçek hayatta benzer bir şey yaşadınız: Amazon'da birkaç şey sipariş edersiniz ve her öğe kendi kutusunda, daha küçük bir kutuyla, öğeniz kendi ambalajında ​​sarılır. İç kutular benzer olsa bile, gönderiler yararlı bir şekilde "birleşik" değildir.

Kutu benzetmesi ile ilgili olarak, ana gözlem, harici modüllerin kendi kutusu olduklarıdır . Çok fazla işlevselliğe sahip çok karmaşık bir öğe olabilir, ancak herhangi bir harici modül kendi kutusudur.


Harici Modüller için Rehberlik

Artık 'ad alanları' kullanmamız gerekmediğini anladığımıza göre, modüllerimizi nasıl organize etmeliyiz? Bazı yol gösterici ilkeler ve örnekler aşağıdadır.

Mümkün olduğunca üst seviyeye yakın dışa aktarın

  • Yalnızca tek bir sınıfı veya işlevi dışa aktarıyorsanız, şunu kullanın export default:

MyClass.ts

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

MyFunc.ts

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

tüketim

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

Bu tüketiciler için idealdir. Türünüzü istedikleri gibi adlandırabilirler ( tbu durumda) ve nesnelerinizi bulmak için herhangi bir yabancı noktalama yapmak zorunda kalmazlar.

  • Birden çok nesneyi dışa aktarıyorsanız, hepsini en üst düzeye yerleştirin:

MyThings.ts

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

tüketim

import * as m from './MyThings';
var x = new m.SomeType();
var y = m.someFunc();
  • Çok sayıda şeyi dışa aktarıyorsanız, yalnızca module/ namespaceanahtar sözcüğünü kullanmanız gerekir :

MyLargeModule.ts

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

tüketim

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

Kırmızı bayraklar

Aşağıdakilerin tümü modül yapılandırması için kırmızı bayraklardır. Bunlardan herhangi biri dosyalarınız için geçerliyse harici modüllerinizi adlandırmaya çalışmadığınızı bir kez daha kontrol edin:

  • Yalnızca üst düzey bildirimi olan bir dosya export module Foo { ... }( Fooher şeyi kaldırın ve bir üst seviyeye taşıyın)
  • Tekli olan export classveya export functionolmayan bir dosyaexport default
  • export module Foo {Üst düzeyde aynı olan birden fazla dosya (bunların tek bir dosyada birleşeceğini düşünmeyin Foo!)

80
Bu bir cevap değil. Harici modüller için ad alanlarına ihtiyaç duymamanız veya istememeniz gerektiği önermesi yanlıştır. Dosya sistemi yapabilirsiniz organizasyon şemasının bir tür iken tür bu amaçlar için kullanmak tüketici var n belirli bir projeden n sınıfları veya işlevleri kullanarak ithalat ifadeleri için, neredeyse kadar güzel değildir; özellikle de gerçek kodun altındayken adlandırma kuralını karıştırdığı için.
Albinofrenchy

12
Ne kadar isterse isteyin, hala mümkün değil .
Ryan Cavanaugh

26
Anlamıyorum, artık pascal yazmıyoruz. Ne zamandan beri dosya sistemini kullanarak düzenleme yapmanın yolu var?
David

9
İlgilendiğiniz her şeyi kitaplığınızın tüketicilerine ithal eden ve yeniden ihraç eden bir "sarmalayıcı" modülüne sahip olabilirsiniz. Fakat yine de, bir "ad alanı" kullanarak, kodunuzu kullanan herkes için başka bir dolaylı aktarma düzeyi zorlamaktan başka bir değer sağlamayacaktır.
Ryan Cavanaugh

13
Harika yazılar, teşekkürler. Buna www.typescriptlang.org/docs/handbook/namespaces.html adresinden bağlantı vermeniz gerektiğini düşünüyorum. Typescriptlang.org bağlantısını 3 veya 4 kez okumuş olmalıyım ve bir C # dev olarak, doğal olarak her şeyi bir ad alanına koymak istiyorum. Ben değil söyleyerek bazı öneriler okudum, ama hiçbir açıklama ile neden ve hiçbir şey olarak kesin (ve iyi tarif) bu gibi. Artı daktilo dokümanlar hiçbir şey bu AFAIK bahseder
Adam Plocher

53

Ryan'ın cevabında yanlış bir şey yok, ancak buraya ES6 ad alanlarını doğru bir şekilde kullanırken dosya başına bir sınıf yapısını nasıl koruyacağını arayanlar için lütfen Microsoft'un bu yararlı kaynağına bakın .

Dokümanı okuduktan sonra benim için belirsiz olan bir şey: tüm (birleştirilmiş) modülün tek biriyle nasıl içe aktarılacağı import.

Düzenleme Turlu bu cevabı güncellemek için geri. İsim alanında birkaç yaklaşım TS'de ortaya çıkmaktadır.

Tüm modül sınıfları tek bir dosyada.

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

Dosyaları ad alanına aktarın ve yeniden atayın

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

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

variller

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

Son değerlendirme. Sen olabilir her dosyayı ad alanı

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

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

Ancak, aynı ad alanından iki sınıf alırken, TS yinelenen bir tanımlayıcı olduğundan şikayet edecektir. Bu seferki tek çözüm daha sonra ad alanını takma addır.

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

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

Bu takma ad kesinlikle mutsuzdur, bu yüzden yapma. Yukarıdaki bir yaklaşımla daha iyi durumdasınız. Şahsen ben 'namluyu' tercih ederim.


6
"ES6 ad alanları" nedir?
Aluan Haddad

@AluanHaddad es2015 + 'ı içe aktarırken, içe aktarılanlar varsayılan, yıkılmış veya ad boşlukludur. const fs = require('fs'), fsad alanıdır. import * as moment from 'moment', momentad alanıdır. Bu ontoloji, şartname değil.
Jefftopia

Bunun farkındayım ama cevabında açıklamak iyi olur. ES6 ad alanları aslında bir şeydir ve requireörnek, ES6 ad alanlarının çağrılmayabileceği gibi bir dizi nedenden dolayı bunlara requireuygulanmazken, iyi çağrılabilen düz bir nesne döndürür.
Aluan Haddad

1
İzlemiyorum, çünkü içe aktarılan şeyin çağrılabilir olup olmadığı hala mantıksal olarak konuşan bir ad alanı olarak hizmet ediyor . Uyarıların yukarıdaki cevabım için önemli olduğunu düşünmüyorum.
Jefftopia

7

Klasöre göre düzenlemeyi deneyin:

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)

Fikir, modülünüzün kendilerinin bir ad alanına katıldıklarını önemsememesi / bilmemesi gerektiğidir, ancak bu, API'nizi, proje için hangi tür modül sistemini kullandığınıza agnostik olan kompakt, mantıklı bir şekilde tüketiciye maruz bırakır.


8
LivingThings.dog.Dog burada ne var.
Corey Alix

Harf büyüklüğünü tutarlı tutmanızı öneririm, "Ağaç" ı dışa aktarırsanız "Ağaç" ı değil, "Ağaç" ı içe aktarırsınız.
demisx

1
Ayrıca, tree.tsdışa aktarılan bir üyesi olmadığı zaman nasıl bir şey aktarabilirsiniz ?
demisx

Man TS , bir ifadede olduğu gibi importve requirebirlikte aptalca eski bir sözdizimine sahip olduğundan emin .
Andy

3

Albinofrenchy cevabının küçük ölçüde gelişmesi:

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
Bunun için teşekkürler! Sadece mevcut bir cevaptaki değişikliklerin tercihen yeni cevaplar olarak gönderilmemesi gerektiğini söylemek istedim: ya mevcut cevaba bir yorum olarak eklenmeli ya da (daha iyi) istediğiniz cevaba bir düzenleme önererek önerilmelidir. geliştirmek.
a3nm

3

OP Ben seninleyim adamım. yine, 300+ yukarı oyla bu cevapta yanlış bir şey yok, ama benim düşüncem:

  1. sınıfları kendi sıcacık kendi dosyalarına tek tek koymanın nesi yanlış? Demek istediğim bu işler daha iyi görünecek değil mi? (veya tüm modeller için 1000 satır dosyası gibi biri)

  2. öyleyse, birincisi elde edilecekse, ithalat ithalatını içe aktarmalıyız ... sadece man, srsly, bir model dosyası, bir .d.ts dosyası gibi model dosyalarının her birinde içe aktarmalıyız, neden bu kadar çok var * orada mı? sadece basit, düzenli olmalı ve hepsi bu kadar. Neden orada ithalata ihtiyacım var? neden? C # bir nedenle ad alanları var.

  3. Ve o zamana kadar, tanımlayıcı olarak "filenames.ts" kullanıyorsunuz. Tanımlayıcılar olarak ... Şimdi 2017'ye gelin ve hala yapıyoruz? Mars'a geri dönün ve 1000 yıl daha uyuyun.

Ne yazık ki, cevabım: nop, tüm bu ithalatları kullanmazsanız veya bu dosya adlarını tanımlayıcı olarak (gerçekten saçma olduğunu düşünüyorum) kullanmazsanız, "ad alanı" özelliğini işlevsel hale getiremezsiniz. Başka bir seçenek: tüm bu bağımlılıkları filenameasidentifier.ts adlı bir kutuya koymak ve kullanmak

export namespace(or module) boxInBox {} .

onları sadece sınıfın hemen üstüne oturmaktan bir referans almaya çalışırken aynı ada sahip diğer sınıflara erişmeye çalışmazlar.


3

Bu konuda gördüğüm soruların / yorumların bazıları bana Namespace'modül takma adı' anlamına geldiği yerde kullanıyormuş gibi geliyor. Ryan Cavanaugh'un yorumlarından birinde belirttiği gibi, bir 'Sarmalayıcı' modülünün birkaç modülü tekrar dışa aktarabilirsiniz.

Hepsini aynı modül adından / diğer adından almak istiyorsanız, bir sarmalayıcı modülünü, eşlemenizdeki yollarla eşleştirin tsconfig.json.

Misal:

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

Not : Çıkış .js dosyalarındaki modül çözünürlüğünün, bu https://github.com/tleunen/babel-plugin-module-resolver gibi bir şekilde ele alınması gerekir.

Örnek .babelrctakma çözünürlüğü işlemek için:

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

1

Bu ad alanları modülünü deneyin

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

--- derleme bölümü ---

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

Kodunuzu düzenlemenin uygun yolu, ad alanları yerine ayrı dizinler kullanmaktır. Her sınıf kendi dosyasında, ilgili ad alanı klasöründe olacaktır. index.ts yalnızca her dosyayı yeniden dışa aktarır; index.ts dosyasında gerçek bir kod olmamalıdır. Kodunuzu bu şekilde düzenlemek gezinmeyi kolaylaştırır ve dizin yapısına göre kendi kendini belgelendirir.

// 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";

Daha sonra bu şekilde kullanırsınız:

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);

Bu yanıltıcı ve kesinlikle yanlış. İsim alanları böyle çalışmaz. Ayrıca ops sorusuna cevap vermez.
AndrewMcLagan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.