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.Plant의를 tree.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 : 글로벌 범위에서 한 컵

모듈을 사용하지 않았다면 다음과 같은 코드를 작성할 수 있습니다 ( export선언 부족 ).

global1.ts

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

global2.ts

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

global3.ts

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

코드 A는 전역 범위에서 병합 된 네임 스페이스 를 만듭니다 .

여기에 이미지 설명을 입력하십시오

이 설정은 유용하지만 모듈의 경우에는 적용되지 않습니다 (모듈이 전역 범위를 오염시키지 않기 때문에).


버전 3 : 컵리스

원래의 예를 다시가는, 컵 A, AA 당신에게 호의를하고 있지 않습니다. 대신 코드를 다음과 같이 작성할 수 있습니다.

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두 개의 동일한 이름을 가진 유형,하지만 다른 네임 스페이스를 -. 모든 식별자가 동일한 루트 범위에 있고 모든 어셈블리가 모든 유형을로드하는 언어에서는 모든 것이 네임 스페이스에 있어야합니다.

이러한 이유는 외부 모듈에서 의미가 있습니까?

구성 : 외부 모듈은 이미 파일 시스템에 존재합니다. 경로와 파일 이름으로 해결해야하므로 사용할 수있는 논리적 구성 체계가 있습니다. 우리는 /collections/generic/폴더를 가질 수 있습니다list모듈 .

이름 충돌 : 이것은 외부 모듈에 전혀 적용되지 않습니다. 모듈 에서 같은 이름을 가진 두 개의 객체를 가질만한 이유는 없습니다. 소비 측면에서 특정 모듈 의 소비자 는 모듈을 참조하는 데 사용할 이름을 선택하므로 우연한 이름 충돌이 불가능합니다.


모듈이 작동하는 방식으로 이러한 이유가 적절히 해결되었다고 생각하지 않더라도 외부 모듈에서 네임 스페이스를 사용하려는 "솔루션"은 작동하지 않습니다.

상자에 상자에 상자

이야기:

친구 Bob이 전화합니다. "저는 집에 아주 훌륭한 조직 체계가 있습니다." 깔끔한, 밥이 무슨 일을했는지 ​​보자.

부엌에서 시작하여 식료품 저장실을여십시오. 각각 "Pantry"라고 레이블이 지정된 60 개의 서로 다른 상자가 있습니다. 상자를 무작위로 골라서 엽니 다. 내부에는 "곡물"이라는 단일 상자가 있습니다. "곡물"상자를 열고 "파스타"라고 표시된 단일 상자를 찾으십시오. "파스타"상자를 열고 "펜"이라는 단일 상자를 찾으십시오. 이 상자를 열고 예상대로 펜네 파스타 한 봉지를 찾으십시오.

약간 혼동되면 "Pantry"라고 표시된 인접 상자를 선택하십시오. 안에는 "곡물"이라는 레이블이 붙은 단일 상자가 있습니다. "곡물"상자를 열고 "파스타"라고 표시된 단일 상자를 다시 찾습니다. "파스타"상자를 열고 하나의 상자를 찾으십시오.이 상자에는 "Rigatoni"라는 레이블이 붙어 있습니다. 이 상자를 열고 리가 토니 파스타 한 봉지를 찾으세요

"좋아요!" 밥이 말합니다. "모든 것이 네임 스페이스에 있습니다!".

"하지만 밥 ..."당신이 대답합니다. "조직 구성은 쓸모가 없습니다. 무엇이든 얻기 위해 많은 상자를 열어야합니다. 실제로 개가 아닌 상자 에 모든 것을 넣은 것보다 더 쉽게 찾을 수는 없습니다. . 실제로, 식료품 저장실은 이미 선반별로 분류되어 있으므로 상자가 전혀 필요하지 않습니다. 선반에 파스타를 놓고 필요할 때 집어 올리는 것이 어떻습니까? "

"당신은 이해하지 못합니다-다른 누구도 'Pantry'네임 스페이스에 속하지 않은 것을 넣지 않도록해야합니다. 그리고 모든 파스타를 Pantry.Grains.Pasta네임 스페이스에 안전하게 정리하여 쉽게 찾을 수 있습니다."

밥은 매우 혼란스러운 사람입니다.

모듈은 자체 상자입니다

아마도 실제 상황에서 비슷한 일이 있었을 것입니다. 아마존에서 몇 가지 물건을 주문하면 각 상자에 작은 상자가 있고 상자에 포장되어있는 각 상자에 자체 항목이 표시됩니다. 내부 상자가 유사하더라도 발송물은 유용하게 "결합"되지 않습니다.

박스 유추와 마찬가지로 주요 관찰 사항은 외부 모듈이 자체 박스라는 것 입니다. 많은 기능을 가진 매우 복잡한 항목 일 수 있지만 지정된 외부 모듈은 자체 상자입니다.


외부 모듈에 대한 지침

이제 '네임 스페이스'를 사용할 필요가 없다는 것을 알았으므로 모듈을 어떻게 구성해야합니까? 몇 가지 지침 원칙과 예가 이어집니다.

가능한 한 최상위 수준에 가깝게 내보내기

  • 단일 클래스 또는 함수 만 내보내는 경우 다음을 사용하십시오 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 {최상위 수준에서 동일한 여러 파일 (이 파일들이 하나로 결합 될 것이라고 생각하지 마십시오 Foo!)

80
이것은 대답이 아닙니다. 외부 모듈에 네임 스페이스가 필요하지 않거나 필요 없다는 전제는 잘못된 것입니다. 파일 시스템은 이러한 목적으로 사용할 수있는 일종의 조직 체계 이지만, 소비자가 주어진 프로젝트에서 n 개의 클래스 또는 함수를 사용하기위한 n 개의 import 문을 갖는 것은 그리 좋지 않습니다. 특히 실제 코드가 다운 될 때 명명 규칙을 어지럽히 기 때문에 특히 그렇습니다.
Albinofrenchy

12
아무리 많은 것을 원하더라도 여전히 불가능합니다 .
Ryan Cavanaugh

26
나는 더 이상 파스칼을 쓰지 않는다. 언제부터 파일 시스템을 사용하여 구성하고 있습니까?
David

9
라이브러리의 소비자에게 관심있는 모든 것을 가져오고 다시 내보내는 "래퍼"모듈을 가질 수 있습니다. 그러나 "네임 스페이스"를 사용하면 코드를 사용하는 사람에게 다른 수준의 간접적 인 강제를 적용하는 것 외에 다른 값을 제공하지 않습니다.
Ryan Cavanaugh

13
대단한 글쓰기 감사합니다. 나는 당신이 www.typescriptlang.org/docs/handbook/namespaces.html에서 이것에 링크해야한다고 생각합니다. typescriptlang.org 링크를 3-4 번 읽었으며 C # 개발자는 자연스럽게 네임 스페이스에 모든 것을 넣고 싶습니다. 나는하지 말라고 제안하는 몇 가지를 읽었지만 그 이유와 그에 대한 결정적인 이유는 없습니다. 또한 타이프 스크립트 문서에서이 AFAIK에 대해 언급 한 것은 없습니다
Adam Plocher

53

아무것도 라이언의 대답은 잘못하지만 유지 관리하는 방법을 찾고 여기 온 사람들을위한 하나의 클래스 당 파일 여전히 제대로을 참조하시기 바랍니다 ES6 네임 스페이스를 사용하는 동안 구조를 Microsoft에서 유용 자원.

해당 문서를 읽은 후 나에게 불분명 한 가지입니다 : 전체를 가져 오는 방법으로 모듈 (합병) 하나 import .

이 답변을 업데이트하려면 순환을 다시 편집하십시오 . 네임 스페이스에 대한 몇 가지 접근 방식이 TS에 등장합니다.

하나의 파일에있는 모든 모듈 클래스.

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

그러나 동일한 네임 스페이스에서 두 개의 클래스를 가져 오면 TS는 중복 식별자가 있다고 불평합니다. 이때 유일한 해결책은 네임 스페이스의 별칭을 지정하는 것입니다.

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

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

이 앨리어싱은 절대적으로 끔찍하기 때문에 그렇게하지 마십시오. 위의 접근 방식을 사용하는 것이 좋습니다. 개인적으로 나는 '배럴'을 선호합니다.


6
"ES6 네임 스페이스"란 무엇입니까?
Aluan Haddad

es2015 +를 가져올 때 @AluanHaddad에서 가져온 항목은 기본, 구조 해제 또는 네임 스페이스입니다. const fs = require('fs'), fs네임 스페이스입니다. import * as moment from 'moment', moment네임 스페이스입니다. 이것은 사양이 아닌 온톨로지입니다.
Jefftopia

나는 그것을 알고 있지만 당신은 당신의 대답에 그것을 설명하는 것이 좋습니다. 그러나 ES6 네임 스페이스는 실제로 하나의 require사례 이지만 ES6 네임 스페이스가 호출되지 않을 수있는 등 여러 가지 이유로이 예제에는 적용되지 않지만 호출 require가능한 일반 객체를 반환합니다.
Aluan Haddad

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

1
또한 tree.ts내 보낸 멤버가 없을 때 어떻게 가져올 수 있습니까?
demisx

남자 TS 확실히처럼 바보 같은 오래된 구문 importrequire함께 한 성명에서.
Andy

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

3

OP 나는 당신과 함께 있습니다. 다시 한 번, 300 개 이상의 투표로 그 대답에는 아무런 문제가 없지만 내 의견은 다음과 같습니다.

  1. 클래스를 아늑한 따뜻한 파일에 개별적으로 넣는 것은 어떤 문제입니까? 이것이 더 잘 보이게 할 것입니까? (또는 모든 모델에서 1000 줄 파일을 좋아하는 사람)

  2. 따라서 첫 번째 파일을 가져 오려면 import import import ... man, srsly, model 파일, .d.ts 파일과 같은 각 모델 파일에서 import를 가져와야합니다. 거기에? 간단하고 깔끔해야합니다. 수입품이 필요한 이유는 무엇입니까? 왜? C #에서 네임 스페이스를 얻었습니다.

  3. 그리고 그때까지 문자 그대로 "filenames.ts"를 식별자로 사용합니다. 식별자로서 ... 2017 년에 와서 아직도 그렇게합니까? 이마는 화성으로 돌아가서 1000 년 더 잠을 잔다.

슬프게도 내 대답은 : nop, 모든 가져 오기를 사용하지 않거나 해당 파일 이름을 식별자로 사용하는 경우 "네임 스페이스"를 기능적으로 만들 수 없습니다 (실제로 바보라고 생각합니다). 또 다른 옵션은 이러한 모든 종속성을 filenameasidentifier.ts라는 상자에 넣고 사용합니다.

export namespace(or module) boxInBox {} .

단순히 클래스 위에있는 참조를 얻으려고 할 때 같은 이름으로 다른 클래스에 액세스하려고하지 않도록 랩핑하십시오.


3

이 주제와 관련하여 본 몇 가지 질문 / 의견은 마치 사람이 Namespace'모듈 별칭'을 의미 하는 곳에서 사용 하는 것처럼 들립니다 . Ryan Cavanaugh가 자신의 의견 중 하나에서 언급했듯이 '래퍼'모듈로 여러 모듈을 다시 내보낼 수 있습니다.

동일한 모듈 이름 / 별칭에서 모두 가져 오려면 래퍼 모듈을의 경로 매핑과 결합하십시오 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.