Spec-Zone.ru › TypeScript 5.1

Пространства имён

Примечание о терминологии: Важно отметить, что в TypeScript 1.5 изменилась номенклатура. «Внутренние модули» теперь называются «пространствами имён». «Внешние модули» теперь просто «модули», чтобы соответствовать терминологии ECMAScript 2015, (а именно, что module X { эквивалентно теперь предпочтительному namespace X {).

В этой статье описаны различные способы организации кода с использованием пространств имён (ранее «внутренние модули») в TypeScript. Как мы уже упоминали в примечании о терминологии, «внутренние модули» теперь называются «пространствами имён». Кроме того, везде, где использовался ключевой module при объявлении внутреннего модуля, теперь можно и следует использовать ключевое слово namespace. Это поможет избежать путаницы у новых пользователей, перегруженных похожими по написанию терминами.

Первые шаги

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

Валидаторы в одном файле

interface StringValidator {
  isAcceptable(s: string): boolean;
}

let lettersRegexp = /^[A-Za-z]+$/;
let numberRegexp = /^[0-9]+$/;

class LettersOnlyValidator implements StringValidator {
  isAcceptable(s: string) {
    return lettersRegexp.test(s);
  }
}

class ZipCodeValidator implements StringValidator {
  isAcceptable(s: string) {
    return s.length === 5 && numberRegexp.test(s);
  }
}

// Some samples to try
let strings = ["Hello", "98052", "101"];

// Validators to use
let validators: { [s: string]: StringValidator } = {};
validators["ZIP code"] = new ZipCodeValidator();
validators["Letters only"] = new LettersOnlyValidator();

// Show whether each string passed each validator
for (let s of strings) {
  for (let name in validators) {
    let isMatch = validators[name].isAcceptable(s);
    console.log(`'${s}' ${isMatch ? "matches" : "does not match"} '${name}'.`);
  }
}

Пространства имён

По мере добавления новых валидаторов нам понадобится схема организации, чтобы отслеживать наши типы и не беспокоиться о конфликтах имён с другими объектами. Вместо того, чтобы помещать множество разных имён в глобальное пространство имён, давайте упакуем наши объекты в пространство имён.

В этом примере мы переместим все сущности, связанные с валидаторами, в пространство имён Validation. Поскольку мы хотим, чтобы интерфейсы и классы здесь были видны снаружи пространства имён, мы предваряем их ключевым словом export. Наоборот, переменные lettersRegexp и numberRegexp являются деталями реализации, поэтому они не экспортируются и не будут видны коду вне пространства имён. В тестовом коде в нижней части файла теперь нам необходимо квалифицировать имена типов при использовании их вне пространства имён, например, Validation.LettersOnlyValidator.

Валидаторы в пространстве имён

namespace Validation {
  export interface StringValidator {
    isAcceptable(s: string): boolean;
  }

  const lettersRegexp = /^[A-Za-z]+$/;
  const numberRegexp = /^[0-9]+$/;

  export class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
      return lettersRegexp.test(s);
    }
  }

  export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
      return s.length === 5 && numberRegexp.test(s);
    }
  }
}

// Some samples to try
let strings = ["Hello", "98052", "101"];

// Validators to use
let validators: { [s: string]: Validation.StringValidator } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();

// Show whether each string passed each validator
for (let s of strings) {
  for (let name in validators) {
    console.log(
      `"${s}" - ${
        validators[name].isAcceptable(s) ? "matches" : "does not match"
      } ${name}`
    );
  }
}

Разделение по файлам

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

Пространства имён в нескольких файлах

Здесь мы разделим пространство имён Validation на множество файлов. Несмотря на то, что файлы отдельные, они могут каждый вносить свой вклад в одно и то же пространство имён и могут быть использованы так, как если бы они все были определены в одном месте. Поскольку между файлами есть зависимости, мы добавим теги ссылок, чтобы сообщить компилятору об отношениях между файлами. Тестовый код при этом останется без изменений.

Validation.ts
namespace Validation {
  export interface StringValidator {
    isAcceptable(s: string): boolean;
  }
}
LettersOnlyValidator.ts
/// <reference path="Validation.ts" />
namespace Validation {
  const lettersRegexp = /^[A-Za-z]+$/;
  export class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
      return lettersRegexp.test(s);
    }
  }
}
ZipCodeValidator.ts
/// <reference path="Validation.ts" />
namespace Validation {
  const numberRegexp = /^[0-9]+$/;
  export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
      return s.length === 5 && numberRegexp.test(s);
    }
  }
}
Test.ts
/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />
/// <reference path="ZipCodeValidator.ts" />

// Some samples to try
let strings = ["Hello", "98052", "101"];

// Validators to use
let validators: { [s: string]: Validation.StringValidator } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();

// Show whether each string passed each validator
for (let s of strings) {
  for (let name in validators) {
    console.log(
      `"${s}" - ${
        validators[name].isAcceptable(s) ? "matches" : "does not match"
      } ${name}`
    );
  }
}

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

Во-первых, мы можем использовать конкатенированный вывод с помощью опции outFile для компиляции всех входных файлов в один выходной файл JavaScript:

tsc --outFile sample.js Test.ts

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

tsc --outFile sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts

В качестве альтернативы, мы можем использовать компиляцию по файлам (по умолчанию), чтобы выводить по одному файлу JavaScript для каждого входного файла. Если генерируется несколько файлов JS, нам нужно использовать теги <script> в нашей веб-странице, чтобы загрузить каждый сгенерированный файл в правильном порядке, например:

MyTestPage.html (фрагмент)
<script src="Validation.js" type="text/javascript" />
<script src="LettersOnlyValidator.js" type="text/javascript" />
<script src="ZipCodeValidator.js" type="text/javascript" />
<script src="Test.js" type="text/javascript" />

Псевдонимы

Другой способ упростить работу с пространствами имён — использовать import q = x.y.z для создания более коротких имён для часто используемых объектов. Не следует путать с синтаксисом import x = require("name"), используемым для загрузки модулей, этот синтаксис просто создаёт псевдоним для указанного символа. Вы можете использовать такие импорты (обычно называемые псевдонимами) для любого типа идентификатора, включая объекты, созданные из импортов модулей.

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

import polygons = Shapes.Polygons;
let sq = new polygons.Square(); // Same as 'new Shapes.Polygons.Square()'

Обратите внимание, что мы не используем ключевое слово require; вместо этого мы напрямую присваиваем квалифицированное имя символа, который импортируем. Это аналогично использованию var, но также работает с типом и значением пространства имён импортированного символа. Важно, что для значений import является отдельной ссылкой от исходного символа, поэтому изменения в псевдониме var не будут отражены в исходной переменной.

Работа с другими JavaScript-библиотеками

Для описания формы библиотек, написанных не на TypeScript, нам необходимо объявить API, предоставляемый библиотекой. Поскольку большинство JavaScript-библиотек предоставляют только несколько объектов верхнего уровня, пространства имён являются хорошим способом их представления.

Объявления, которые не определяют реализацию, называются «ambient». Обычно они определяются в файлах .d.ts. Если вы знакомы с C/C++, вы можете представить их как файлы .h. Давайте рассмотрим несколько примеров.

Пространства имён ambient

Популярная библиотека D3 определяет свою функциональность в глобальном объекте, называемом d3. Поскольку эта библиотека загружается через тег <script> (а не через менеджер модулей), её объявление использует пространства имён для определения её формы. Для того, чтобы компилятор TypeScript видел эту форму, мы используем объявление пространства имён ambient. Например, мы можем начать его писать так:

D3.d.ts (упрощённый фрагмент)
declare namespace D3 {
  export interface Selectors {
    select: {
      (selector: string): Selection;
      (element: EventTarget): Selection;
    };
  }

  export interface Event {
    x: number;
    y: number;
  }

  export interface Base extends Selectors {
    event: Event;
  }
}

declare var d3: D3.Base;

© 2012-2024 Microsoft
Licensed under the Apache License, Version 2.0.
https://www.typescriptlang.org/docs/handbook/namespaces.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API