TypeScript 2: pengetikan khusus untuk modul npm tanpa tipe

93

Setelah mencoba saran yang diposting di tempat lain , saya mendapati diri saya tidak dapat menjalankan proyek ketikan yang menggunakan modul NPM yang tidak diketik. Di bawah ini adalah contoh minimal dan langkah-langkah yang saya coba.

Untuk contoh minimal ini, kita akan menganggap bahwa lodashdefinisi tipe tidak ada. Karena itu, kami akan mengabaikan paket @types/lodashdan mencoba menambahkan file pengetikannya secara manual lodash.d.tske proyek kami.

Struktur folder

  • node_modules
    • lodash
  • src
    • foo.ts
  • pengetikan
    • adat
      • lodash.d.ts
    • global
    • index.d.ts
  • package.json
  • tsconfig.json
  • typings.json

Selanjutnya, file.

Mengajukan foo.ts

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

console.log('Weeee');

File lodash.d.tsdisalin langsung dari @types/lodashpaket aslinya .

Mengajukan index.d.ts

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

Mengajukan 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"
}

Mengajukan 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"
  ]
}

Mengajukan typings.json

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

Seperti yang Anda lihat, saya telah mencoba berbagai cara untuk mengimpor pengetikan:

  1. Dengan mengimpornya secara langsung foo.ts
  2. Oleh typingsproperti dipackage.json
  3. Dengan menggunakan typeRootsdalam tsconfig.jsondengan filetypings/index.d.ts
  4. Dengan menggunakan eksplisit typesdalamtsconfig.json
  5. Dengan memasukkan typesdirektori ditsconfig.json
  6. Dengan membuat typings.jsonfile kustom dan menjalankantypings install

Namun, ketika saya menjalankan Typecript:

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

Apa yang saya lakukan salah?

Jodiug
sumber

Jawaban:

209

Sayangnya hal-hal ini saat ini tidak didokumentasikan dengan baik, tetapi bahkan jika Anda dapat membuatnya berfungsi, mari kita bahas konfigurasi Anda sehingga Anda memahami apa yang dilakukan setiap bagian dan bagaimana hal itu berkaitan dengan bagaimana proses skrip dan memuat pengetikan.

Pertama mari kita bahas kesalahan yang Anda terima:

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

Kesalahan ini sebenarnya tidak berasal dari impor atau referensi Anda atau upaya Anda untuk menggunakan lodash di mana pun di file ts Anda. Alih-alih, ini berasal dari kesalahpahaman tentang cara menggunakan properti typeRootsdan types, jadi mari kita bahas lebih detail tentang itu.

Hal tentang typeRoots:[]dan types:[]properti adalah bahwa mereka BUKAN cara tujuan umum untuk memuat *.d.tsfile declaration ( ) arbitrer .

Kedua properti ini secara langsung terkait dengan fitur TS 2.0 baru yang memungkinkan deklarasi pengemasan dan pemuatan pengetikan dari paket NPM .

Ini sangat penting untuk dipahami, bahwa ini hanya bekerja dengan folder dalam format NPM (yaitu folder yang berisi package.json atau index.d.ts ).

Default untuk typeRootsadalah:

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

Secara default ini berarti bahwa skrip ketikan akan masuk ke node_modules/@typesfolder dan mencoba memuat setiap sub-folder yang ditemukan di sana sebagai paket npm .

Penting untuk dipahami bahwa ini akan gagal jika folder tidak memiliki struktur seperti paket npm.

Inilah yang terjadi dalam kasus Anda, dan sumber kesalahan awal Anda.

Anda telah mengganti typeRoot menjadi:

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

Artinya, skrip ketikan sekarang akan memindai ./typingsfolder untuk subfolder dan mencoba memuat setiap subfolder yang ditemukannya sebagai modul npm.

Jadi anggap saja Anda baru saja typeRootsmenyiapkan untuk diarahkan ./typingstetapi belum memiliki types:[]penyiapan properti apa pun . Anda mungkin akan melihat kesalahan ini:

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

Ini karena tscmemindai ./typingsfolder Anda dan menemukan sub-folder customdan global. Kemudian mencoba menafsirkan ini sebagai pengetikan tipe paket npm, tetapi tidak ada index.d.tsatau package.jsondi folder ini dan Anda mendapatkan kesalahan.

Sekarang mari kita bicara sedikit tentang types: ['lodash']properti yang Anda tetapkan. Apa fungsinya? Secara default, skrip ketikan akan memuat semua sub-folder yang ditemukannya dalam file typeRoots. Jika Anda menentukan types:properti, itu hanya akan memuat sub-folder tertentu.

Dalam kasus Anda, Anda menyuruhnya untuk memuat ./typings/lodashfolder tetapi tidak ada. Inilah mengapa Anda mendapatkan:

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

Jadi mari kita meringkas apa yang telah kita pelajari. Ketik 2.0 diperkenalkan typeRootsdan typesuntuk memuat file deklarasi yang dikemas dalam paket npm . Jika Anda memiliki pengetikan khusus atau satu d.tsfile longgar yang tidak terdapat dalam folder yang mengikuti konvensi paket npm, maka kedua properti baru ini bukanlah yang ingin Anda gunakan. Typecript 2.0 tidak benar-benar mengubah cara penggunaannya. Anda hanya perlu memasukkan file-file ini dalam konteks kompilasi Anda dengan salah satu dari banyak cara standar:

  1. Langsung memasukkannya ke dalam .tsfile: ///<reference path="../typings/custom/lodash.d.ts" />

  2. Termasuk ./typings/custom/lodash.d.tsdi dalam files: []properti Anda .

  3. Termasuk ./typings/index.d.tsdi files: []properti Anda (yang kemudian secara rekursif menyertakan pengetikan lainnya.

  4. Menambahkan ./typings/**keincludes:

Mudah-mudahan, berdasarkan diskusi ini Anda akan dapat mengetahui mengapa perubahan yang Anda lakukan terhadap tsconfig.jsonbarang-barang Anda berhasil kembali.

EDIT:

Satu hal yang lupa saya sebutkan adalah bahwa typeRootsdan typesproperti benar-benar hanya berguna untuk pemuatan otomatis deklarasi global.

Misalnya jika Anda

npm install @types/jquery

Dan Anda menggunakan tsconfig default, maka paket tipe jquery itu akan dimuat secara otomatis dan $akan tersedia di semua skrip Anda tanpa harus melakukan lebih jauh ///<reference/>atauimport

The typeRoots:[]properti dimaksudkan untuk menambahkan lokasi tambahan dari mana jenis paket akan dimuat frrom otomatis.

Kasus types:[]penggunaan utama properti adalah menonaktifkan perilaku pemuatan otomatis (dengan menyetelnya ke array kosong), lalu hanya mencantumkan jenis tertentu yang ingin Anda sertakan secara global.

Cara lain untuk memuat paket tipe dari berbagai typeRootsadalah dengan menggunakan ///<reference types="jquery" />direktif baru . Perhatikan typesalih - alih path. Sekali lagi, ini hanya berguna untuk file deklarasi global, biasanya file yang tidak berfungsi import/export.

Nah, inilah salah satu hal yang menyebabkan kebingungan typeRoots. Ingat, saya katakan itu typeRootstentang penyertaan global modul. Tetapi @types/folderjuga terlibat dalam resolusi modul standar (terlepas dari typeRootspengaturan Anda ).

Secara khusus, secara eksplisit mengimpor modul selalu melewati semua includes, excludes, files, typeRootsdan typespilihan. Jadi, saat Anda melakukannya:

import {MyType} from 'my-module';

Semua properti yang disebutkan di atas sepenuhnya diabaikan. Sifat-sifat yang relevan selama resolusi modul yang baseUrl, paths, dan moduleResolution.

Pada dasarnya, ketika menggunakan noderesolusi modul, itu akan mulai mencari untuk nama file my-module.ts, my-module.tsx, my-module.d.tsmulai dari folder yang ditunjuk oleh Anda baseUrlkonfigurasi.

Jika tidak menemukan file tersebut, maka ia akan mencari folder bernama my-moduledan kemudian mencari a package.jsondengan typingsproperti, jika ada package.jsonatau tidak ada typingsproperti di dalamnya yang memberi tahu file mana yang akan dimuat kemudian akan mencari di index.ts/tsx/d.tsdalam folder itu.

Jika itu masih tidak berhasil, ia akan mencari hal-hal yang sama di node_modulesfolder mulai dari Anda baseUrl/node_modules.

Selain itu, jika tidak menemukannya, ia akan mencari baseUrl/node_modules/@typessemua hal yang sama.

Jika masih tidak menemukan apa pun, ia akan mulai masuk ke direktori induk dan mencari node_modulesdan di node_modules/@typessana. Ini akan terus naik direktori sampai mencapai root sistem file Anda (bahkan mendapatkan modul-node di luar proyek Anda).

Satu hal yang ingin saya tekankan adalah resolusi modul sepenuhnya mengabaikan apa pun yang typeRootsAnda tetapkan. Jadi jika Anda mengkonfigurasi typeRoots: ["./my-types"], ini tidak akan dicari selama resolusi modul eksplisit. Ini hanya berfungsi sebagai folder tempat Anda dapat meletakkan file definisi global yang ingin Anda sediakan untuk seluruh aplikasi tanpa perlu mengimpor atau referensi lebih lanjut.

Terakhir, Anda dapat mengganti perilaku modul dengan pemetaan jalur (misalnya pathsproperti). Jadi misalnya, saya menyebutkan bahwa kebiasaan apa pun typeRootstidak dikonsultasikan saat mencoba menyelesaikan modul. Tetapi jika Anda suka, Anda dapat membuat perilaku ini terjadi sebagai berikut:

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

Apa yang dilakukannya adalah untuk semua impor yang cocok dengan sisi kiri, coba ubah impor seperti di sisi kanan sebelum mencoba untuk memasukkannya ( *sisi kanan mewakili string impor awal Anda. Misalnya jika Anda mengimpor:

import {MyType} from 'my-types';

Ini akan mencoba impor terlebih dahulu seolah-olah Anda telah menulis:

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

Dan kemudian jika tidak menemukannya akan mencoba lagi tanpa awalan (item kedua dalam larik hanya *yang berarti impor awal.

Jadi dengan cara ini Anda dapat menambahkan folder tambahan untuk mencari file deklarasi kustom atau bahkan .tsmodul kustom yang Anda inginkan import.

Anda juga dapat membuat pemetaan khusus untuk modul tertentu:

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

Ini akan membiarkan Anda melakukannya

import {MyType} from 'my-types';

Tapi kemudian baca jenis itu dari some/custom/folder/location/my-awesome-types-file.d.ts

dtabuenc.dll
sumber
1
Terima kasih atas jawaban rinci Anda. Ternyata solusinya bekerja secara terpisah, tetapi tidak tercampur dengan baik. Itu menjelaskan hal-hal jadi saya akan memberi Anda hadiah. Jika Anda dapat menemukan waktu untuk menjawab beberapa poin lagi, itu mungkin sangat membantu orang lain. (1) Apakah ada alasan bagi typeRoots untuk hanya menerima struktur folder tertentu? Sepertinya sewenang-wenang. (2) Jika saya mengubah typeRoots, skrip ketikan masih menyertakan folder @types di node_modules, bertentangan dengan spesifikasi. (3) Apa pathsdan apa bedanya includedengan tujuan pengetikan?
Jodiug
1
Saya mengedit jawabannya, beri tahu saya jika Anda memiliki pertanyaan lagi.
dtabuenc
1
Terima kasih, saya membaca sisanya dan wow. Alat peraga bagi Anda untuk menemukan semua ini. Sepertinya ada banyak perilaku kompleks yang tidak perlu terjadi di sini. Saya berharap Typecript akan membuat properti config mereka sedikit lebih terdokumentasi sendiri di masa mendatang.
Jodiug
1
Seharusnya ada tautan ke jawaban ini dari typescriptlang.org/docs/handbook/tsconfig-json.html
hgoebl
2
Bukankah contoh terakhir harus seperti itu "paths" :{ "my-types": ["some/custom/folder/location/my-awesome-types-file"] }?
Koen.
6

Edit: kedaluwarsa. Baca jawabannya di atas.

Saya masih tidak mengerti ini, tetapi saya menemukan solusinya. Gunakan yang berikut ini 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"
  ]
}

Hapus typings.jsondan semua yang ada di bawah folder typingskecuali lodash.d.ts. Hapus juga semua ///...referensi

Jodiug
sumber
3

"*": ["./types/*"] Baris ini di jalur tsconfig memperbaiki semuanya setelah perjuangan 2 jam.

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

jenis adalah nama folder, yang berada di samping node_module yaitu di tingkat folder klien (atau folder src ) types/third-party-lib/index.d.ts
index.d.ts memilikideclare module 'third-party-lib';

Catatan: Konfigurasi di atas adalah konfigurasi yang tidak lengkap, hanya untuk memberikan gambaran tentang tampilannya dengan jenis, jalur, sertakan dan kecualikan di dalamnya.

Uday Sravan K.
sumber
2

Saya tahu ini adalah pertanyaan lama, tetapi perkakas jenis skrip terus berubah. Saya pikir opsi terbaik saat ini hanya mengandalkan pengaturan jalur "sertakan" di tsconfig.json.

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

Secara default, kecuali Anda membuat perubahan tertentu, semua file * .ts dan semua * .d.ts di bawah src/akan disertakan secara otomatis. Saya pikir ini adalah cara termudah / terbaik untuk menyertakan file deklarasi tipe kustom tanpa penyesuaiantypeRoots dan types.

Referensi:

realharry
sumber