Модули .d.ts
Сравнение JavaScript с примером DTS
Общие паттерны CommonJS
Модуль, использующий паттерны CommonJS, использует module.exports для описания экспортируемых значений. Например, вот модуль, который экспортирует функцию и числовую константу:
const maxInterval = 12;
function getArrayLength(arr) {
return arr.length;
}
module.exports = {
getArrayLength,
maxInterval,
}; Это можно описать следующим .d.ts:
export function getArrayLength(arr: any[]): number; export const maxInterval: 12;
Игровой плацдарм TypeScript может показать вам .d.ts эквивалент для кода JavaScript. Вы можете попробовать самостоятельно здесь.
Синтаксис .d.ts намеренно похож на синтаксис модулей ES. Модули ES были ратифицированы TC39 в 2015 году как часть ES2015 (ES6), хотя они были доступны через транспайлеры уже давно. Однако, если у вас есть база кода JavaScript, использующая модули ES:
export function getArrayLength(arr) {
return arr.length;
} Это имело бы следующий .d.ts эквивалент:
export function getArrayLength(arr: any[]): number;
Экспорт по умолчанию
В CommonJS вы можете экспортировать любое значение как экспорт по умолчанию, например, здесь модуль регулярных выражений:
module.exports = /hello( world)?/;
Что можно описать следующим .d.ts:
declare const helloWorld: RegExp; export default helloWorld;
Или число:
module.exports = 3.142;
declare const pi: number; export default pi;
Один из стилей экспорта в CommonJS — экспорт функции. Поскольку функция также является объектом, можно добавлять дополнительные поля, и они включаются в экспорт.
function getArrayLength(arr) {
return arr.length;
}
getArrayLength.maxInterval = 12;
module.exports = getArrayLength; Что можно описать следующим образом:
export default function getArrayLength(arr: any[]): number; export const maxInterval: 12;
Обратите внимание, что использование export default в ваших файлах .d.ts требует esModuleInterop: true, чтобы это работало. Если вы не можете использовать esModuleInterop: true в вашем проекте, например, когда вы отправляете PR в Definitely Typed, вам придется использовать синтаксис export= вместо этого. Этот более старый синтаксис сложнее в использовании, но работает везде. Вот как должен быть написан приведенный выше пример, используя export=:
declare function getArrayLength(arr: any[]): number;
declare namespace getArrayLength {
declare const maxInterval: 12;
}
export = getArrayLength; См. Модуль: Функции для получения подробной информации о работе этого и страницу справки по модулям.
Обработка множественных импортов
Существует множество способов импортировать модуль в современном потребительском коде:
const fastify = require("fastify");
const { fastify } = require("fastify");
import fastify = require("fastify");
import * as Fastify from "fastify";
import { fastify, FastifyInstance } from "fastify";
import fastify from "fastify";
import fastify, { FastifyInstance } from "fastify"; Для поддержки всех этих случаев код JavaScript должен фактически поддерживать все эти паттерны. Чтобы поддержать многие из этих паттернов, модуль CommonJS должен выглядеть примерно так:
class FastifyInstance {}
function fastify() {
return new FastifyInstance();
}
fastify.FastifyInstance = FastifyInstance;
// Allows for { fastify }
fastify.fastify = fastify;
// Allows for strict ES Module support
fastify.default = fastify;
// Sets the default export
module.exports = fastify; Типы в модулях
Вы можете захотеть предоставить тип для кода JavaScript, которого не существует
function getArrayMetadata(arr) {
return {
length: getArrayLength(arr),
firstObject: arr[0],
};
}
module.exports = {
getArrayMetadata,
}; Это можно описать следующим образом:
export type ArrayMetadata = {
length: number;
firstObject: any | undefined;
};
export function getArrayMetadata(arr: any[]): ArrayMetadata; Этот пример является хорошим случаем для использования универсальных типов, чтобы предоставить более подробную информацию о типах:
export type ArrayMetadata<ArrType> = {
length: number;
firstObject: ArrType | undefined;
};
export function getArrayMetadata<ArrType>(
arr: ArrType[]
): ArrayMetadata<ArrType>; Теперь тип массива распространяется на тип ArrayMetadata.
Экспортируемые типы могут затем повторно использоваться потребителями модулей с помощью import или import type в коде TypeScript или импорта JSDoc.
Пространства имен в коде модуля
Попытка описать временные отношения кода JavaScript может быть сложной. Когда синтаксис, похожий на модули ES, не предоставляет достаточно инструментов для описания экспортов, можно использовать namespaces.
Например, у вас могут быть достаточно сложные типы, чтобы вы решили сгруппировать их в пространстве имен внутри вашего .d.ts:
// This represents the JavaScript class which would be available at runtime
export class API {
constructor(baseURL: string);
getInfo(opts: API.InfoRequest): API.InfoResponse;
}
// This namespace is merged with the API class and allows for consumers, and this file
// to have types which are nested away in their own sections.
declare namespace API {
export interface InfoRequest {
id: string;
}
export interface InfoResponse {
width: number;
height: number;
}
} Чтобы понять, как работают пространства имен в файлах .d.ts прочитайте .d.ts подробный обзор.
Необязательное глобальное использование
Вы можете использовать export as namespace для объявления того, что ваш модуль будет доступен в глобальном пространстве имен в контекстах UMD:
export as namespace moduleName;
Пример справки
Чтобы дать вам представление о том, как все эти части могут объединиться, вот пример .d.ts с которого можно начать при создании нового модуля
// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
// Project: [~THE PROJECT NAME~]
// Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>
/*~ This is the module template file. You should rename it to index.d.ts
*~ and place it in a folder with the same name as the module.
*~ For example, if you were writing a file for "super-greeter", this
*~ file should be 'super-greeter/index.d.ts'
*/
/*~ If this module is a UMD module that exposes a global variable 'myLib' when
*~ loaded outside a module loader environment, declare that global here.
*~ Otherwise, delete this declaration.
*/
export as namespace myLib;
/*~ If this module exports functions, declare them like so.
*/
export function myFunction(a: string): string;
export function myOtherFunction(a: number): number;
/*~ You can declare types that are available via importing the module */
export interface SomeType {
name: string;
length: number;
extras?: string[];
}
/*~ You can declare properties of the module using const, let, or var */
export const myField: number; Структура файлов библиотеки
Структура ваших файлов объявлений должна отражать структуру библиотеки.
Библиотека может состоять из нескольких модулей, например
myLib
+---- index.js
+---- foo.js
+---- bar
+---- index.js
+---- baz.js Их можно импортировать как
var a = require("myLib");
var b = require("myLib/foo");
var c = require("myLib/bar");
var d = require("myLib/bar/baz"); Таким образом, ваши файлы объявлений должны быть
@types/myLib
+---- index.d.ts
+---- foo.d.ts
+---- bar
+---- index.d.ts
+---- baz.d.ts Тестирование ваших типов
Если вы планируете отправить эти изменения в DefinitelyTyped для использования всеми, мы рекомендуем:
- Создать новую папку в
node_modules/@types/[libname]- Создать
index.d.tsв этой папке и скопировать пример в неё- Выяснить, где ваш код использования модуля прерывается, и начать заполнять index.d.ts
- После завершения работы скопируйте DefinitelyTyped/DefinitelyTyped и следуйте инструкциям в файле README.
В противном случае
- Создайте новый файл в корне вашего исходного дерева:
[libname].d.ts- Добавьте
declare module "[libname]" { }- Добавьте шаблон внутрь фигурных скобок declare module и выясните, где прерывается использование
© 2012-2023 Microsoft
Licensed under the Apache License, Version 2.0.
https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-d-ts.html