TypeScript 2:型なしnpmモジュールのカスタム型付け


93

他の場所に投稿された提案を試した後、型付けされていないNPMモジュールを使用するtypescriptプロジェクトを実行できない。以下は最小限の例と私が試した手順です。

この最小限の例では、lodash既存の型定義がないものを使用します。そのため、パッケージは無視して、@types/lodash手動でタイピングファイルlodash.d.tsをプロジェクトに追加しようとします。

フォルダー構造

  • node_modules
    • ロダッシュ
  • src
    • 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"
    }
}

ご覧のとおり、タイピングをインポートするさまざまな方法を試しました。

  1. 直接インポートすることにより foo.ts
  2. typingsプロパティによってpackage.json
  3. ファイルで使用typeRootsするtsconfig.jsontypings/index.d.ts
  4. で明示的typesに使用するtsconfig.json
  5. typesディレクトリを含めることによりtsconfig.json
  6. カスタムtypings.jsonファイルを作成して実行するtypings install

それでも、Typescriptを実行すると:

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

何が悪いのですか?

回答:


202

残念ながら、これらのことは現在十分に文書化されていませんが、機能させることができたとしても、構成を調べて、各部分の動作と、typescriptが入力を処理およびロードする方法にどのように関連するかを理解しましょう。

最初に、受け取ったエラーについて説明します。

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

このエラーは実際には、インポートや参照、またはtsファイルのどこかにlodashを使用しようとしたことによるものではありません。むしろ、typeRootstypesプロパティの使い方の誤解から来ているので、それらについてもう少し詳しく見ていきましょう。

typeRoots:[]types:[]性質は、彼らがしていることですしないで任意の宣言(ロードするための汎用的な方法*.d.ts)ファイルを。

これら2つのプロパティは、NPMパッケージからのタイピング宣言のパッケージ化とロードを可能にする新しいTS 2.0機能に直接関連しています

これらは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フォルダをサブフォルダを見つけるcustomglobal。次に、これらをnpmパッケージタイプタイプとして解釈しようとしていますが、これらのフォルダーがないindex.d.tspackage.json、フォルダーにあるため、エラーが発生します。

次に、types: ['lodash']設定するプロパティについて少し話しましょう。これは何をしますか?デフォルトでは、typescript は内にあるすべてのサブフォルダーを読み込みますtypeRootstypes:プロパティを指定すると、それらの特定のサブフォルダーのみが読み込まれます。

あなたのケースでは、./typings/lodashフォルダをロードするように指示していますが、存在しません。これがあなたが得る理由です:

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

それでは、私たちが学んだことを要約しましょう。導入2.0活字体typeRootstypesにパッケージロード宣言ファイル用のNPMパッケージd.tsnpmパッケージ規則に従ってフォルダーに含まれていないカスタムタイプまたは単一のルースファイルがある場合、これらの2つの新しいプロパティは使用したくないものです。Typescript 2.0は、これらがどのように消費されるかを実際には変更しません。多くの標準的な方法の1つで、これらのファイルをコンパイルコンテキストに含める必要があります。

  1. .tsファイルに直接含める: ///<reference path="../typings/custom/lodash.d.ts" />

  2. ./typings/custom/lodash.d.tsあなたのfiles: []財産に含む。

  3. プロパティに含め./typings/index.d.tsますfiles: [](他の型を再帰的に含めます)。

  4. 追加./typings/**あなたへincludes:

うまくいけば、この議論に基づいて、あなたがあなたのtsconfig.json作ったものに対して行った変更が再び機能する理由を知ることができるでしょう。

編集:

言及し忘れたことの1つはtypeRootstypesプロパティはグローバル宣言の自動読み込みにのみ実際に役立つということです。

例えばあなたが

npm install @types/jquery

そして、デフォルトのtsconfigを使用している場合、そのjqueryタイプのパッケージは自動的にロードされ$、それ///<reference/>以上の操作を行わなくても、すべてのスクリプトで使用できます。import

このtypeRoots:[]プロパティは、タイプパッケージが自動的に読み込まれる場所を追加するためのものです。

types:[]プロパティの主なユースケースは、(空の配列に設定することで)自動ロード動作を無効にすることで、その後、あなただけは、グローバルに含める特定のタイプが一覧表示されます。

さまざまなパッケージからタイプパッケージをロードするもう1つの方法typeRootsは、新しい///<reference types="jquery" />ディレクティブを使用することです。のtypes代わりに注意してくださいpath。繰り返しますが、これはグローバル宣言ファイル、実行しない一般的なものに対してのみ有効ですimport/export

ここで、との混乱を引き起こすものの1つを示しtypeRootsます。覚えておいてください、それtypeRootsはモジュールのグローバルなインクルージョンについてです。しかし、@types/foldertypeRoots設定に関係なく)標準のモジュール解決にも関与しています。

具体的には、明示的にモジュールをインポートすると、常にすべてをバイパスし includesexcludesfilestypeRootsおよびtypesオプション。だからあなたがするとき:

import {MyType} from 'my-module';

上記のプロパティはすべて完全に無視されます。中に関連するプロパティモジュール解像度がありbaseUrlpathsおよびmoduleResolution

基本的には、使用するときにnodeモジュールの解像度を、それはファイル名の検索を開始しますmy-module.tsmy-module.tsxmy-module.d.tsフォルダから始まることはあなたによって指されbaseUrlた構成。

ファイルが見つからない場合は、という名前のフォルダーmy-moduleを検索package.jsonし、typingsプロパティを使用してを検索します。内部にプロパティがあるpackage.jsonか、またはない場合は、typings読み込むファイルを指定しindex.ts/tsx/d.tsて、そのフォルダー内で検索します。

それでもうまくいかない場合は、でnode_modules始まるフォルダで同じものを検索しますbaseUrl/node_modules

また、これらが見つからない場合はbaseUrl/node_modules/@types、同じものをすべて検索します。

それはまだ何も見つからなかった場合には、親ディレクトリに行き始めると検索しますnode_modulesと、node_modules/@typesそこに。ファイルシステムのルートに到達するまで、ディレクトリに移動し続けます(プロジェクトの外部にノードモジュールを取得する場合でも)。

私が強調したいことの一つは、モジュールの解決はtypeRootsあなたが設定したものを完全に無視するということです。そのため、を設定した場合typeRoots: ["./my-types"]、明示的なモジュール解決中には検索されません。これは、アプリケーション全体で使用できるようにするグローバル定義ファイルを配置できるフォルダとしてのみ機能し、インポートまたは参照する必要はありません。

最後に、モジュールの動作をパスマッピング(つまり、pathsプロパティ)でオーバーライドできます。したがって、たとえば、typeRootsモジュールを解決しようとするときに、カスタムは参照されないことを述べました。ただし、気に入った場合は、この動作を次のように行うことができます。

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

これは、左側に一致するすべてのインポートに対して行われます。右側のようにインポートを変更してから、インポートを試みてください(*右側は最初のインポート文字列を表します。たとえば、インポートする場合:

import {MyType} from 'my-types';

最初に、次のようにインポートを試みます。

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

そして、見つからなかった場合は、プレフィックスを付けずに再試行します(配列の2番目の項目*は、最初のインポートを意味します。

したがって、この方法で、追加のフォルダを追加して、カスタム宣言ファイルや.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


1
詳しい回答ありがとうございます。ソリューションは単独で機能することがわかりましたが、うまく混合できません。それは物事を明確にするので、私はあなたに賞金を差し上げます。さらにいくつかのポイントに答える時間を見つけることができれば、それは他の人々にとって本当に役立つでしょう。(1)typeRootsが特定のフォルダー構造のみを受け入れる理由はありますか?それは恣意的です。(2)typeRootsを変更しても、仕様とは逆に、typescriptにはnode_modulesに@typesフォルダーが含まれています。(3)とは何でpaths、どのように異なっているincludeタイピングの目的のために?
Jodiug、2016年

1
回答を編集しました。他にご不明な点がありましたらお知らせください。
dtabuenc 2016年

1
ありがとう、残りを読んですごい。このすべてを見つけるための小道具。ここでは、不必要に複雑な動作がたくさん行われているようです。Typescriptがそれらの設定プロパティを将来的にもう少し自己文書化することを願っています。
Jodiug


2
最後の例は次のようにすべきではありません"paths" :{ "my-types": ["some/custom/folder/location/my-awesome-types-file"] }か?
公園。

6

編集:古くなっています。上記の答えを読んでください。

私はまだこれを理解していませんが、解決策を見つけました。以下を使用してください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.jsontypingsを除くすべてのフォルダの下を削除しますlodash.d.ts。また、すべてを削除します///...参照ます


3

"*": ["./types/*"] tsconfigパスのこの行は、2時間の苦闘の後、すべてを修正しました。

{
  "compilerOptions": {
    "moduleResolution": "node",
    "strict": true,
    "baseUrl": ".",
    "paths": {
      "*": ["./types/*"]
    },
    "jsx": "react",
    "types": ["node", "jest"]
  },
  "include": [
    "client/**/*",
    "packages/**/*"
  ],
  "exclude": [
    "node_modules/**/*"
  ]
}

typesはフォルダー名で、node_moduleの横、つまりクライアントフォルダー(またはsrcフォルダー) のレベルにありますtypes/third-party-lib/index.d.ts
。index.d.tsdeclare module 'third-party-lib';

注:上記の設定は、タイプ、パス、インクルード、エクスクルードでどのように見えるかを示すために、不完全な設定です。


1

これは古い質問であることは承知していますが、typescriptツールは常に変化しています。この時点での最良のオプションは、tsconfig.jsonの「include」パス設定に依存しているだけだと思います。

  "include": [
        "src/**/*"
    ],

デフォルトでは、特定の変更を加えない限り、すべての* .tsおよび* .d.tsファイルsrc/が自動的に含まれます。これは、typeRootsとをカスタマイズせずにカスタム型宣言ファイルを含める最も簡単で最良の方法だと思いますtypes

参照:


0

上記の詳細な説明を拡張するために、最近の観察の一部を共有したいと思います。まず、VSコードは、物事をどのように実行する必要があるかについて異なる意見を持つことが多いことに注意することが重要です。

私が最近働いた例を見てみましょう:

src / app / components / plot-plotly / plot-plotly.component.ts:

/// <reference types="plotly.js" />
import * as Plotly from 'plotly.js';

VS Codeは次のことを訴える場合があります。 No need to reference "plotly.js", since it is imported. (no-reference import) tslint(1)

プロジェクトを開始すると、エラーなしでコンパイルされますが、その行を削除すると、開始時に次のエラーが表示されます。

ERROR in src/app/components/plot-plotly/plot-plotly.component.ts:19:21 - error TS2503: Cannot find namespace 'plotly'.

reference typesimportステートメントの後にディレクティブを配置した場合も、同じエラーメッセージが表示されます。

重要/// <reference types="plotly.js" />はタイプスクリプトファイルの前に置く必要があります!関連ドキュメントを参照してください:リンク

トリプルスラッシュディレクティブは、それらを含むファイルの先頭でのみ有効です。

tsconfig.jsonとtypeRootセクションのドキュメントを読むこともお勧めします:link

タイプパッケージは、index.d.tsと呼ばれるファイルを含むフォルダー、またはタイプフィールドを含むpackage.jsonを含むフォルダーです。

上記の参照ディレクティブは、次のシナリオで機能します。

types / plotly.js / index.d.ts:(リンク

  declare namespace plotly {
    export interface ...
  }

そして

tsconfig.json:

  "compilerOptions": {
    ...
    "typeRoots": [
      "types/",
      "node_modules/@types"
    ],
    ...  

:上記の設定では、2つの「plotly.js」は2つの異なるフォルダとファイル(ライブラリ/定義)を意味します。インポートは「node_modules / plotly.js」フォルダーに適用されます(npm install plotly.js)に適用されますが、参照はtypes / plotly.jsに適用されます。

私のプロジェクトがVSコードの不満と「2つの」plotly.jsのあいまいさを解決するために、私は次の設定で終わりました:

  • すべてのファイルは元の場所に残りました
  • tsconfig.json:
  "compilerOptions": {
    ...
    "typeRoots": [
      "./",
      "node_modules/@types"
    ],
    ...  
  • src / app / components / plot-plotly / plot-plotly.component.ts:
  /// <reference types="types/plotly.js" />
  import * as Plotly from 'plotly.js';

  plotDemo() {

  // types using plotly namespace
  const chunk: plotly.traces.Scatter = {
      x: [1, 2, 3, 4, 5],
      y: [1, 3, 2, 3, 1],
      mode: 'lines+markers',
      name: 'linear',
      line: { shape: 'linear' },
      type: 'scatter'
  };

  // module call using Plotly module import
  Plotly.newPlot(...);
  }

私のシステムのDevDeps:

  • "ts-node": "〜8.3.0"、
  • "tslint": "〜5.18.0"、
  • "typescript": "〜3.7.5"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.