Typescript: Saran: larang penggunaan sebelum definisi

Dibuat pada 15 Jul 2014  ·  29Komentar  ·  Sumber: microsoft/TypeScript

Kompilator harus mengeluarkan kesalahan ketika kode menggunakan nilai sebelum mereka mungkin dapat diinisialisasi.

// Error, 'Derived' declaration must be after 'Base'
class Derived extends Base { }
class Base { }
Bug

Komentar yang paling membantu

Hanya untuk menyebutkan bahwa kami baru saja digigit oleh hari ini, dan kami butuh waktu untuk mencari tahu apa yang sedang terjadi.

TypeScript v1.8.10, build berbasis Webpack, kelas dasar dan turunannya ditentukan dalam file yang sama, tetapi (tampaknya) dalam urutan yang salah, tidak ada kesalahan kompilasi atau peringatan, dan bahkan jika peta sumber berfungsi, tumpukan panggilan kesalahan mengarah ke lokasi yang sangat tidak berguna (akhir dari kelas lain yang mengimpor kelas turunan).

Tidak melalui keseluruhan diskusi, tetapi sebagai pertolongan pertama sepertinya peringatan kompilator akan membantu. Hanya 2 ¢ kami

Semua 29 komentar

Melempar kesalahan kompilator adalah solusi yang baik, mungkin kompilator dapat mengeluarkan kelas dalam urutan yang benar. Itu akan menjadi fitur yang mematikan. Misalnya, kompilator melacak hubungan ketergantungan dan mengeluarkan kelas-kelas yang sesuai dengan itu, melemparkan kesalahan kompilator hanya jika tidak dapat menyelesaikan urutan ketergantungan.

Kompilator melacak hubungan ketergantungan dan mengeluarkan kelas-kelas sesuai dengan itu, memunculkan kesalahan kompilator hanya ketika tidak dapat menyelesaikan urutan ketergantungan.

Haruskah kami membuat saran baru ini? Inilah sebabnya mengapa saat ini saya menggunakan modul AMD daripada modul internal TypeScript; compiler RequireJS menentukan urutan serialisasi modul yang sesuai menggunakan dependensi yang saya tentukan di seluruh basis kode (menggunakan require() ).

Menautkan ke # 274. Kita perlu menguraikan aturan dan ruang lingkupnya

Kasus yang meluas sepertinya merupakan kandidat yang baik untuk pemeriksaan leksikal; kita hanya perlu memastikan bahwa secara leksikal kelas dasar muncul sebelum kelas turunan. Apakah ada kasus lain yang harus kita pertimbangkan?

Satu masalah adalah bahwa menata ulang definisi kelas mungkin menyusun ulang urutan penginisialisasi statis secara diam-diam. Jika kelas dasar muncul setelah kelas turunan, saya memilih untuk mempertahankan kode penginisialisasi statis di situs definisi kelas atau hanya menandai kesalahan.

Saya pikir kasus beberapa file lebih menarik dan berguna dari sudut pandang proyek / pemeliharaan besar (yang merupakan tujuan nyata dari skrip jenis).

Jadi, saya pikir kita perlu mempertimbangkan urutan keluaran dalam mode keluaran satu file. (Alangkah baiknya juga mendapatkan pesanan ini untuk membuat file html yang menyertakan banyak file).

Berikut adalah beberapa pernyataan yang menurut saya akan memastikan pemesanan:

class X extends Y {} // ensure Y is defined in prior file
module { new X(); } // ensure X is defined in prior file
class S { static z = new Z(); } // ensure Z is defined in prior file

Kita juga bisa memperluas ini ke fungsi dan variabel yang ditentukan sebelum digunakan, bukan hanya kelas.

PS Saya memiliki prototipe.

Saya tidak berpikir ada niat untuk mencoba menyusun ulang emit untuk Anda, hanya untuk memberikan kesalahan yang kami bisa untuk hal-hal yang pasti akan gagal saat runtime.

Dan, saya setuju dengan Anda tentang penataan ulang dalam satu file, tetapi ketika beberapa file digabungkan menggunakan --out, kompilator memiliki kendali atas urutan emit dan saya lebih suka urutan yang dipilihnya, berfungsi.

Fungsi @sparecycles diangkat ke atas cakupan kapan saja saat berjalan. jadi fungsinya tidak menarik. variabel juga diangkat, masalahnya adalah bahwa mereka tidak akan diinisialisasi pada saat itu. sekarang digunakan sebelum inisialisasi adalah masalah yang berbeda, dan saya pikir itu dilacak di bawah # 274.

untuk menata ulang; filosofi yang kita ikuti, adalah membiarkan kode keluaran sedekat mungkin dengan kode masukan. intinya kita membiarkan kode pengguna lewat, kita hanya menghapus tipe. dalam pengertian ini, kesalahan akan lebih sejalan dengan apa yang telah kita lakukan selama ini.

Adapun implementasinya, saya telah menambahkan verifikasi urutan leksikal baru-baru ini dengan Let dan Const, dan dapat diekstraksi sebagai pemeriksaan umum dan digunakan untuk kasus yang berbeda ini. Anda dapat menemukannya di sini:
https://github.com/Microsoft/TypeScript/blob/master/src/compiler/checker.ts#L329

Kami perlu mengidentifikasi dengan jelas kasus-kasus yang kami periksa, dan PR pasti akan disambut :)

Ya, saya setuju bahwa kami tidak ingin menyusun ulang dalam satu file skrip, tetapi dalam kasus file --out, urutannya tidak ditentukan oleh pengguna jadi, sekali lagi, saya lebih suka kompilator melakukan upaya terbaik untuk pilih pesanan yang berhasil.

Fungsi mengangkat adalah contoh yang baik di mana kita tidak perlu peduli dalam kasus file tunggal, tetapi di mana mengkompilasi ke beberapa file dan memilih urutan untuk memasukkannya ke dalam file .html bisa menjadi hal yang tidak sepele bagi manusia. Variabel yang tidak terdefinisi saat digunakan adalah contoh yang bagus di mana perilaku tak terduga dapat diperkenalkan oleh kompilator karena perubahan dalam /// <reference> lines.

tetapi dalam kasus file --out, urutannya tidak ditentukan oleh pengguna

Sebenarnya tidak demikian. Kami memiliki aturan yang sangat sederhana di sini - gunakan urutan yang disiratkan oleh tag reference dan urutan file pada baris perintah. Dalam kedua kasus tersebut, pengguna memberi kami pesanan. Membuat kompilator mengabaikan pengurutan yang diberikan pengguna adalah rute berbahaya untuk turun. Bagaimana jika kompilator memutuskan pesanan yang berbeda dari yang Anda inginkan? Bagaimana Anda mengesampingkannya? Bagaimana jika satu pesanan merusak 2 kelas dan pesanan lain merusak 2 variabel?

Maka kita tidak harus mengubah urutannya tetapi haruskah kita setidaknya (memiliki opsi untuk) memperingatkan pengguna bahwa urutan yang digunakan kompilator mungkin salah?

ya. kita tidak harus memesan, tapi justru error.

Apa idiom yang tepat dalam TypeScript untuk kelas yang saling rekursif? A declare class sebelum definisi sebenarnya?

Jika kelas Anda hanya merujuk satu sama lain dalam sistem tipe atau dalam metode instan, itu tidak masalah. Satu-satunya pola "saling rekursif" yang menjadi masalah adalah pola ini:

class Alpha {
    static myFriendBeta = new Beta();   
}

class Beta {
    static myFriendAlpha = new Alpha(); 
}

Anda dapat menulis ulang ini sebagai klodul:

class Alpha {
}

class Beta {
    static myFriendAlpha = new Alpha();
}

module Alpha {
    export var myFriendBeta = new Beta();
}

Ok, aturan apa yang ingin kita terapkan sebagai bagian dari masalah ini selain 'kelas dasar harus didefinisikan secara leksikal sebelum kelas turunan'?

Larang meneruskan referensi ke anggota modul internal, misalnya

var x = M.fn(); // Should error
module M {
    export function fn() {}
}

Larang meneruskan referensi ke anggota enum

var x = E.A; // Should error
enum E { A }

IMO ruang lingkup masalah ini agak terbatas karena orang biasanya tidak mendefinisikan semua kode mereka dalam satu file: kelas dasar lebih mungkin ada dalam file terpisah ke kelas turunan.

Saya menyarankan yang berikut dari @sparecycles juga harus menjadi bagian dari resolusi untuk masalah ini:

Maka kita tidak harus mengubah urutannya tetapi haruskah kita setidaknya (memiliki opsi untuk) memperingatkan pengguna bahwa urutan yang digunakan kompilator mungkin salah?

"Urutan yang digunakan kompilator" harus menyertakan urutan yang ditentukan dalam tsconfig .

Ketika kelas dasar dan kelas turunan berada dalam file yang sama, masalahnya tidak seburuk itu: program akan macet saat startup dan pemrogram akan mengubah urutan dalam satu file itu dan masalahnya adalah _fixed_.

Kasus beberapa file adalah tempat compiler harus memperingatkan saat menggabungkan beberapa file dalam urutan yang meragukan, karena urutan tersebut dapat berubah karena alasan yang tidak kentara.

Pertimbangkan kasus di mana ada kelas dasar dan beberapa kelas turunan, semuanya dalam file terpisah. Kelas dasar menggunakan beberapa kelas turunan dalam implementasinya, sehingga ia mereferensikannya, tetapi masih perlu ditempatkan pertama dalam keluaran. Demikian pula, semua kelas turunan perlu mereferensikan kelas dasar.

Nah, tidak ada masalah dengan file yang saling direferensikan, jika A.ts saling mereferensikan B.ts dan X.ts menyertakan A.ts, maka urutan keluarannya adalah [B, A, X], dan jika mereferensikan B.ts urutannya adalah [A, B, X]. (Tapi hanya satu dari perintah ini yang dapat bekerja pada waktu proses.) Hal ini membuat segalanya menjadi rapuh, karena kompilasi akan berhasil dengan baik jika B atau A direferensikan.

Solusi saya untuk masalah sistem kelas turunan dasar saya: tambahkan index.ts untuk hierarki kelas saya dan sertakan, dalam file ini, semua kelas turunan, diikuti oleh kelas dasar. Ini menjamin bahwa keluarannya akan mengutamakan kelas dasar. (benar-benar kontra-intuitif!). Saya menemukan bahwa jika saya secara langsung mereferensikan file yang saya inginkan itu akan menghasilkan kelas dasar setelah yang diturunkan.

Peringatan compiler akan sangat bagus, tetapi juga bagus untuk dapat menandai salah satu referensi dalam skenario referensi bersama sebagai emit-ordering, dan yang lainnya hanya untuk menarik deklarasi. Referensi pengurutan emisi bersama akan menjadi kesalahan.

(Saat ini saya telah menerapkan ini untuk secara otomatis menghasilkan daftar .js termasuk dalam proyek Visual Studio / Typecript kami karena kami menggunakan output multi-file (lebih mudah untuk debug). Tetapi kodenya ada di C # sebagai tugas inline. Jika ada adalah minat yang akan saya tanyakan apakah saya dapat membagikannya. Ini pada dasarnya adalah dua proses dari algoritme CC Tarjan.)

Kedua peringatan ketika emit-order salah dan menstabilkan emit-order dengan arahan eksplisit akan sangat membantu membuat naskah ketikan bahasa yang layak untuk proyek besar ... ya?

Saya mengalami masalah ini cukup sering dengan basis kode saya yang agak kecil (sekitar 80 file .ts). Idealnya, saya tidak ingin memiliki tag <reference> di bagian atas semua file saya, dan kompilator dapat mengerjakan semuanya untuk saya.

Aplikasi saya hanya memiliki 1 file yang membuat instance kelas dan menjalankan aplikasi (root komposisi saya), beberapa file yang menambahkan ekstensi (misalnya menambahkan Array.prototype.distinct ) dan sisanya hanya definisi kelas / antarmuka.

Dalam hal ini, sebagian besar kode adalah permainan yang adil untuk pengurutan ulang, dan seharusnya tidak memerlukan definisi <reference> secara manual untuk melakukannya dengan benar. Saya melihat definisi kelas sebagai permainan yang adil untuk penyusunan ulang compiler, dan harus didorong ke atas keluaran gabungan, sementara pernyataan lainnya dapat mempertahankan pengurutan seperti saat masukan.

Akankah flag compiler --looseSorting mungkin? Sepertinya fitur yang cukup dicari.

Dalam deklarasi kelas emit di ES6, kita melakukan penugasan properti statis setelah deklarasi kelas. Ini membuat pengacuan ke properti statis kelas dalam nama properti yang dihitung menjadi use-before-definition.

JS yang dipancarkan:

class C {
    [C.p] () {}  // Use before definition
    [C.p+ C.e]() {}  // Use before definition
    [D.f] () {}  // Use before definition
}
C.p = 10;
C.e = 20;

class D {
}
D.f = "hi";

Kami hanya ingin memperingatkan tentang kesalahan ini dalam dua kasus: nama properti-komputasi merujuk ke properti statis kelasnya, atau merujuk ke kelas lain, yang didefinisikan di bawahnya, properti.

Contoh sepele yang kami mainkan hari ini, hanya untuk dimasukkan dalam tes apa pun:

function f() {
    function g() {
        i = 10;
    }

    let i = 20;
    g();
}

Akan bagus untuk mendapatkan permutasi penggunaan / definisi g sekitar i .

Jangan lupa untuk memikirkan fungsi yang didefinisikan dalam lingkup blok. Itu perilaku tidak terdefinisi sesuai standar JavaScript, dan saya tahu setidaknya Firefox dan Chrome tidak setuju dalam penerapannya.

misalnya:

function f() {
    if (true) {
        g(); // iirc, g executes in Chrome, and is undefined in Firefox
        function g() {
        }
        g(); // works in both browsers
    }
}

Hanya untuk menyebutkan bahwa kami baru saja digigit oleh hari ini, dan kami butuh waktu untuk mencari tahu apa yang sedang terjadi.

TypeScript v1.8.10, build berbasis Webpack, kelas dasar dan turunannya ditentukan dalam file yang sama, tetapi (tampaknya) dalam urutan yang salah, tidak ada kesalahan kompilasi atau peringatan, dan bahkan jika peta sumber berfungsi, tumpukan panggilan kesalahan mengarah ke lokasi yang sangat tidak berguna (akhir dari kelas lain yang mengimpor kelas turunan).

Tidak melalui keseluruhan diskusi, tetapi sebagai pertolongan pertama sepertinya peringatan kompilator akan membantu. Hanya 2 ¢ kami

Saya merasa konyol bahwa TS tidak mendukung fitur ini di luar kotak. Kebingungan yang ditimbulkannya mirip dengan menggunakan JS standar. Juga, metode virtual, siapa?

@MrGuardian repro yang dijelaskan di OP telah diperbaiki. Mungkin Anda dapat mengklarifikasi dalam masalah baru atau masalah yang sudah ada yang lebih menggambarkan masalah yang Anda alami?

(# 12673) inilah dua kasus lain di mana IMO seharusnya menjadi kesalahan:

`` ''
Tes kelas
{
_b = ini._a; // tidak ditentukan, tidak ada kesalahan / peringatan
_a = 3;

static _B = Test._A; // undefined, no error/warning
static _A = 3;

method()
{
    let a = b; // Block-scoped variable 'b' used before its declaration
    let b = 3;
}

}
`` ''

@Spongman, bisakah Anda mencatatnya dalam terbitan terpisah? Terima kasih!

12673

Apakah halaman ini membantu?
0 / 5 - 0 peringkat