Typescript: TS4023: Variabel yang diekspor 'X' memiliki atau menggunakan nama 'Y' dari modul eksternal 'a / file / path' tetapi tidak dapat dinamai

Dibuat pada 18 Nov 2015  ·  24Komentar  ·  Sumber: microsoft/TypeScript

Saya mencoba membuat satu set modul TypeScript eksternal sebagai berikut:

// ClassA.ts
export default class ClassA {
  public method() { return true; } 
}

// ModuleB.ts
import ClassA from './ClassA';
var moduleB = {
  ClassA: ClassA
};
export default moduleB;

// ModuleA.ts
import moduleB from './ModuleB';
var moduleA = {
  moduleB: moduleB
}
export default moduleA;

Idenya adalah ini akan dikompilasi dan disediakan sebagai perpustakaan. Rencananya adalah bahwa konsumen perpustakaan akan membuat instance dari ClassA sebagai berikut:

import moduleA from './ModuleA';
var anInstance = moduleA.moduleB.ClassA();

Ini terlihat seolah-olah sedang dikompilasi ke JS yang diharapkan, tetapi saya mendapatkan kesalahan kompiler kesalahan yang saya perjuangkan untuk menemukan informasi lebih lanjut.

Using tsc v1.6.2
.../src/ModuleA.ts(3,5): error TS4023: Exported variable 'moduleA' has or is using name 'ClassA' from external module ".../src/ClassA" but cannot be named.

Adakah yang bisa menjelaskan masalah ini di sini? Apakah yang saya coba lakukan masuk akal?

Question

Komentar yang paling membantu

FWIW biasanya Anda akan mendapatkan jawaban yang lebih cepat di Stack Overflow, tetapi kami menjawab pertanyaan yang disusun dengan baik di sini.

Masalahnya di sini adalah Anda menggunakan flag --declaration , tetapi belum menyediakan cara bagi kompilator untuk melakukan tugasnya.

Ketika mencoba mengeluarkan ModuleA.d.ts , kompilator perlu menulis literal tipe objek (mis. { moduleB: { classA: *mumble?* } } ) mewakili bentuk modul. Tetapi tidak ada nama dalam lingkup yang merujuk langsung ke classA , jadi itu jenis "tidak dapat dinamai" dan ada kesalahan.

Jika Anda menambahkan import dari ClassA menjadi ModuleA.ts , kesalahan tersebut akan hilang.

Semua 24 komentar

Oh - jika ini bukan tempat yang cocok untuk menanyakan pertanyaan ini. Tolong beri tahu saya dan saya akan dengan senang hati mempostingnya di media lain.

FWIW biasanya Anda akan mendapatkan jawaban yang lebih cepat di Stack Overflow, tetapi kami menjawab pertanyaan yang disusun dengan baik di sini.

Masalahnya di sini adalah Anda menggunakan flag --declaration , tetapi belum menyediakan cara bagi kompilator untuk melakukan tugasnya.

Ketika mencoba mengeluarkan ModuleA.d.ts , kompilator perlu menulis literal tipe objek (mis. { moduleB: { classA: *mumble?* } } ) mewakili bentuk modul. Tetapi tidak ada nama dalam lingkup yang merujuk langsung ke classA , jadi itu jenis "tidak dapat dinamai" dan ada kesalahan.

Jika Anda menambahkan import dari ClassA menjadi ModuleA.ts , kesalahan tersebut akan hilang.

Hai Ryan - terima kasih atas saran RE stack overflow dan sarannya - menambahkan impor ke ModuleA membuat kompilasi seperti yang diharapkan.

Sedikit konteks di sini - tujuan saya di sini memang untuk menghasilkan deklarasi untuk modul eksternal saya. (Sesuatu yang saya percaya belum cukup berfungsi? # 5039?) Impor ini mungkin sepenuhnya diperlukan tetapi Rasanya agak aneh untuk ModuleA perlu mengimpor simbol yang sudah diekspor moduleB. Hasilnya adalah setiap kali saya menambahkan ekspor objek ModuleB , saya perlu menambahkan impor ke ModuleA - baik secara langsung:

import {moduleB} from './moduleB';
import {ClassA} from './ClassA';

Atau melalui ModuleB (dalam upaya untuk mengurangi jalur yang ditentukan untuk ClassA di kedua tempat):

import {moduleB, ClassA} from './moduleB';

Saya tahu sangat sedikit tentang kompiler TS - jadi mungkin yang saya katakan sama sekali tidak realistis, tetapi pada awalnya berpikir sepertinya kompilator dapat menyimpulkan bahwa ModuleB mengekspor objek dengan simbol N di atasnya, jadi ModuleA membutuhkan simbol-simbol itu? Informasi impor untuk ModuleB sudah ada - dapatkah ModuleA menggunakannya? Atau apakah itu tidak mungkin karena impor untuk ClassA sepenuhnya internal ke ModuleB , jadi tidak mungkin kompilator dapat menyimpulkan tipe untuk ClassA saat membuat definisi untuk ModuleA ?

Sekali lagi terima kasih telah meluangkan waktu untuk menjawab. Saya telah menyelesaikan masalah saya sekarang jadi yang di atas terutama adalah rasa ingin tahu - tidak perlu terburu-buru untuk menjawab.

Saya tidak yakin persis apa yang Anda katakan. Seperti apa tampilan ModuleA.d.ts dalam proposal ini?

Kompilator tidak akan menambahkan dependensi (yaitu pernyataan impor) yang tidak ada dalam kode pengguna ketika ia mengeluarkan deklarasi. kesalahan yang Anda peroleh berarti bahwa kompilator mencoba untuk menulis anotasi tipe untuk deklarasi yang diekspor tetapi tidak bisa. ini dapat memiliki salah satu dari dua alasan, apakah namanya tidak dapat diakses, yaitu tidak diimpor dalam modul saat ini, atau ada deklarasi yang membayangi deklarasi asli.

dalam kedua kasus, pekerjaan Anda adalah menambahkan anotasi tipe eksplisit, jika Anda menambahkan anotasi tipe apa pun, itu akan dipancarkan kata demi kata dalam output; pilihan lainnya adalah untuk memastikan nama dapat diakses, yaitu Anda memiliki impor ke modul, dan Anda memahami apa artinya bagi pengguna Anda yang mengimpor modul Anda.

@mhegazy berkata:

Kompilator tidak akan menambahkan dependensi (yaitu pernyataan impor) yang tidak ada dalam kode pengguna ketika ia mengeluarkan deklarasi.

Masalahnya adalah saya tidak selalu membutuhkan pernyataan impor dalam kode (jelas, karena ini berfungsi tanpa --declarations ), dan menyertakannya berisik, menyebabkan hal-hal seperti tslint mengeluh tentang "impor tidak terpakai". Saya dapat melihat mengapa Anda tidak ingin kompiler menambahkan dependensi ke javascript yang dipancarkan, tetapi apa masalah dengan menambahkannya ke deklarasi pemancar?

jangan ragu untuk mencatat masalah untuk melacak saran ini. import rasional was adalah deklarasi ketergantungan, dan compiler tidak boleh membuatnya untuk Anda kecuali Anda menginstruksikannya untuk melakukannya.

Ini bisa menimbulkan konsekuensi yang lebih dalam.

Pertimbangkan moduleA -> moduleB -> moduleC -> moduleD .

moduleB adalah salah satu yang perlu dilakukan import { ABC } from 'moduleA' untuk mengatasi masalah ini.

Ketika moduleC menggunakan moduleB dan mengekspor tanda tangannya, ia kembali gagal untuk dikompilasi karena moduleB membutuhkan ABC dari moduleA tapi kali ini moduleB tidak mengekspornya.

Ini berarti:

  1. moduleC harus memiliki ketergantungan keras moduleA dan impor ABC
  2. moduleB tidak hanya perlu mengimpor tetapi juga mengekspor ulang ABC dan kemudian moduleC mengimpornya.

Jika moduleD melakukan hal serupa, maka pada dasarnya Anda perlu mengetahui seluruh rantai dependensi.

Saya tidak dapat menguji ini untuk membuktikan bahwa ini masalahnya karena saya menggunakan typings dan ada masalah yang memblokir saya jadi: https://github.com/typings/typings/issues/625 ~~

EDIT: Saya dapat mereproduksinya dan memang moduleD perlu mereferensikan moduleA untuk mengimpor.
Dalam contoh saya:

  • moduleA => redux
  • moduleB => redux-thunk
  • moduleC => kode kustom di atas redux-thunk
  • moduleD => beberapa perpustakaan
  • ABC => Dispatch antarmuka di redux

Saya memiliki masalah yang sama yang dijelaskan oleh @unional

Harap buka kembali masalah ini, masalah yang diuraikan oleh @unional sangat realistis dan ini membuatnya sangat sulit untuk menambahkan pustaka perantara / penolong di atas pustaka saat menggunakan jenis yang sama dengan modul asli yang kami ekspor ulang.

Masalah https://github.com/Microsoft/TypeScript/issues/9944 trek menambahkan impor pada fase mengeluarkan pernyataan.

Terima kasih!

Masalah dengan menambahkan import adalah bahwa dengan noUnusedLocals compiler akan mengeluh tentang tipe yang tidak digunakan. Saya dapat menambahkan anotasi tipe eksplisit, tetapi kemudian saya tidak mendapatkan kesimpulan. Contoh:

class Whatever {
  fetch(uri: string): Promise<void> { }
  ensureFetched = MemoizedFunction<(uri: string) => Promise<void>> = memoize((uri: string) => this.fetch(uri))
}

Saya ingin menghilangkan anotasi tipe untuk ensureFetched

Saya menemukan solusi untuk ini:
di tsconfig: include: [ ..., "node_modules/@your_scope/your_library" ]
semoga berhasil dan bersenang-senang: smiley:

@ salim7 ini memperlambat waktu kompilasi Anda (karena Anda sedang mengkompilasi ulang perpustakaan yang seharusnya sudah dikompilasi) dan memaksa Anda untuk menggunakan penyebut yang paling tidak umum dari pengaturan keketatan dengan perpustakaan target.

@mhegazy masalah telah direproduksi dalam skenario berikutnya:

import * as Foo from "./Foo";

export class Bar {
    baz = new Foo.Baz(); // Compiler forgot "Foo." prefix in the type, and throws this error, because "Baz" without perfix is not imported.
    getBaz() { // All the same
       return new Foo.Baz();
    }
}

Solusinya adalah tentukan tipe eksplisit:

import * as Foo from "./Foo";

export class Bar {
    baz: Foo.Baz = new Foo.Baz(); // ok
    getBaz(): Foo.Baz { // ok
       return new Foo.Baz();
    }
}

Saya tidak bisa mendapatkan ini untuk mereproduksi menggunakan contoh di atas. laporkan bug baru, dan berikan lebih banyak konteks agar dapat mereproduksi masalah tersebut.

@PFight Terima kasih! Itu untukku!

Saya baru saja menghadapi masalah ini saat menggunakan:

export { IMyInterface } from './file'
````

The solution was to do this:
```ts
import { IMyInterface } from './file'
export { IMyInterface }

Tapi ini seharusnya tidak perlu tbh.

Apakah ada yang tahu cara menangani noUnusedLocals ?

@yordis // @ts-ignore

@pelotom ya ini sepenuhnya salah saya,

Saya sedang memikirkan satu hal dan saya menulis sesuatu yang lain.

Apakah Ketikan memperbaiki masalah antara noUnusedLocals dan deklarasi?

Solusi saya saat ini, yang terasa sangat menyakitkan bagi saya,

import {SomeInterface} from "./SomeFile";

const _dummySomeInterface : undefined|SomeInterface = undefined;
_dummySomeInterface;

//Code that implicitly uses SomeInterface

Menghindari noUnusedLocals , mengizinkan inferensi tipe untuk antarmuka umum, juga, jika memungkinkan.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat