Как использовать пространства имен с внешними модулями TypeScript?



у меня есть код:



базовые типы.ТС



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


собака.ТС



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

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


дерево.ТС



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

}
}


все это очень запутанно. Я хочу иметь кучу внешних модулей всех типов вклада в одно и то же пространство имен, Living.Things. Кажется, что это вообще не работает - я не вижу Animal на dogs.ts. Я должен написать полное имя пространства имен b.Living.Things.Plant на tree.ts. Это не работает, чтобы объедините несколько объектов в одном пространстве имен в файле. Как мне это сделать?

698   8  

8 ответов:

Аналогия С Конфетами

Версия 1: чашка для каждой конфеты

допустим, вы написали такой код:

Mod1.ТС

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

Mod2.ТС

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

Mod3.ТС

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

вы создали эту установку: enter image description here

каждый модуль (лист бумаги) получает своя чашка имени A. Это бесполезно - на самом деле ты не организации ваша конфета здесь, вы просто добавляете дополнительный шаг (вынимая его из чашки) между вами и лакомствами.


Версия 2: Одна чашка в глобальном объеме

если вы не использовали модули, вы можете написать такой код (обратите внимание на отсутствие export объявления):

global1.ТС

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

global2.ТС

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

global3.ТС

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

этой код создает объединенное пространство имен A в глобальном масштабе:

enter image description here

Эта настройка полезна, но не применяется в случае модулей (потому что модули не загрязняют глобальный масштаб.)


версия 3: Going cupless

возвращаясь к исходному примеру, чашки A,A и A не делаю тебе никаких одолжений. Вместо этого вы можете написать код как:

Mod1.ТС

export class Twix { ... }

Mod2.ТС

export class PeanutButterCup { ... }

Mod3.ТС

export class KitKat { ... }

чтобы создать изображение, которое выглядит так это:

enter image description here

намного лучше!

теперь, если вы все еще думаете о том, сколько вы действительно хотите использовать пространство имен с модулями, читайте дальше...


это не те понятия, которые вы ищете

нам нужно вернуться к истокам того, почему пространства имен существуют в первую очередь и изучить, имеют ли эти причины смысл для внешних модули.

организация: пространства имен удобны для группировки логически связанных объектов и типов. Например, в C# вы найдете все типы коллекций в System.Collections. Организуя наши типы в иерархические пространства имен, мы предоставляем хороший опыт "обнаружения" для пользователей этих типов.

Конфликты Имен: пространства имен важны, чтобы избежать конфликтов именования. Например, у вас может быть My.Application.Customer.AddForm и My.Application.Order.AddForm -- два типа с одинаковым именем, но разным пространством имен. В языке, где все идентификаторы существуют в одной корневой области и все сборки загружают все типы, очень важно, чтобы все было в пространстве имен.

имеют ли эти причины смысл во внешних модулях?

организация внешние модули уже присутствуют в файловой системе, обязательно. Мы должны решить их по пути и имени файла, так что есть логическая схема организации для нас использовать. Мы можем иметь С в нем.

Конфликты Имен: это не применяется вообще во внешних модулях. внутри модуль, нет никаких оснований, чтобы иметь два объекта с одинаковым именем. Со стороны потребления,потребитель любого данного модуля получает выбрать имя, которое они будут использовать для ссылки на модуль, поэтому случайные конфликты именования невозможны.


даже если вы не верите, что эти причины адекватно решаются тем, как работают модули, "решение" попытки использовать пространства имен во внешних модулях даже не работает.

коробки в коробках в коробках

история:

тебе звонит твой друг Боб. "У меня есть отличная новая схема организации в моем доме", - говорит он, - "приходите проверить это!". Опрятно, пойдем посмотрим, что придумал Боб.

вы начинаете на кухне и открыть кладовая. Есть 60 различных коробок, каждая с надписью "кладовая". Вы выбираете коробку наугад и открываете ее. Внутри находится одна коробка с надписью "зерна". Вы открываете коробку " зерна "и находите одну коробку с надписью"паста". Вы открываете коробку " паста "и находите одну коробку с надписью"Пенне". Вы открываете эту коробку и находите, как и ожидалось, пакет пасты пенне.

слегка растерявшись, вы поднимаете соседнюю коробку, также помеченную "кладовая". Внутри находится одна коробка, опять же с надписью "зерна". Ты открываешь ... коробка " зерна "и, опять же, найти одну коробку с надписью"макароны". Вы открываете коробку " паста "и находите одну коробку, на которой написано"ригатони". Вы открываете эту коробку и находите... пакетик пасты ригатони.

"это здорово!- говорит Боб. "Все находится в пространстве имен!".

", Но Боб...- Отвечай ты. - Ваша организационная схема бесполезна. Вы должны открыть кучу ящиков, чтобы добраться до чего-либо, и на самом деле не более удобно найти что-либо, чем если бы у вас было просто положите все в один вместо три. На самом деле, поскольку ваша кладовая уже отсортирована по полкам, вам вообще не нужны коробки. Почему бы просто не поставить пасту на полку и не забрать ее, когда она вам понадобится?"

ничего плохого в ответе Райана, но для людей, которые пришли сюда в поисках того, как поддерживать один класс на файл структура при правильном использовании пространств имен ES6 пожалуйста, обратитесь к этой полезный ресурс от Microsoft.

одна вещь, которая мне непонятна после прочтения документа: как импортировать весь (объединенный) модуль с помощью одинimport.

Edit Возвращается, чтобы обновить этот ответ. Один несколько подходов к пространству имен появляются в 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();

это сглаживание абсолютно отвратительно, так что не делайте этого. Вам лучше с подходом выше. Лично я предпочитаю "бочку".

попробуйте организовать по папкам:

базовые типы.ТС

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

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

собака.ТС

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

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

дерево.ТС

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

class Tree extends b.Plant {
}

LivingThings.ТС

import dog = require('./dog')
import tree = require('./tree')

export = {
    dog: dog,
    tree: tree
}

главная.ТС

import LivingThings = require('./LivingThings');
console.log(LivingThings.Tree)
console.log(LivingThings.Dog)

идея заключается в том, что ваш модуль сам не должен заботиться / знать, что они участвуют в пространстве имен, но это предоставляет ваш API потребителю компактным, разумным способом, который является агностиком для какого типа модульной системы вы используете для проекта.

малое оскудение Альбинофренческого ответа:

базы.ТС

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

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

собака.ТС

import * as b from './base';

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

вещи.ТС

import { Dog } from './dog'

namespace things {
  export const dog = Dog;
}

export = things;

главная.ТС

import * as things from './things';

console.log(things.dog);

ОП я с тобой мужик. опять же, нет ничего плохого в этом ответе С 300 + голосами, но мое мнение:

  1. что плохого в том, чтобы помещать классы в свои уютные теплые собственные файлы индивидуально? Я имею в виду, что это сделает вещи выглядят намного лучше, верно? (или кто-то просто как 1000-строчный файл для всех моделей)

  2. Итак, если первый будет достигнут, мы должны импортировать импорт импорта... импорт только в каждый из файлов модели как человек, srsly, файл модели, a .D. TS файл, почему там так много *s? это должно быть просто, аккуратно, и все. Зачем мне нужен импорт туда? зачем? C# получил пространства имен не просто так.

  3. и к тому времени, вы буквально используете "имена файлов.ТС" в качестве идентификаторов. В качестве идентификаторов... Приходите на его 2017 сейчас, и мы все еще делаем это? Я возвращаюсь на Марс и сплю еще 1000 лет.

Так что, к сожалению, мой ответ: Нет, вы не можете сделать "пространство имен" вещь функциональная, если вы не используете все эти импорт или использование этих имен файлов в качестве идентификаторов (что я думаю, действительно глупо). Другой вариант: поместите все эти зависимости в поле с именем filenameasidentifier.ТС и использовать

export namespace(or module) boxInBox {} .

оберните их, чтобы они не пытались получить доступ к другим классам с тем же именем, когда они просто пытаются получить ссылку из класса, сидящего прямо над ними.

несколько вопросов / комментариев, которые я видел вокруг этой темы, звучат для меня так, как будто человек использует Namespace где они означают "псевдоним модуля". Как отметил Райан Кавано в одном из своих комментариев, Вы можете иметь модуль "обертки" для реэкспорта нескольких модулей.

если вы действительно хотите импортировать все это из одного и того же имени/псевдонима модуля, объедините модуль-оболочку с отображением путей в вашем 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 ...
    ]
}

собака.ТС

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

дерево.ТС

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

некромантии.
Если я правильно понимаю, вы спрашиваете, как иметь все ваши классы в одном отдельном файле, сохраняя при этом одно пространство имен для них всех.

поскольку никто, кажется, не имеет хорошего решения-вот идея простого решения, которое даже не включает в себя typescript: это решение называется залпом.

Просто поместите все свои классы, которые должны быть в одном пространстве имен в одну папку (полезно для организации кода в любом случае). Затем добавьте gulp-задачу, которая объединяет все файлы в этом каталоге в один файл (gulp-concat). Затем добавьте пространство имен с тем же именем, что и верхний каталог, затем добавьте объединенные файлы, затем добавьте закрывающую скобку и сохраните в один файл.
Сделанный.
Затем добавьте gulp-задачу, которая следит за изменениями (и добавлениями/удалениями) в том же каталоге. При изменении / добавлении активируйте функцию concat.

Теперь у вас есть все классы в одном файле, и один файл, который содержит все классы в одном пространстве имен.
Пример кода-по строкам:

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']);
});

есть глоток добавить-добавить модуль здесь: 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/'));
});

наконец, установите watcher для запуска при загрузке решения, и все готово.

Comments

    Ничего не найдено.