TypeScript 2: пользовательские типизации для нетипизированного модуля npm

После проработки предложений, размещенных в других местах, я не могу получить проект typescript, который использует нетипизированный модуль NPM. Ниже приведен минимальный пример и шаги, которые я пробовал.

В этом минимальном примере мы будем притворяться, что lodash не имеет существующих определений типов. Таким образом, мы проигнорируем пакет @types/lodash и попробуем вручную добавить его файл типизации lodash.d.ts в наш проект.

Структура папок

  • node_modules
    • lodash
  • ЦСИ
    • foo.ts
  • типизации
    • заказ
      • lodash.d.ts
    • глобальный
    • index.d.ts
  • package.json
  • tsconfig.json
  • typings.json

Далее, файлы.

Файл foo.ts

///<reference path="../typings/custom/lodash.d.ts" />
import * as lodash from 'lodash';

console.log('Weeee');

Файл lodash.d.ts копируется непосредственно из исходного пакета @types/lodash.

Файл index.d.ts

/// <reference path="custom/lodash.d.ts" />
/// <reference path="globals/lodash/index.d.ts" />

Файл package.json

{
  "name": "ts",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "typings": "./typings/index.d.ts",
  "dependencies": {
    "lodash": "^4.16.4"
  },
  "author": "",
  "license": "ISC"
}

Файл tsconfig.json

{
  "compilerOptions": {
    "target": "ES6",
    "jsx": "react",
    "module": "commonjs",
    "sourceMap": true,
    "noImplicitAny": true,
    "experimentalDecorators": true,
    "typeRoots" : ["./typings"],
    "types": ["lodash"]
  },
  "include": [
    "typings/**/*",
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts"
  ]
}

Файл typings.json

{
    "name": "TestName",
    "version": false,
    "globalDependencies": {
        "lodash": "file:typings/custom/lodash.d.ts"
    }
}

Как вы можете видеть, я пробовал много разных способов импорта титров:

  • Прямым импортом в foo.ts
  • С помощью свойства typings в package.json
  • Используя typeRoots в tsconfig.json с файлом typings/index.d.ts
  • Используя явный types в tsconfig.json
  • Включив каталог types в tsconfig.json
  • Создав собственный файл typings.json и запустив typings install

Тем не менее, когда я запускаю Typescript:

E:\temp\ts>tsc
error TS2688: Cannot find type definition file for 'lodash'.

Что я делаю неправильно?

Ответ 1

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

Сначала отпустите полученную ошибку:

error TS2688: Cannot find type definition file for 'lodash'.

Эта ошибка на самом деле не связана с вашими импортами или ссылками или вашей попыткой использовать lodash в любом месте ваших ts файлов. Скорее, это происходит из-за непонимания того, как использовать свойства typeRoots и types, поэтому давайте немного подробнее о них.

Дело в свойствах typeRoots:[] и types:[] заключается в том, что они являются НЕ универсальными способами загрузки файлов с произвольным объявлением (*.d.ts).

Эти два свойства напрямую связаны с новой функцией TS 2.0, которая позволяет упаковывать и загружать объявления ввода из пакетов NPM.

Это очень важно понять, что они работают только с папками в формате NPM (т.е. папке, содержащей package.json или index.d.ts).

По умолчанию для typeRoots:

{
   "typeRoots" : ["node_modules/@types"]
}

По умолчанию это означает, что typescript перейдет в папку node_modules/@types и попытается загрузить каждую подпапку, найденную там как пакет npm.

Важно понимать, что это не удастся, если папка не имеет структуры, похожей на npm.

Это то, что происходит в вашем случае, и источник вашей первоначальной ошибки.

Вы переключили typeRoot следующим образом:

{
    "typeRoots" : ["./typings"]
}

Это означает, что typescript теперь сканирует папку ./typings для вложенных папок и пытается загрузить каждую подпапку, которую он находит в качестве модуля npm.

Итак, давайте притвориться, что вы только что установили typeRoots, чтобы указать на ./typings, но еще не имели настройки свойств types:[]. Вероятно, вы увидите эти ошибки:

error TS2688: Cannot find type definition file for 'custom'.
error TS2688: Cannot find type definition file for 'global'.

Это потому, что tsc сканирует вашу папку ./typings и находит подпапки custom и global. Затем он пытается интерпретировать их как типизацию типа пакета npm, но в этих папках нет index.d.ts или package.json, поэтому вы получаете ошибку.

Теперь поговорим немного о свойстве types: ['lodash'], которое вы задаете. Что это делает? По умолчанию typescript загрузит подпапки все, найденные в вашем typeRoots. Если вы укажете свойство types:, оно будет загружать только эти определенные подпапки.

В вашем случае вы сообщаете ему, чтобы загрузить папку ./typings/lodash, но она не существует. Вот почему вы получаете:

error TS2688: Cannot find type definition file for 'lodash'

Итак, давайте обобщим то, что мы узнали. typescript 2.0 ввел typeRoots и types для загрузки файлов декларации, упакованных в пакеты npm. Если у вас есть настраиваемые типизации или отдельные свободные файлы d.ts, которые не содержатся в папке, следующей за пакетами пакетов npm, эти два новых свойства не являются тем, что вы хотите использовать. typescript 2.0 действительно не изменяет, как они будут потребляться. Вам просто нужно включить эти файлы в свой контекст компиляции одним из многих стандартных способов:

  • Непосредственно включив его в файл .ts: ///<reference path="../typings/custom/lodash.d.ts" />

  • Включение ./typings/custom/lodash.d.ts в ваше свойство files: [].

  • Включение ./typings/index.d.ts в ваше свойство files: [] (которое затем рекурсивно включает в себя другие типы.

  • Добавление ./typings/** в ваш includes:

Надеемся, что на основе этой дискуссии вы сможете узнать, почему изменения, которые вы с ума со своим tsconfig.json, снова заработали.

EDIT:

Одна вещь, о которой я забыл упомянуть, заключается в том, что свойства typeRoots и types действительно полезны только для загрузки автоматической глобальных деклараций.

Например, если вы

npm install @types/jquery

И вы используете tsconfig по умолчанию, тогда этот пакет типов jquery будет загружен автоматически, и $ будет доступен по всем вашим скриптам, не имея необходимости делать дальнейшие ///<reference/> или import

Свойство typeRoots:[] предназначено для добавления дополнительных мест, откуда автоматически загружаются пакеты типов.

Основной прецедент использования types:[] - это отключить автоматическое поведение загрузки (путем установки его в пустой массив), а затем только перечисление конкретных типов, которые вы хотите включить глобально.

Другим способом загрузки пакетов типов из различных typeRoots является использование новой директивы ///<reference types="jquery" />. Обратите внимание на types вместо path. Опять же, это полезно только для файлов глобальной декларации, обычно для тех, которые не выполняют import/export.

Теперь вот одна из вещей, которая вызывает путаницу с typeRoots. Помните, я сказал, что typeRoots - это глобальное включение модулей. Но @types/folder также участвует в стандартном модульном разрешении (независимо от вашей настройки typeRoots).

В частности, явно импортирование модулей всегда обходит все опции includes, excludes, files, typeRoots и types. Поэтому, когда вы делаете:

import {MyType} from 'my-module';

Все вышеупомянутые свойства полностью игнорируются. Соответствующие свойства при разрешении модуля: baseUrl, paths и moduleResolution.

В принципе, при использовании разрешения модуля node он начнет поиск имени файла my-module.ts, my-module.tsx, my-module.d.ts, начиная с папки, на которую указывает ваша конфигурация baseUrl.

Если он не найдет файл, он будет искать папку с именем my-module и затем выполнить поиск package.json с помощью свойства typings, если есть свойство package.json или no typings внутри которого указывается, какой файл для загрузки он будет искать index.ts/tsx/d.ts в этой папке.

Если это все еще не удастся, он будет искать эти же вещи в папке node_modules, начиная с вашего baseUrl/node_modules.

Кроме того, если он не найдет их, он будет искать baseUrl/node_modules/@types для всех тех же вещей.

Если он все равно ничего не найдет, он начнет переходить в родительский каталог и будет искать node_modules и node_modules/@types там. Он будет продолжать подниматься по каталогам, пока не достигнет корня вашей файловой системы (даже получив node -модули вне вашего проекта).

Одна вещь, которую я хочу подчеркнуть, заключается в том, что разрешение модуля полностью игнорирует любой установленный typeRoots. Поэтому, если вы настроили typeRoots: ["./my-types"], это не будет проверяться при явном разрешении модуля. Он служит только в качестве папки, в которую вы можете поместить файлы глобального определения, которые хотите сделать доступными для всего приложения, без дополнительной необходимости импорта или ссылки.

Наконец, вы можете переопределить поведение модуля с помощью сопоставлений пути (т.е. свойство paths). Так, например, я упомянул, что при попытке разрешить модуль не запрашивается какой-либо пользовательский typeRoots. Но если вам нравится, вы можете сделать это так:

"paths" :{
     "*": ["my-custom-types/*", "*"]
 }

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

import {MyType} from 'my-types';

Сначала попробуйте импорт, как если бы вы написали:

import {MyType} from 'my-custom-types/my-types'

И тогда, если он не нашел, он попытается снова с префиксом (второй элемент в массиве - это просто *, что означает начальный импорт.

Таким образом, вы можете добавить дополнительные папки для поиска пользовательских файлов декларации или даже настраиваемых модулей .ts, которые вы хотите иметь import.

Вы также можете создавать настраиваемые сопоставления для определенных модулей:

"paths" :{
   "*": ["my-types", "some/custom/folder/location/my-awesome-types-file"]
 }

Это позволит вам сделать

import {MyType} from 'my-types';

Но затем прочитайте эти типы из some/custom/folder/location/my-awesome-types-file.d.ts

Ответ 2

Изменить: устаревший. Прочтите ответ выше.

Я все еще не понимаю этого, но нашел решение. Используйте tsconfig.json:

{
  "compilerOptions": {
    "target": "ES6",
    "jsx": "react",
    "module": "commonjs",
    "sourceMap": true,
    "noImplicitAny": true,
    "experimentalDecorators": true,
    "baseUrl": ".",
    "paths": {
      "*": [
        "./typings/*"
      ]
    }
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts"
  ]
}

Удалите typings.json и все в папке typings кроме lodash.d.ts. Также удалите все ссылки ///...