UMD
Un módulo UMD es aquel que puede usarse como módulo (a través de una importación) o como global (cuando se ejecuta en un entorno sin un cargador de módulos). Muchas bibliotecas populares, como Moment.js, están escritas de esta manera. Por ejemplo, en Node.js o usando RequireJS, escribirías:
tsimport moment = require("moment");console.log(moment.format());
mientras que en un entorno de navegador vanilla escribirías:
jsconsole.log(moment.format());
Identificando una biblioteca UMD
Los módulos UMD verifican la existencia de un entorno de cargador de módulos. Este es un patrón fácil de detectar que se parece a algo así:
js(function (root, factory) {if (typeof define === "function" && define.amd) {define(["libName"], factory);} else if (typeof module === "object" && module.exports) {module.exports = factory(require("libName"));} else {root.returnExports = factory(root.libName);}}(this, function (b) {
Si ves pruebas para typeof define, typeof window, o typeof module en el código de una biblioteca, especialmente en la parte superior del archivo, casi siempre es una biblioteca UMD.
La documentación para bibliotecas UMD también mostrará a menudo un ejemplo de “Uso en Node.js” mostrando require,
y un ejemplo de “Uso en el navegador” mostrando el uso de una etiqueta <script> para cargar el script.
Ejemplos de bibliotecas UMD
La mayoría de las bibliotecas populares ahora están disponibles como paquetes UMD. Los ejemplos incluyen jQuery, Moment.js, lodash y muchas más.
Plantilla
Hay tres plantillas disponibles para módulos,
module.d.ts, module-class.d.ts y module-function.d.ts.
Usa module-function.d.ts si tu módulo puede ser llamado como una función:
jsvar x = require("foo");// Nota: llamando a 'x' como una funciónvar y = x(42);
Asegúrate de leer la nota al pie “El impacto de ES6 en las Firmas de Llamada de Módulos”
Usa module-class.d.ts si tu módulo puede ser construido usando new:
jsvar x = require("bar");// Nota: usando el operador 'new' en la variable importadavar y = new x("hello");
La misma nota al pie aplica a estos módulos.
Si tu módulo no es llamable o construible, usa el archivo module.d.ts.
Complemento de Módulo o Complemento UMD
Un complemento de módulo cambia la forma de otro módulo (ya sea UMD o módulo).
Por ejemplo, en Moment.js, moment-range agrega un nuevo método range al objeto moment.
Para los propósitos de escribir un archivo de declaración, escribirás el mismo código ya sea que el módulo que se está cambiando sea un módulo simple o un módulo UMD.
Plantilla
Usa la plantilla module-plugin.d.ts.
Complemento Global
Un complemento global es código global que cambia la forma de algún global. Al igual que con los módulos modificadores globales, estos aumentan la posibilidad de conflictos en tiempo de ejecución.
Por ejemplo, algunas bibliotecas agregan nuevas funciones a Array.prototype o String.prototype.
Identificando complementos globales
Los complementos globales generalmente son fáciles de identificar a partir de su documentación.
Verás ejemplos que se ven así:
jsvar x = "hello, world";// Crea nuevos métodos en tipos incorporadosconsole.log(x.startsWithHello());var y = [1, 2, 3];// Crea nuevos métodos en tipos incorporadosconsole.log(y.reverseAndSort());
Plantilla
Usa la plantilla global-plugin.d.ts.
Módulos Modificadores Globales
Un módulo modificador global altera los valores existentes en el ámbito global cuando se importan.
Por ejemplo, podría existir una biblioteca que agregue nuevos miembros a String.prototype cuando se importe.
Este patrón es algo peligroso debido a la posibilidad de conflictos en tiempo de ejecución,
pero aún podemos escribir un archivo de declaración para él.
Identificando módulos modificadores globales
Los módulos modificadores globales generalmente son fáciles de identificar a partir de su documentación.
En general, son similares a los complementos globales, pero necesitan una llamada require para activar sus efectos.
Podrías ver documentación como esta:
js// Llamada 'require' que no usa su valor de retornovar unused = require("magic-string-time");/* o */require("magic-string-time");var x = "hello, world";// Crea nuevos métodos en tipos incorporadosconsole.log(x.startsWithHello());var y = [1, 2, 3];// Crea nuevos métodos en tipos incorporadosconsole.log(y.reverseAndSort());
Plantilla
Usa la plantilla global-modifying-module.d.ts.
Consumiendo Dependencias
Existen varios tipos de dependencias que tu biblioteca podría tener. Esta sección muestra cómo importarlas al archivo de declaración.
Dependencias de Bibliotecas Globales
Si tu biblioteca depende de una biblioteca global, usa una directiva /// <reference types="..." />:
ts/// <reference types="someLib" />function getThing(): someLib.thing;
Dependencias de Módulos
Si tu biblioteca depende de un módulo, usa una declaración import:
tsimport * as moment from "moment";function getThing(): moment;
Dependencias de bibliotecas UMD
Desde una Biblioteca Global
Si tu biblioteca global depende de un módulo UMD, usa una directiva /// <reference types:
ts/// <reference types="moment" />function getThing(): moment;
Desde una Biblioteca Módulo o UMD
Si tu biblioteca módulo o UMD depende de una biblioteca UMD, usa una declaración import:
tsimport * as someLib from "someLib";
¡No uses una directiva /// <reference para declarar una dependencia a una biblioteca UMD!
Notas al pie
Previniendo Conflictos de Nombres
Ten en cuenta que es posible definir muchos tipos en el ámbito global al escribir un archivo de declaración global. Desaconsejamos encarecidamente esto, ya que conduce a posibles conflictos de nombres irresolubles cuando muchos archivos de declaración están en un proyecto.
Una regla simple a seguir es declarar tipos solo dentro del espacio de nombres de la variable global que define la biblioteca. Por ejemplo, si la biblioteca define el valor global ‘cats’, deberías escribir:
tsdeclare namespace cats {interface KittySettings {}}
Pero no
ts// en el nivel superiorinterface CatsKittySettings {}
Esta guía también asegura que la biblioteca pueda ser trasladada a UMD sin romper los usuarios del archivo de declaración.
El impacto de ES6 en los Complementos de Módulos
Algunos complementos agregan o modifican exportaciones de nivel superior en módulos existentes. Si bien esto es legal en CommonJS y otros cargadores, los módulos ES6 se consideran inmutables y este patrón no será posible. Debido a que TypeScript es agnóstico al cargador, no hay aplicación en tiempo de compilación de esta política, pero los desarrolladores que intenten hacer la transición a un cargador de módulos ES6 deben ser conscientes de esto.
El impacto de ES6 en las Firmas de Llamada de Módulos
Muchas bibliotecas populares, como Express, se exponen como una función llamable cuando se importan. Por ejemplo, el uso típico de Express se ve así:
tsimport exp = require("express");var app = exp();
En los cargadores de módulos ES6, el objeto de nivel superior (aquí importado como exp) solo puede tener propiedades;
el objeto del módulo de nivel superior nunca es llamable.
La solución más común aquí es definir una exportación default para un objeto llamable/construible;
algunos shims de cargador de módulos detectarán automáticamente esta situación y reemplazarán el objeto de nivel superior con la exportación default.
Diseño del archivo de la biblioteca
El diseño de tus archivos de declaración debe reflejar el diseño de la biblioteca.
Una biblioteca puede constar de múltiples módulos, como
myLib+---- index.js+---- foo.js+---- bar+---- index.js+---- baz.js
These could be imported as
jsvar a = require("myLib");var b = require("myLib/foo");var c = require("myLib/bar");var d = require("myLib/bar/baz");
Your declaration files should thus be
@types/myLib+---- index.d.ts+---- foo.d.ts+---- bar+---- index.d.ts+---- baz.d.ts
ts// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]// Project: [~THE PROJECT NAME~]// Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>/*~ This template shows how to write a global plugin. *//*~ Write a declaration for the original type and add new members.*~ For example, this adds a 'toBinaryString' method with overloads to*~ the built-in number type.*/interface Number {toBinaryString(opts?: MyLibrary.BinaryFormatOptions): string;toBinaryString(callback: MyLibrary.BinaryFormatCallback,opts?: MyLibrary.BinaryFormatOptions): string;}/*~ If you need to declare several types, place them inside a namespace*~ to avoid adding too many things to the global namespace.*/declare namespace MyLibrary {type BinaryFormatCallback = (n: number) => string;interface BinaryFormatOptions {prefix?: string;padding: number;}}