Typescript: Saran: opsi untuk memasukkan undefined dalam tanda tangan indeks

Dibuat pada 31 Jan 2017  ·  242Komentar  ·  Sumber: microsoft/TypeScript

Pembaruan : untuk proposal terbaru saya, lihat komentar https://github.com/Microsoft/TypeScript/issues/13778#issuecomment -406316164

Dengan mengaktifkan strictNullChecks , TypeScript tidak menyertakan undefined dalam tanda tangan indeks (misalnya pada objek atau larik). Ini adalah peringatan terkenal dan dibahas dalam beberapa masalah, yaitu https://github.com/Microsoft/TypeScript/issues/9235 , https://github.com/Microsoft/TypeScript/issues/13161 , https://github .com/Microsoft/TypeScript/issues/12287 , dan https://github.com/Microsoft/TypeScript/pull/7140#issuecomment -192606629.

Contoh:

const xs: number[] = [1,2,3];
xs[100] // number, even with strictNullChecks

Namun, tampaknya dari membaca masalah di atas bahwa banyak pengguna TypeScript berharap ini tidak terjadi. Memang, jika tanda tangan indeks memang menyertakan undefined , kode kemungkinan akan membutuhkan lebih banyak penjagaan, tetapi—untuk beberapa—ini adalah pertukaran yang dapat diterima untuk meningkatkan keamanan jenis.

Contoh tanda tangan indeks termasuk undefined :

const xs: number[] = [1,2,3];
xs[100] // number | undefined

Saya ingin tahu apakah perilaku ini dapat dianggap sebagai opsi kompiler tambahan selain strictNullChecks . Dengan cara ini, kami dapat memuaskan semua kelompok pengguna: mereka yang menginginkan pemeriksaan nol ketat dengan atau tanpa tanda tangan indeks yang tidak ditentukan.

Committed Suggestion

Komentar yang paling membantu

Kami tetap cukup skeptis bahwa siapa pun akan mendapatkan manfaat dari bendera ini dalam praktiknya. Peta dan hal-hal seperti peta sudah dapat ikut serta dalam | undefined di situs definisinya

Bukankah salah satu tujuan TypeScript untuk memungkinkan kesalahan ditangkap pada waktu "kompilasi" daripada mengandalkan pengguna untuk mengingat/tahu melakukan sesuatu yang spesifik? Ini tampaknya bertentangan dengan tujuan itu; mengharuskan pengguna untuk melakukan sesuatu untuk menghindari crash. Hal yang sama dapat dikatakan untuk banyak fitur lainnya; mereka tidak diperlukan jika pengembang selalu melakukan x. Tujuan dari TypeScript adalah (mungkin) untuk membuat pekerjaan lebih mudah dan menghilangkan hal-hal ini.

Saya menemukan bug ini karena saya mengaktifkan strictNullChecks pada kode yang ada dan saya sudah memiliki perbandingan sehingga saya mendapatkan kesalahan. Jika saya telah menulis kode baru, saya mungkin tidak akan menyadari masalah di sini (sistem tipe memberi tahu saya bahwa saya selalu mendapatkan nilai) dan berakhir dengan kegagalan runtime. Mengandalkan pengembang TS untuk mengingat (atau lebih buruk, bahkan tahu) bahwa mereka seharusnya mendeklarasikan semua peta mereka dengan | undefined terasa seperti TypeScript gagal melakukan apa yang sebenarnya diinginkan orang.

Semua 242 komentar

Dengan pengecualian strictNullChecks, kami tidak memiliki tanda yang mengubah perilaku sistem tipe. flag biasanya mengaktifkan/menonaktifkan pelaporan kesalahan.
anda selalu dapat memiliki versi kustom dari perpustakaan yang mendefinisikan semua pengindeks dengan | undefined . harus bekerja seperti yang diharapkan.

@mhegazy Itu ide yang menarik. Adakah panduan tentang cara mengganti tanda tangan tipe untuk array/objek?

Ada interface Array<T> di lib.d.ts . Saya mencari dengan regexp \[\w+: (string|number)\] untuk menemukan tanda tangan pengindeksan lainnya juga.

Menarik, jadi saya mencoba ini:

{
    // https://github.com/Microsoft/TypeScript/blob/1f92bacdc81e7ae6706ad8776121e1db986a8b27/lib/lib.d.ts#L1300
    declare global {
        interface Array<T> {
            [n: number]: T | undefined;
        }
    }

    const xs = [1,2,3]
    const x = xs[100]
    x // still number :-(
}

Ada ide?

salin lib.d.ts secara lokal, katakan lib.strict.d.ts , ubah tanda tangan indeks menjadi [n: number]: T | undefined; , sertakan file dalam kompilasi Anda. Anda akan melihat efek yang diinginkan.

Keren, terima kasih untuk itu.

Masalah dengan perbaikan yang disarankan di sini adalah memerlukan forking dan memelihara file lib .

Saya ingin tahu apakah fitur ini cukup dituntut untuk menjamin semacam opsi di luar kotak.

Sebagai tambahan, menarik bahwa tanda tangan tipe untuk metode get pada koleksi ES6 ( Map / Set ) mengembalikan T | undefined ketika Array / Object tanda tangan indeks tidak.

ini adalah keputusan sadar. akan sangat mengganggu jika kode ini menjadi kesalahan:

var a = [];
for (var i =0; i< a.length; i++) {
    a[i]+=1; // a[i] is possibly undefined
}

dan tidak masuk akal untuk meminta setiap pengguna menggunakan ! . atau untuk menulis

var a = [];
for (var i =0; i< a.length; i++) {
    if (a[i]) {
        a[i]+=1; // a[i] is possibly undefined
    }
}

Untuk peta hal ini tidak terjadi pada umumnya.

Demikian pula untuk tipe Anda, Anda dapat menentukan | undefined pada semua tanda tangan indeks Anda, dan Anda akan mendapatkan perilaku yang diharapkan. tapi untuk Array itu tidak masuk akal. Anda dipersilakan untuk melakukan fork perpustakaan dan membuat perubahan apa pun yang perlu Anda lakukan, tetapi kami tidak memiliki rencana untuk mengubah deklarasi di perpustakaan standar saat ini.

Saya tidak berpikir menambahkan bendera untuk mengubah bentuk deklarasi adalah sesuatu yang akan kami lakukan.

@mhegazy tetapi untuk array dengan lubang a[i] sebenarnya mungkin tidak terdefinisi:

let a: number[] = []
a[0] = 0
a[5] =0
for (let i = 0; i < a.length; i++) {
  console.log(a[i])
}

Keluarannya adalah:

undefined
undefined
undefined
undefined
0

Kami tetap cukup skeptis bahwa siapa pun akan mendapatkan manfaat dari bendera ini dalam praktiknya. Peta dan hal-hal seperti peta sudah dapat ikut serta dalam | undefined di situs definisinya, dan menerapkan perilaku seperti EULA pada akses larik sepertinya bukan kemenangan. Kami mungkin perlu secara substansial meningkatkan CFA dan tipe pelindung untuk membuat ini enak.

Jika seseorang ingin memodifikasi lib.d.ts mereka dan memperbaiki semua jeda hilir dalam kode mereka sendiri dan menunjukkan seperti apa perbedaan keseluruhan untuk menunjukkan bahwa ini memiliki beberapa proposisi nilai, kami terbuka untuk data itu. Atau jika banyak orang sangat bersemangat untuk menggunakan postfix ! lebih banyak tetapi belum memiliki banyak kesempatan untuk melakukannya, flag ini akan menjadi pilihan.

Kami tetap cukup skeptis bahwa siapa pun akan mendapatkan manfaat dari bendera ini dalam praktiknya. Peta dan hal-hal seperti peta sudah dapat ikut serta dalam | undefined di situs definisinya

Bukankah salah satu tujuan TypeScript untuk memungkinkan kesalahan ditangkap pada waktu "kompilasi" daripada mengandalkan pengguna untuk mengingat/tahu melakukan sesuatu yang spesifik? Ini tampaknya bertentangan dengan tujuan itu; mengharuskan pengguna untuk melakukan sesuatu untuk menghindari crash. Hal yang sama dapat dikatakan untuk banyak fitur lainnya; mereka tidak diperlukan jika pengembang selalu melakukan x. Tujuan dari TypeScript adalah (mungkin) untuk membuat pekerjaan lebih mudah dan menghilangkan hal-hal ini.

Saya menemukan bug ini karena saya mengaktifkan strictNullChecks pada kode yang ada dan saya sudah memiliki perbandingan sehingga saya mendapatkan kesalahan. Jika saya telah menulis kode baru, saya mungkin tidak akan menyadari masalah di sini (sistem tipe memberi tahu saya bahwa saya selalu mendapatkan nilai) dan berakhir dengan kegagalan runtime. Mengandalkan pengembang TS untuk mengingat (atau lebih buruk, bahkan tahu) bahwa mereka seharusnya mendeklarasikan semua peta mereka dengan | undefined terasa seperti TypeScript gagal melakukan apa yang sebenarnya diinginkan orang.

Kami tetap cukup skeptis bahwa siapa pun akan mendapatkan manfaat dari bendera ini dalam praktiknya. Peta dan hal-hal seperti peta sudah dapat ikut serta dalam | tidak terdefinisi di situs definisi mereka
Bukankah salah satu tujuan TypeScript untuk memungkinkan kesalahan ditangkap pada waktu "kompilasi" daripada mengandalkan pengguna untuk mengingat/tahu melakukan sesuatu yang spesifik?

Sebenarnya tujuannya adalah:

1) Secara statis mengidentifikasi konstruk yang mungkin error.

Apa yang sedang dibahas di sini kemungkinan kesalahan (rendah menurut pendapat tim TypeScript) dan kegunaan produktif umum dari bahasa tersebut. Beberapa perubahan awal pada CFA adalah menjadi kurang waspada atau meningkatkan analisis CFA untuk lebih cerdas menentukan hal-hal ini.

Saya pikir pertanyaan dari tim TypeScript adalah bahwa alih-alih memperdebatkan kebenarannya secara ketat, untuk memberikan contoh di mana keketatan semacam ini, dalam penggunaan umum sebenarnya akan mengidentifikasi kesalahan yang harus dijaga.

Saya masuk ke alasannya sedikit lebih banyak di komentar ini https://github.com/Microsoft/TypeScript/issues/11238#issuecomment -250562397

Pikirkan dua jenis kunci di dunia: Mereka yang Anda tahu do memiliki yang sesuai properti di beberapa objek (aman), orang-orang yang Anda tidak tahu untuk memiliki yang sesuai properti di beberapa objek (berbahaya).

Anda mendapatkan jenis kunci pertama, kunci "aman", dengan menulis kode yang benar seperti

for (let i = 0; i < arr.length; i++) {
  // arr[i] is T, not T | undefined

atau

for (const k of Object.keys(obj)) {
  // obj[k] is T, not T | undefined

Anda mendapatkan jenis kedua dari kunci, jenis "berbahaya", dari hal-hal seperti input pengguna, atau file JSON acak dari disk, atau beberapa daftar kunci yang mungkin ada tetapi mungkin tidak.

Jadi, jika Anda memiliki kunci dari jenis yang berbahaya dan diindeks olehnya, alangkah baiknya jika Anda memiliki | undefined di sini. Tapi proposalnya bukan "Perlakukan kunci berbahaya sebagai berbahaya", itu "Perlakukan semua kunci, bahkan yang aman, sebagai berbahaya". Dan begitu Anda mulai memperlakukan kunci aman sebagai berbahaya, hidup benar-benar menyebalkan. Anda menulis kode seperti

for (let i = 0; i < arr.length; i++) {
  console.log(arr[i].name);

dan TypeScript mengeluh kepada Anda bahwa arr[i] mungkin undefined meskipun hei lihat, saya baru saja @#%#ing mengujinya . Sekarang Anda terbiasa menulis kode seperti ini, dan rasanya bodoh:

for (let i = 0; i < arr.length; i++) {
  // TypeScript makes me use ! with my arrays, sad.
  console.log(arr[i]!.name);

Atau mungkin Anda menulis kode seperti ini:

function doSomething(myObj: T, yourObj: T) {
  for (const k of Object.keys(myObj)) {
    console.log(yourObj[k].name);
  }
}

dan TypeScript mengatakan "Hei, ekspresi indeks itu mungkin | undefined , jadi Anda dengan patuh memperbaikinya karena Anda sudah melihat kesalahan ini 800 kali:

function doSomething(myObj: T, yourObj: T) {
  for (const k of Object.keys(myObj)) {
    console.log(yourObj[k]!.name); // Shut up TypeScript I know what I'm doing
  }
}

Tapi Anda tidak memperbaiki bug . Anda bermaksud menulis Object.keys(yourObj) , atau mungkin myObj[k] . Itu adalah jenis kesalahan kompiler terburuk, karena itu tidak benar-benar membantu Anda dalam skenario apa pun - itu hanya menerapkan ritual yang sama untuk setiap jenis ekspresi, tanpa memperhatikan apakah itu sebenarnya lebih berbahaya daripada ekspresi lain dari bentuk yang sama.

Saya memikirkan yang lama "Apakah Anda yakin ingin menghapus file ini?" dialog. Jika dialog itu muncul setiap kali Anda mencoba menghapus file, Anda akan segera belajar untuk menekan del y ketika Anda biasa menekan del , dan peluang Anda untuk tidak menghapus sesuatu yang penting reset ke pra -dialog dasar. Jika dialog hanya muncul saat Anda menghapus file saat tidak masuk ke tempat sampah daur ulang, sekarang Anda memiliki keamanan yang berarti. Tapi kami tidak tahu (kami juga tidak bisa ) apakah kunci objek Anda aman atau tidak, jadi tunjukkan "Apakah Anda yakin ingin mengindeks objek itu?" dialog setiap kali Anda melakukannya sepertinya tidak akan menemukan bug pada tingkat yang lebih baik daripada tidak menampilkan semuanya.

Secara statis mengidentifikasi konstruksi yang mungkin kesalahan.

Mungkin ini perlu diubah untuk mengatakan "Identifikasi secara statis konstruksi yang lebih mungkin daripada yang lain untuk menjadi kesalahan." : mengedipkan mata:. Saya teringat ketika kami mendapatkan bug yang pada dasarnya adalah "Saya menggunakan * ketika saya bermaksud menggunakan / , dapatkah Anda menggunakan membuat * peringatan?"

Saya mengerti, tetapi indeks di luar jangkauan adalah masalah nyata dan umum; memaksa orang untuk menghitung array dengan cara yang tidak dapat mereka lakukan bukanlah hal yang buruk.

Perbaikan dengan ! Saya sebenarnya juga tidak suka - bagaimana jika seseorang datang dan membuat perubahan sehingga asumsi itu sekarang tidak valid? Anda kembali ke titik awal (potensi kegagalan runtime untuk sesuatu yang harus ditangkap oleh kompiler). Seharusnya ada cara yang aman untuk menghitung array yang tidak bergantung pada kebohongan tentang tipe atau menggunakan ! (mis. tidak bisakah Anda melakukan sesuatu seperti array.forEach(i => console.log(i.name) ?).

Anda sudah mempersempit tipe berdasarkan kode sehingga secara teori tidak bisakah Anda melihat pola yang aman mempersempit tipe untuk menghapus | undefined dalam kasus itu, memberikan yang terbaik dari kedua dunia? Saya berpendapat bahwa jika Anda tidak dapat dengan mudah menyampaikan kepada kompiler bahwa Anda tidak mengakses elemen yang valid maka mungkin jaminan Anda tidak valid atau dapat dengan mudah rusak secara tidak sengaja di masa mendatang.

Yang mengatakan, saya hanya menggunakan TS pada satu proyek dan itu pada akhirnya akan dimigrasikan ke Dart sehingga tidak mungkin membuat perbedaan nyata bagi saya. Saya hanya sedih bahwa kualitas umum perangkat lunak buruk dan ada peluang untuk membantu menghilangkan kesalahan di sini yang tampaknya diabaikan demi kenyamanan. Saya yakin sistem tipe dapat dibuat solid dan gangguan umum diatasi dengan cara yang tidak menimbulkan lubang ini.

Bagaimanapun, itu hanya 2 sen saya .. Saya tidak ingin berlarut-larut - saya yakin Anda mengerti dari mana kami berasal dan Anda jauh lebih baik untuk membuat keputusan tentang ini daripada saya :-)

Saya pikir ada beberapa hal yang perlu dipertimbangkan. Ada banyak pola untuk mengulangi array yang umum digunakan yang memperhitungkan jumlah elemen. Meskipun ini adalah pola yang mungkin untuk mengakses indeks secara acak pada array, di alam liar itu adalah pola yang sangat tidak umum dan tidak mungkin menjadi kesalahan statis. Meskipun ada cara modern untuk beralih, yang paling umum adalah seperti:

for (let i = 0; i < a.length; i++) {
  const value = a[i];
}

Jika Anda menganggap array cadangan tidak umum (mereka) tidak banyak membantu untuk memiliki value menjadi | undefined . Jika ada pola umum, di alam liar, di mana ini berisiko (dan kemungkinan kesalahan) maka saya pikir TypeScript akan mendengarkan untuk mempertimbangkan ini, tetapi pola yang digunakan secara umum, harus kembali lagi terhadap semua nilai dari akses indeks menjadi tidak terdefinisi jelas merupakan sesuatu yang mempengaruhi produktivitas dan seperti yang ditunjukkan, dapat diikutsertakan jika Anda berada dalam situasi di mana itu berpotensi berguna.

Saya pikir telah ada percakapan sebelumnya tentang meningkatkan CFA sehingga ada cara untuk mengekspresikan ketergantungan nilai bersama (misalnya Array.prototype.length berkaitan dengan nilai indeks) sehingga hal-hal seperti indeks di luar batas dapat dianalisis secara statis. Jelas itu adalah pekerjaan yang signifikan, ditempa dengan segala macam kasus dan pertimbangan tepi yang tidak ingin saya pahami (meskipun kemungkinan Anders bangun dengan keringat dingin karena beberapa hal seperti ini).

Jadi itu menjadi trade off... Tanpa perbaikan CFA, memperumit 90% kode dengan herring merah untuk menangkap berpotensi 10% kode buruk . Kalau tidak, itu berinvestasi dalam peningkatan CFA besar, yang mungkin ditempa dengan konsekuensi stabilitas dan masalah mereka sendiri, menemukan apa yang akan menjadi kode yang tidak aman.

Hanya ada begitu banyak yang bisa dilakukan TypeScript untuk menyelamatkan kita dari diri kita sendiri.

Semua fokus ini adalah pada array dan saya setuju itu cenderung menjadi masalah di sana, tetapi sebagian besar masalah asli yang diangkat (seperti milik saya) adalah tentang peta di mana saya tidak berpikir kasus umum adalah kunci yang selalu ada sama sekali?

Semua fokus ini adalah pada array dan saya setuju itu cenderung menjadi masalah di sana, tetapi sebagian besar masalah asli yang diangkat (seperti milik saya) adalah tentang peta di mana saya tidak berpikir kasus umum adalah kunci yang selalu ada sama sekali?

Jika ini adalah tipe Anda, tambahkan | undefined ke tanda tangan indeks. Sudah merupakan kesalahan untuk mengindeks ke dalam tipe tanpa tanda tangan indeks di bawah --noImplicitAny .
ES6 Map sudah didefinisikan dengan get sebagai get(key: K): V | undefined; .

saya menulis ulang semua definisi Array dan Peta untuk membuat tanda tangan indeks kembali | undefined , tidak pernah menyesal sejak itu, menemukan beberapa bug, tidak menyebabkan ketidaknyamanan karena saya bekerja dengan array secara tidak langsung melalui lib buatan tangan yang terus memeriksa untuk undefined atau ! di dalamnya

akan lebih bagus jika TypeScript dapat mengontrol aliran pemeriksaan seperti yang dilakukan C# (untuk menghilangkan pemeriksaan rentang indeks untuk menghemat waktu prosesor), misalnya:

declare var values: number[];
for (let index = 0, length = values.length; index< length; index ++) {
   const value = value[index]; // always defined, because index is within array range and only controlled by it
}

(bagi mereka yang menggunakan susunan jarang - bunuh diri dengan api yang membara)

untuk Object.keys , dibutuhkan tipe khusus katakanlah allkeysof T untuk membiarkan analisis aliran kontrol melakukan penyempitan yang aman

Saya pikir ini akan menjadi pilihan yang baik untuk dimiliki, karena saat ini kita pada dasarnya berbohong tentang jenis operasi pengindeksan, dan mudah lupa untuk menambahkan | undefined ke jenis objek saya. Saya pikir menambahkan ! dalam kasus di mana kita tahu kita ingin mengabaikan undefined s akan menjadi cara yang bagus untuk menangani operasi pengindeksan ketika opsi ini diaktifkan.

Ada (setidaknya) dua masalah lain dengan menempatkan |undefined dalam definisi tipe objek Anda:

  • itu berarti Anda dapat menetapkan tidak terdefinisi ke dalam objek-objek ini, yang jelas tidak dimaksudkan
  • operasi lain, seperti Object.values (atau _.values ) akan mengharuskan Anda untuk menangani undefined dalam hasil

tslint melaporkan peringatan positif palsu dari kondisi konstan, karena TypeScript mengembalikan informasi tipe yang salah (= kurang | undefined ).
https://github.com/palantir/tslint/issues/2944

Salah satu kesalahan yang sering dilewati dengan tidak adanya | undefined dalam pengindeksan array adalah pola ini ketika digunakan sebagai pengganti find :

const array = [ 1, 2, 3 ];
const firstFour = array.filter((x) => (x === 4))[0];
// if there is no `4` in the `array`,
// `firstFour` will be `undefined`, but TypeScript thinks `number` because of the indexer signature.
const array = [ 1, 2, 3 ];
const firstFour = array.find((x) => (x === 4));
// `firstFour` will be correctly typed as `number | undefined` because of the `find` signature.

Saya pasti akan menggunakan bendera ini. Ya, loop for akan mengganggu untuk digunakan, tetapi kami memiliki operator ! untuk memberi tahu kompiler ketika kami tahu itu didefinisikan:

for (let i = 0; i < arr.length; i++) {
  foo(arr[i]!)
}

Juga, masalah ini bukan masalah dengan loop for of yang lebih baru dan jauh lebih baik dan bahkan ada aturan TSLint prefer-for-of yang memberitahu Anda untuk tidak menggunakan loop for gaya lama lagi.

Saat ini saya merasa sistem tipe tidak konsisten untuk pengembang. array.pop() memerlukan cek if atau ! , tetapi mengakses melalui [array.length - 1] tidak. ES6 map.get() memerlukan cek if atau ! pernyataan, tetapi hash objek tidak. Contoh @sompylasar juga bagus.

Contoh lain adalah merusak:

const specifier = 'Microsoft/TypeScript'
const [repo, revision] = specifier.split('@') // types of repo and revision are string
console.log('Repo: ' + repo)
console.log('Short rev: ' + revision.slice(0, 7)) // Error: Cannot call function 'slice' on undefined

Saya lebih suka jika kompiler memaksa saya melakukan ini:

const specifier = 'Microsoft/TypeScript'
const [repo, revision] = specifier.split('@') // types of repo and revision are string | undefined
console.log('Repo: ', repo || 'no repo')
console.log('Short rev:', revision ? revision.slice(0, 7) : 'no revision')

Ini adalah bug aktual yang pernah saya lihat yang dapat dicegah oleh kompiler.

Imo ini seharusnya tidak termasuk dalam file pengetikan, tetapi seharusnya menjadi mekanik sistem tipe - ketika mengakses apa pun dengan tanda tangan indeks, itu bisa undefined . Jika logika Anda memastikan tidak, gunakan saja ! . Jika tidak, tambahkan if dan Anda baik-baik saja.

Saya pikir banyak orang lebih suka kompiler ketat dengan beberapa pernyataan yang diperlukan daripada longgar dengan bug yang tidak tertangkap.

Saya sangat ingin melihat bendera ini ditambahkan. Dalam basis kode perusahaan saya, akses acak array adalah pengecualian langka dan loop for adalah bau kode yang biasanya ingin kita tulis ulang dengan fungsi tingkat tinggi.

@pelotom apa yang menjadi perhatian Anda (karena sepertinya Anda sebagian besar keluar dari masalah)?

@aleksey-bykov sebagian besar tanda tangan indeks objek, yang terjadi secara luas di perpustakaan pihak ketiga. Saya ingin mengakses properti di { [k: string]: A } untuk memperingatkan saya bahwa hasilnya mungkin tidak terdefinisi. Saya hanya menyebutkan pengindeksan array karena diangkat sebagai kasus mengapa bendera terlalu mengganggu untuk dikerjakan.

Anda tahu Anda dapat menulis ulang mereka persis seperti yang Anda inginkan? (diberi sedikit kerja ekstra)

Ya, saya bisa menulis ulang pengetikan semua orang untuk mereka, atau saya bisa mengaktifkan flag compiler

terus bermain kapten O...: Anda dapat menulis ulang lib.d.ts hari ini dan menjadi pemilik yang bahagia dari lebih banyak basis kode suara atau Anda dapat menunggu bendera untuk N tahun ke depan

@aleksey-bykov bagaimana itu bisa dilakukan dengan menulis ulang lib.d.ts ?

declare type Keyed<T> = { [key: string]: T | undefined; }

kemudian dalam definisi Array di lib.es2015.core.d.ts , ganti

[n: number]: T;

dengan

[n: number]: T | undefined;

@aleksey-bykov mungkin Anda melewatkan bagian di mana saya katakan saya tidak peduli dengan array. Saya peduli di mana perpustakaan pihak ketiga telah mendeklarasikan sesuatu bertipe { [k: string]: T } , dan saya ingin mengakses objek seperti itu untuk mengembalikan sesuatu yang mungkin tidak terdefinisi. Tidak ada cara untuk mencapainya hanya dengan mengedit lib.d.ts ; itu membutuhkan mengubah tanda tangan perpustakaan yang bersangkutan.

apakah Anda memiliki kendali atas file definisi pihak ke-3? jika demikian Anda dapat memperbaikinya

Dan sekarang kita kembali ke

Ya, saya bisa menulis ulang pengetikan semua orang untuk mereka, atau saya bisa mengaktifkan flag compiler

Waktu adalah lingkaran datar.

jangan konyol, Anda tidak menggunakan "mengetik semua orang" kan? ini benar-benar satu hari kerja maksimal untuk proyek biasa, pernah melakukannya di sana

Ya, saya harus mengedit pengetikan orang lain setiap saat dan saya ingin melakukannya lebih sedikit.

dan Anda akan melakukannya dalam N tahun, mungkin, untuk saat ini Anda dapat menderita atau menjadi dewasa

Terima kasih atas masukannya yang sangat membangun 👍

masukan yang membangun untuk ini adalah sebagai berikut, masalah ini perlu ditutup, karena:
A. baik keputusan apakah [x] dapat undefined atau tidak diserahkan kepada pengembang oleh

  • membiarkan mereka menyimpan semuanya di kepala mereka seperti yang selalu mereka lakukan sebelumnya
  • atau dengan mengubah lib.d.ts dan 3rd-party.d.ts seperti yang disarankan

B. atau dibutuhkan sintaks / jenis / analisis aliran khusus / N tahun untuk mengurangi sesuatu yang dapat dengan mudah dilakukan dengan tangan di #a

Masalahnya adalah proposal untuk (b), kecuali tidak ada sintaks baru yang diusulkan, itu hanya flag kompiler.

Apa yang terjadi adalah bahwa tipe { [x: string]: {} } hampir selalu bohong; kecuali penggunaan Proxy , tidak ada objek yang dapat memiliki jumlah properti tak terbatas, apalagi setiap string yang mungkin. Proposalnya adalah memiliki flag kompiler yang mengenali ini. Mungkin terlalu sulit untuk menerapkan ini untuk apa yang diperoleh; Saya akan menyerahkan panggilan itu kepada pelaksana.

intinya bukan itu

  • T | undefined
  • atau T

tepat untuk kasus umum

untuk memperbaikinya untuk kasus umum, Anda perlu menyandikan informasi tentang prerense nilai ke dalam jenis wadahnya yang membutuhkan sistem tipe dependen ... yang dengan sendirinya bukan hal yang buruk untuk dimiliki :) tetapi mungkin serumit semua sistem tipe TypeScript saat ini dilakukan hingga hari ini, demi ... menghemat beberapa pengeditan?

T | undefined benar untuk kasus umum, untuk alasan yang baru saja saya berikan. Akan mengabaikan ocehan tidak masuk akal Anda tentang tipe dependen, semoga harimu menyenangkan.

Anda dapat mengabaikan saya sebanyak yang Anda inginkan tetapi T | undefined adalah overshoot untuk

declare var items: number[];
for (var index = 0; index < items.length; index ++) {
   void items[index];
}

Saya lebih suka memiliki T | undefined sana secara default dan memberi tahu kompiler bahwa index adalah rentang indeks numerik items sehingga tidak keluar jika batas ketika diterapkan ke items ; dalam kasus sederhana seperti sekumpulan bentuk loop for / while sering digunakan, kompilator dapat menyimpulkannya secara otomatis; dalam kasus yang kompleks, maaf, mungkin ada undefined s. Dan ya, tipe berbasis nilai akan cocok di sini; tipe string literal sangat berguna, mengapa tidak memiliki tipe literal boolean dan number dan range/set-of-ranges? Sejauh TypeScript berjalan, ia mencoba untuk menutupi semua yang dapat diekspresikan dengan JavaScript (berbeda dengan, misalnya, Elm yang membatasi itu).

ini benar-benar satu hari kerja maksimal untuk proyek biasa, pernah melakukannya di sana

@aleksey-bykov, penasaran apa pengalaman Anda setelah perubahan itu? seberapa sering Anda harus menggunakan ! ? dan seberapa sering Anda menemukan kompiler menandai bug yang sebenarnya?

@mhegazy jujur ​​saya tidak melihat banyak perbedaan bergerak dari T ke T | undefined , saya juga tidak menangkap bug, saya kira masalah saya adalah saya bekerja dengan array melalui fungsi utilitas yang menjaga ! di dalamnya, jadi secara harfiah tidak ada efek untuk kode luar:

https://github.com/aleksey-bykov/basic/blob/master/array.ts

Di file lib saya dapat menemukan definisi tipe indeks untuk objek? Saya telah menemukan dan memperbarui Array dari [n: number]: T menjadi [n: number]: T | undefined . Sekarang saya ingin melakukan hal yang sama untuk objek.

tidak ada antarmuka standar (seperti Array untuk array) untuk objek dengan tanda tangan indeks, Anda perlu mencari definisi yang tepat untuk setiap kasus dalam kode Anda dan memperbaikinya

Anda perlu mencari definisi yang tepat untuk setiap kasus dalam kode Anda dan memperbaikinya

Bagaimana dengan pencarian kunci langsung? Misalnya

const xs = { foo: 'bar' }
xs['foo']

Apakah ada cara untuk menegakkan T | undefined alih-alih T sini? Saat ini saya menggunakan pembantu ini di basis kode saya di mana-mana, sebagai alternatif yang aman untuk mengindeks pencarian pada array dan objek:

// TS doesn't return the correct type for array and object index signatures. It returns `T` instead
// of `T | undefined`. These helpers give us the correct type.
// https://github.com/Microsoft/TypeScript/issues/13778
export const getIndex = function<X> (index: number, xs: X[]): X | undefined {
  return xs[index];
};
export const getKeyInMap = function<X> (key: string, xs: { [key: string]: X }): X | undefined {
  return xs[key];
};

@mhegazy Saat saya menulis ini, saya memperbaiki bug dalam produksi di https://unsplash.com yang dapat ditangkap dengan jenis tanda tangan indeks yang lebih ketat.

saya mengerti, pertimbangkan operator tipe yang dipetakan:

const xs = { foo: 'bar' };
type EachUndefined<T> = { [P in keyof T]: T[P] | undefined; }
const xu : EachUndefined<typeof xs> = xs;
xu.foo; // <-- string | undefined

Jika flag seperti --strictArrayIndex bukan merupakan opsi karena flag tidak dirancang untuk mengubah file lib.d.ts . Mungkin kalian dapat merilis versi ketat dari file lib.d.ts seperti " lib": ['strict-es6'] ?

Itu bisa berisi beberapa peningkatan, bukan hanya indeks array yang ketat. Misalnya, Object.keys :

interface ObjectConstructor {
    // ...
    keys(o: {}): string[];
}

Bisa jadi:

interface ObjectConstructor {
    // ...
    keys<T>(o: T): (keyof T)[];
}

Pembaruan dari SBS hari ini: Kami saling berteriak selama 30 menit dan tidak ada yang terjadi. ️

@RyanCavanaugh Apa itu SBS, karena penasaran?

@radix "Saran Backlog Slog"

itu menarik, karena solusinya jelas:
keduanya T dan T | undefined salah, satu-satunya cara yang benar adalah membuat variabel indeks mengetahui kapasitas wadahnya, baik dengan memilih dari satu set atau dengan melampirkannya dalam rentang numerik yang diketahui

@RyanCavanaugh saya sudah memikirkan ini, sepertinya trik berikut akan mencakup 87% dari semua kasus:

  • values[index] memberikan T jika indeks dideklarasikan dalam for (HERE; ...)
  • values[somethingElse] memberikan T | undefined untuk semua variabel yang dideklarasikan di luar for

@aleksey-bykov kami membahas sesuatu yang lebih cerdas - bahwa mungkin ada mekanisme penjaga tipe aktual untuk " arr[index] diuji oleh index < arr.length . Tapi itu tidak akan membantu kasus di mana Anda pop 'd di tengah lingkaran, atau meneruskan array Anda ke fungsi yang menghapus elemen darinya. Sepertinya orang tidak mencari mekanisme yang mencegah kesalahan OOB 82,9% dari waktu - setelah semua , sudah menjadi kasus bahwa kira-kira sebagian kecil dari kode pengindeksan array sudah benar.Menambahkan upacara untuk memperbaiki kode sementara tidak menangkap bug dalam kasus yang salah adalah hasil yang buruk.

bukan untuk menyiratkan bahwa Aleksey akan pernah mengubah array

Saya mem-porting aplikasi dari Elm ke Typescript baru-baru ini dan operasi pengindeksan yang salah ketik mungkin merupakan salah satu sumber bug terbesar yang saya temui, dengan semua pengaturan TS yang paling ketat diaktifkan (juga hal-hal seperti this tidak terikat ).

  • Anda tidak dapat melacak mutasi ke wadah
  • Anda tidak dapat melacak manipulasi indeks

dengan sedikit yang dikatakan ini, Anda dapat menjamin, jika demikian mengapa Anda bahkan mencoba jika kasus penggunaan tipikal adalah iterasi bodoh melalui semua elemen dalam < array.length

seperti yang saya katakan biasanya

  • jika ada kesalahan, kemungkinan berasal dari suatu tempat di klausa pernyataan for : inisialisasi, kenaikan, kondisi berhenti dan dengan demikian bukan sesuatu yang dapat Anda verifikasi, karena memerlukan pemeriksaan pengetikan lengkap
  • di luar klausa for biasanya tidak ada tempat untuk kesalahan

jadi selama indeks dideklarasikan entah bagaimana (tidak ada yang bisa kami katakan tentang itu dengan percaya diri) cukup adil untuk percaya bahwa indeks itu benar di dalam badan loop

Tapi itu tidak akan membantu kasus di mana Anda muncul di tengah lingkaran, atau meneruskan array Anda ke fungsi yang menghapus elemen darinya

Ini sepertinya argumen yang sangat lemah terhadap ini. Kasus-kasus itu sudah rusak - menggunakannya sebagai alasan untuk tidak memperbaiki kasus lain tidak masuk akal. Tidak ada seorang pun di sini yang meminta salah satu dari kasus itu ditangani dengan benar atau 100% benar. Kami hanya ingin tipe yang akurat dalam kasus yang sangat umum. Ada banyak kasus di TypeScript yang tidak ditangani dengan sempurna; jika Anda melakukan sesuatu yang aneh, jenisnya mungkin salah. Dalam hal ini kami tidak melakukan sesuatu yang aneh dan jenisnya salah.

Menambahkan upacara ke kode yang benar

Saya ingin tahu melihat contoh kode di mana ini "menambahkan upacara untuk memperbaiki kode". Seperti yang saya pahami, perulangan dasar untuk array mudah dideteksi/disempit. Apa kode dunia nyata yang tidak sederhana untuk loop di mana ini menjadi tidak nyaman yang tidak berpotensi bug? (baik sekarang, atau bisa dari perubahan sepele di masa depan). Saya tidak mengatakan tidak ada; Saya tidak bisa membayangkannya dan belum melihat contoh apa pun (saya telah melihat banyak contoh menggunakan for loop, tetapi kecuali Anda mengatakan ini tidak dapat dipersempit, sepertinya tidak relevan).

sementara tidak menangkap bug

Ada contoh kode kerja yang gagal dikompilasi karena ini dan kode yang muncul saat runtime karena tipenya menyesatkan pengembang. Mengatakan tidak ada nilai dalam mendukung ini adalah omong kosong.

Mengapa kita tidak bisa membuatnya tetap sederhana dan tidak menambahkan _any_ perilaku ajaib di sekitar loop for gaya lama sama sekali. Anda selalu dapat menggunakan ! untuk membuat semuanya berfungsi. Jika basis kode Anda penuh dengan loop for gaya lama, jangan gunakan flag. Semua basis kode modern yang pernah saya tangani menggunakan forEach atau for of untuk mengulangi array dan basis kode tersebut akan mendapat manfaat dari keamanan tipe tambahan.

kami tidak melakukan sesuatu yang aneh dan jenisnya salah.

Paragraf ini, bagi saya, terdengar seperti alasan yang bagus untuk tidak memiliki fitur ini. Mengakses array di luar batas adalah hal yang aneh untuk dilakukan; jenisnya hanya "salah" jika Anda melakukan hal yang tidak biasa (OOBing). Sebagian besar pembacaan kode dari array melakukannya secara in-bound; akan "salah" untuk memasukkan undefined dalam kasus tersebut dengan argumen ini.

... kode kerja yang gagal dikompilasi

Saya tidak mengetahuinya - dapatkah Anda menunjukkannya secara spesifik?

Mengapa kita tidak bisa membuatnya tetap sederhana dan tidak menambahkan perilaku ajaib apa pun di sekitar loop for gaya lama sama sekali. Anda selalu dapat menggunakan ! untuk membuat hal-hal bekerja.

Apa perbedaan yang berguna antara ini dan aturan TSLint yang mengatakan "Semua ekspresi akses array harus memiliki ! " ?

@RyanCavanaugh asumsi saya adalah bahwa aturan TSLint tidak akan dapat mempersempit tipe atau menggunakan analisis tipe aliran kontrol (misalnya membungkus akses dalam if , melemparkan pengecualian, return ing atau continue ing jika tidak disetel, dll). Ini juga bukan hanya tentang ekspresi akses array, ini juga tentang destrukturisasi. Seperti apa implementasinya untuk contoh di https://github.com/Microsoft/TypeScript/issues/13778#issuecomment -336265143? Untuk menegakkan ! , pada dasarnya harus membuat tabel untuk melacak jenis variabel ini sebagai kemungkinan undefined . Yang bagi saya terdengar seperti sesuatu yang harus dilakukan oleh pemeriksa tipe, bukan linter.

@RyanCavanaugh

Paragraf ini, bagi saya, terdengar seperti alasan yang bagus untuk tidak memiliki fitur ini. Mengakses array di luar batas adalah hal yang aneh untuk dilakukan; jenisnya hanya "salah" jika Anda melakukan hal yang tidak biasa (OOBing).

... kode kerja yang gagal dikompilasi

Saya tidak mengetahuinya - dapatkah Anda menunjukkannya secara spesifik?

Dalam kasus saya itu bukan array, tetapi ditutup sebagai penipuan jadi saya menganggap masalah ini seharusnya mencakup ini juga. Lihat #11186 untuk edisi asli saya. Saya mem-parsing file ke dalam peta dan kemudian memeriksanya terhadap undefined . IIRC Saya mendapatkan kesalahan pada perbandingan bahwa mereka tidak dapat tidak terdefinisi, meskipun mereka bisa (dan dulu).

Itu selalu diperbolehkan untuk membandingkan sesuatu dengan undefined

yang memalukan

const canWeDoIt = null === undefined; // yes we can!

Itu selalu diperbolehkan untuk membandingkan sesuatu dengan undefined

Sudah lama sekali, saya khawatir saya tidak ingat persis apa kesalahannya. Saya pasti memiliki kesalahan untuk kode yang berfungsi dengan baik (tanpa strictNullChecks ) dan itu membawa saya ke pengujian yang menghasilkan kasus di atas.

Jika saya punya waktu, saya akan melihat apakah saya bisa mengetahui dengan tepat apa itu lagi. Itu pasti terkait dengan pengetikan ini.

Sebagian besar diskusi ini telah difokuskan pada array, tetapi, menurut pengalaman saya, akses objek adalah tempat fitur ini akan sangat membantu. Misalnya, contoh (yang disederhanakan) dari basis kode saya ini terlihat masuk akal dan dapat dikompilasi dengan baik, tetapi jelas merupakan bug:

export type Chooser = (context?: Context) => number | string;
export interface Choices {
    [choice: number]: Struct;
    [choice: string]: Struct;
}

export const Branch = (chooser: Chooser, choices: Choices, context?: Context): Struct => {
    return choices[chooser(context)];  // Could be undefined
}

Mengenai objek dan hanya mengubah tanda tangan untuk memasukkan | undefined , @types/node melakukannya untuk process.env :

    export interface ProcessEnv {
        [key: string]: string | undefined;
    }

tetapi itu tidak memungkinkan mempersempit jenis sama sekali:

process.env.SOME_CONFIG && JSON.parse(process.env.SOME_CONFIG)

memberi

[ts]
Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
  Type 'undefined' is not assignable to type 'string'.

@felixfbecker

Saya percaya itu terkait dengan bug ini: https://github.com/Microsoft/TypeScript/issues/17960

Anda dapat mengatasinya dengan menetapkan env var ke variabel, dan kemudian menjaganya:

const foo = process.env.SOME_CONFIG
foo && JSON.parse(foo);

Menambahkan | undefined di beberapa pengetikan simpul dilakukan untuk memungkinkan augmentasi antarmuka ini untuk kenyamanan dengan properti yang sering digunakan untuk mendapatkan penyelesaian kode. ea

interface ProcessEnv {
  foo?: string;
  bar?: string;
}

Tanpa | undefined dalam tanda tangan indeks, TSC mengeluh dengan Property 'foo' of type 'string | undefined' is not assignable to string index type 'string'. .

Saya tahu bahwa ini memiliki biaya jika Anda harus bekerja dengan tipe yang memiliki | undefined ekstra dan yang tidak memilikinya.

Akan sangat bagus jika kita bisa menyingkirkan ini.

Saya mencoba mengumpulkan pemikiran saya tentang masalah ini.

Objek adalah tas campuran dalam JavaScript. Mereka dapat digunakan untuk dua tujuan :

  • kamus alias peta, di mana kuncinya tidak diketahui
  • catatan, di mana kuncinya diketahui

Untuk objek yang digunakan sebagai catatan, tanda tangan indeks tidak perlu digunakan. Sebaliknya, kunci yang dikenal didefinisikan pada jenis antarmuka. Pencarian kunci yang valid mengembalikan jenis yang sesuai, pencarian kunci yang tidak valid akan mundur ke tanda tangan indeks. Jika tidak ada tanda tangan indeks yang ditentukan (yang seharusnya tidak ada untuk objek yang digunakan sebagai catatan), dan noImplicitAny diaktifkan, ini akan error seperti yang diinginkan.

Untuk objek yang digunakan sebagai kamus (alias peta) dan array, tanda tangan indeks digunakan, dan kita dapat memilih untuk menyertakan | undefined dalam tipe nilai. Misalnya, { [key: index]: string | undefined } . Semua pencarian kunci valid (karena kunci tidak diketahui pada waktu kompilasi), dan semua kunci mengembalikan tipe yang sama (dalam contoh ini, T | undefined ).

Karena tanda tangan indeks hanya boleh digunakan untuk pola dan larik objek kamus, TypeScript diharapkan menerapkan | undefined dalam jenis nilai tanda tangan indeks: jika kunci tidak diketahui, dan pencarian kunci mungkin mengembalikan undefined .

Ada contoh bug yang mungkin tampak tidak terlihat tanpa ini, seperti Array.prototype.find kembali undefined , atau pencarian kunci seperti array[0] kembali undefined . (Destrukturisasi hanyalah sintaks gula untuk pencarian kunci.) Dimungkinkan untuk menulis fungsi seperti getKey untuk memperbaiki tipe pengembalian, tetapi kita harus mengandalkan disiplin untuk menegakkan penggunaan fungsi-fungsi ini di seluruh basis kode.

Jika saya mengerti dengan benar, masalahnya kemudian menjadi tentang refleksi atas objek dan larik kamus, sehingga ketika memetakan kunci objek atau larik kamus, pencarian kunci diketahui valid yaitu tidak akan mengembalikan undefined . Dalam hal ini, tipe nilai tidak diinginkan untuk menyertakan undefined . Dimungkinkan untuk menggunakan analisis aliran kontrol untuk memperbaikinya.

Apakah ini masalah yang luar biasa, atau apakah saya salah paham?

Kasus penggunaan dan masalah mana yang belum saya sebutkan?

Ada (setidaknya) dua masalah lain dengan menempatkan |undefined dalam definisi tipe objek Anda:

  • itu berarti Anda dapat menetapkan tidak terdefinisi ke dalam objek-objek ini, yang jelas tidak dimaksudkan
  • operasi lain, seperti Object.values ​​(atau _.values) akan mengharuskan Anda untuk menangani hasil yang tidak ditentukan

Saya pikir ini adalah poin yang sangat penting.

Saat ini, saya sedang bereksperimen dengan pendekatan berikut:

Tentukan const safelyAccessProperty = <T, K extends keyof T>(object: T, key: K): T[K] | undefined => object[key];

Kemudian akses properti seperti safelyAccessProperty(myObject, myKey) , alih-alih myObject[myKey] .

@plul Tangkapan yang bagus. Diskusi saat ini difokuskan pada operasi baca, tetapi definisi tipe pengindeks sebenarnya dua kali lipat, dan menambahkan | undefined akan memungkinkan penulisan nilai undefined .

Fungsi safelyAccessProperty Anda uji ( disebutkan di atas sebagai getKey oleh @OliverJAsh) memerlukan disiplin dan/atau aturan linter untuk melarang operasi pengindeksan pada semua array dan objek.

Ini dapat dibuat scalable jika fungsi disediakan pada semua instance array dan objek (setiap jenis yang menyediakan operasi pengindeks), seperti di C++ std::vector memiliki .at() yang melempar pengecualian saat runtime untuk akses OOB , dan operator [] yang tidak dicentang yang dalam kasus terbaik crash dengan SEGFAULT pada akses OOB, dalam kasus terburuk merusak memori.

Saya pikir masalah akses OOB tidak dapat dipecahkan dalam TypeScript/JavaScript pada tingkat definisi tipe saja, dan memerlukan dukungan bahasa untuk membatasi operasi pengindeks yang berpotensi berbahaya jika fitur ketat ini diaktifkan.

Sifat dua kali lipat dari pengindeks dapat dimodelkan sebagai properti dengan operasi get dan set sebagai fungsi, tetapi itu akan menjadi perubahan besar untuk semua definisi tipe pengindeks yang ada.

Satu ide yang sepertinya menjanjikan: bagaimana jika Anda dapat menggunakan ?: saat mendefinisikan tanda tangan indeks untuk menunjukkan bahwa Anda mengharapkan nilai terkadang hilang dalam penggunaan normal? Itu akan bertindak seperti | undefined disebutkan di atas, tetapi tanpa kerugian yang canggung. Itu perlu melarang nilai undefined eksplisit, yang saya kira berbeda dari biasanya ?: .

Ini akan terlihat seperti ini:

type NewWay = {[key: string]?: string};
const n: NewWay = {};

// Has type string | undefined
n['foo']

// Has type Array<string>
Object.values(n)

// Doesn't work
n['foo'] = undefined;

// Works
delete n['foo'];

Dibandingkan dengan pendekatan sebelumnya | undefined :

type OldWay = {[key: string]: string | undefined};
const o: OldWay = {};

// Has type string | undefined
o['foo']

// Has type Array<string | undefined>
Object.values(o)

// Works
o['foo'] = undefined;

// Works
delete o['foo'];

Saya datang ke sini karena menolak menambahkan | undefined di DT PR di atas, karena itu akan merusak semua pengguna API yang ada - dapatkah ini dilihat lebih baik karena memungkinkan pengguna memilih seberapa rewel yang mereka inginkan, daripada selain perpustakaan?


Saya akan mencatat properti opsional menambahkan | undefined juga, dan itu telah menggigit saya beberapa kali - pada dasarnya TS tidak membedakan antara properti yang hilang dan properti yang disetel ke tidak terdefinisi. Saya hanya ingin { foo?: T, bar?: T } diperlakukan sama dengan { [name: 'foo' | 'bar']: T } , apa pun caranya (lihat juga komentar process.env atas)


Apakah TS menentang pelanggaran simetri di sini pada pengindeks angka dan string?

foo[bar] && foo[bar].baz() adalah pola JS yang sangat umum, rasanya canggung ketika tidak didukung oleh TS (dalam arti mengingatkan Anda bahwa Anda perlu melakukannya jika Anda tidak menambahkan | undefined , dan peringatan ketika itu jelas tidak diperlukan jika Anda melakukannya).


Mengenai mutasi array selama iterasi yang melanggar jaminan ekspresi penjaga, itu juga mungkin dengan penjaga lain:

class Foo {
    foo: string | number = 123

    bar() {
        this.foo = 'bar'
    }

    broken() {
        if (typeof this.foo === 'number') {
            this.bar();
            this.foo.toLowerCase(); // right, but type error
            this.foo.toExponential(); // wrong, but typechecks
        }
    }
}

tapi saya kira itu jauh lebih kecil kemungkinannya dalam kode nyata bahwa loop lama mengubah iteratee.

Jelas bahwa ada permintaan untuk fitur ini. Saya sangat berharap tim TS akan menemukan beberapa solusi. Bukan hanya dengan menambahkan | undefined ke pengindeks karena memiliki masalah sendiri (sudah disebutkan) tetapi dengan cara yang lebih "pintar" (membaca mengembalikan T|undefined , menulis membutuhkan T , kompiler yang baik memeriksa for loop, dll. proposisi yang baik juga telah disebutkan.)

Kami baik-baik saja dengan kesalahan runtime ketika kami bermutasi dan bekerja dengan array yang tidak sepele, sulit untuk diverifikasi dengan cara kompiler. Kami hanya ingin memeriksa kesalahan untuk sebagian besar kasus dan baik-baik saja dengan menggunakan ! kadang-kadang.

Karena itu, jika penanganan array yang lebih ketat ini akan diterapkan, sekarang dengan #24897 dimungkinkan untuk menerapkan penyempitan tipe Nice saat memeriksa panjang array dengan konstan. Kita bisa mempersempit array menjadi Tuple dengan elemen istirahat.

let arr!: string[];
if (arr.length == 3) {
  //arr is of type [string, string, string]
}

if (arr.length > 3) {
  //arr is of type [string, string, string, string, ...string[]]
}

if (arr.length) {
  //arr is of type [string, ...string[]]
}

if (arr.length < 3) {
  //arr is of type [string?, string?, string?]
  if (arr.length > 0) {
    //arr is of type [string, string?, string?]
  }
}

Ini akan berguna ketika Anda mengindeks dengan konstan atau merusak array.

let someNumber = 55;
if (arr.length) {
  let el1 = arr[0]; //string
  let el2 = arr[1]; //string | undefined
  let el3 = arr[someNumber]; //string | undefined
}

if(arr.length >= 3){
    let [el1, el2, el3, el4] = arr;
    //el1, el2, el3 are string
    // el4 is string | undefined    
}

if (arr.length == 2){
    let [el1, el2, el3] = arr; //compiler error: "Tuple type '[string, string]' with length '2' cannot be assigned to tuple with length '3'.",
}

Pertanyaan lain adalah apa yang akan kita lakukan dengan jumlah besar seperti:

if(arr.length >= 99999){
    // arr is [string, string, ... , string, ...string[]]
}

Kami tidak dapat menampilkan jenis tuple besar ini dalam IDE atau pesan kompiler.

Saya kira kita bisa memiliki beberapa sintaks untuk mewakili "Tuple dengan panjang tertentu dengan tipe yang sama untuk semua item". Jadi misalnya Tuple 1000 string adalah string[10000] dan jenis array yang dipersempit dari contoh di atas bisa menjadi [...string[99999], ...string[]] .

Kekhawatiran lainnya adalah jika infrastruktur kompiler dapat mendukung tupel sebesar itu sekarang, dan jika tidak, seberapa sulit untuk melakukannya.

Objek

Saya selalu menginginkan tipe indeks [key: string (or number, symbol)]: V | undefined , hanya terkadang saya lupa tentang kasus undefined . Setiap kali seorang dev harus secara eksplisit memberi tahu kompiler "percayalah, ini adalah jenis hal yang nyata" Anda tahu itu tidak aman.
Sangat tidak masuk akal untuk mengetik Map.get dengan benar (secara ketat) tetapi entah bagaimana objek biasa mendapatkan izin masuk gratis.
Namun, ini mudah diperbaiki di lahan pengguna, jadi tidak terlalu buruk. Saya tidak punya solusi, bagaimanapun caranya.

Array

Mungkin saya melewatkan sesuatu tetapi tampaknya argumen bahwa "Anda hampir tidak pernah mengakses Array dengan cara yang tidak aman" dapat berjalan dua arah, terutama dengan flag compiler baru.

Saya cenderung berpikir semakin banyak orang mengikuti dua praktik terbaik ini:

  • Gunakan metode atau pustaka asli fungsional untuk mengulangi atau mengubah Array. Tidak ada akses braket di sini.
  • Jangan bermutasi Array di tempat

Dengan mengingat hal ini, satu-satunya kasus yang tersisa dan jarang terjadi di mana Anda memerlukan logika akses braket tingkat rendah akan benar-benar mendapat manfaat dari keamanan tipe.

Yang ini sepertinya tidak punya otak dan saya tidak berpikir menyalin-menempelkan seluruh lib.d.ts secara lokal adalah solusi yang dapat diterima.

Ketika kita secara eksplisit mengindeks ke dalam array/objek, misalnya untuk mendapatkan item pertama dari sebuah array ( array[0] ), kita ingin hasilnya menyertakan undefined .

Ini dimungkinkan dengan menambahkan undefined ke tanda tangan indeks.

Namun, jika saya mengerti dengan benar, masalah dengan menyertakan undefined dalam tanda tangan indeks adalah, saat memetakan array atau objek, nilainya akan menyertakan undefined meskipun diketahui tidak menjadi undefined (jika tidak, kami tidak akan memetakannya).

Jenis/tanda tangan indeks digunakan untuk pencarian indeks (misalnya array[0] ) dan pemetaan (misalnya for loop dan Array.prototype.map ), tetapi kami memerlukan jenis yang berbeda untuk setiap kasus ini.

Ini bukan masalah dengan Map karena Map.get adalah fungsi, dan oleh karena itu tipe pengembaliannya dapat dipisahkan dari tipe nilai dalam, tidak seperti pengindeksan ke dalam array/objek yang bukan fungsi, dan karena itu menggunakan tanda tangan indeks secara langsung.

Jadi, pertanyaannya menjadi: bagaimana kita bisa memenuhi kedua kasus tersebut?

// Manually adding `undefined` to the index signature
declare const array: (number | undefined)[];

const first = array[0]; // number | undefined, as desired :-)
type IndexValue = typeof array[0]; // number | undefined, as desired! :-)

array.map(x => {
  x // number | undefined, not desired! :-(
})

Usul

Opsi kompiler yang memperlakukan pencarian indeks (misalnya array[0] ) mirip dengan cara mengetik Set.get dan Map.get , dengan menyertakan undefined dalam tipe nilai indeks (bukan tanda tangan indeks itu sendiri). Tanda tangan indeks itu sendiri tidak akan menyertakan undefined , sehingga fungsi pemetaan tidak terpengaruh.

Contoh:

declare const array: number[];

// The compiler option would include `undefined` in the index value type
const first = array[0]; // number | undefined, as desired :-)
type IndexValue = typeof array[0]; // number | undefined, as desired :-)

array.map(x => {
  x // number, as desired :-)
})

Namun ini tidak akan menyelesaikan kasus perulangan array/objek menggunakan loop for , karena teknik itu menggunakan pencarian indeks.

for (let i = 0; i < array.length; i++) {
  const x = array[i];
  x; // number | undefined, not desired! :-(
}

Bagi saya dan saya menduga banyak orang lain, ini dapat diterima karena for loop tidak digunakan, alih-alih lebih suka menggunakan gaya fungsional, misalnya Array.prototype.map . Jika kami memang harus menggunakannya, kami akan senang menggunakan opsi kompiler yang disarankan di sini bersama dengan operator pernyataan non-null.

for (let i = 0; i < array.length; i++) {
  const x = array[i]!;
  x; // number, as desired :-)
}

Kami juga dapat menyediakan cara untuk ikut serta atau keluar, misalnya dengan beberapa sintaks untuk menghias tanda tangan indeks (maafkan sintaks ambigu yang saya buat sebagai contoh). Sintaks ini hanya akan menjadi cara untuk memberi sinyal perilaku mana yang kita inginkan untuk pencarian indeks.

Opt-out (opsi kompiler diaktifkan secara default, opt-out jika diperlukan):

declare const array: { [index: number]!!: string };

declare const dictionary: { [index: string]!!: string }

Keikutsertaan (tidak ada opsi kompiler, cukup ikut serta jika diperlukan):

declare const array: { [index: string]!!: string };

declare const dictionary: { [index: string]??: string }

Saya belum membaca tentang masalah ini atau pro dan kontra, berbagai proposal, dll. (baru saja menemukannya di Google setelah berulang kali terkejut bahwa akses array/objek tidak ditangani secara konsisten dengan pemeriksaan nol yang ketat), tetapi saya memiliki saran terkait: opsi untuk membuat inferensi tipe array seketat mungkin kecuali secara khusus diganti.

Sebagai contoh:

const balls = [1, 2 ,3];

Secara default, balls akan diperlakukan sebagai [number, number, number] . Ini dapat ditimpa dengan menulis:

const balls: number[] = [1, 2 ,3];

Selanjutnya, akses elemen Tuple akan ditangani secara konsisten dengan pemeriksaan nol yang ketat. Sangat mengejutkan bagi saya bahwa dalam contoh berikut n saat ini disimpulkan sebagai number bahkan dengan pemeriksaan null yang ketat diaktifkan.

const balls: [number, number, number] = [1, 2 ,3];
const n = balls[100];

Saya juga mengharapkan metode mutasi array seperti .push tidak ada dalam definisi tipe Tuple, karena metode tersebut mengubah tipe run-time menjadi tidak konsisten dengan tipe waktu kompilasi.

Bagus! Nah itu waktu yang menarik. Saya sudah membuka pengumuman rilis, tetapi belum membacanya; hanya datang ke sini setelah mengalami situasi di mana saya perlu melakukan casting aneh ( (<(T|undefined)[]> arr).slice(-1)[0] ) untuk membuat TypeScript (2.9) melakukan apa yang saya inginkan.

Hanya ingin mengembalikan saran ini: https://github.com/Microsoft/TypeScript/issues/13778#issuecomment -383072468

Ini akan memperbaiki masalah dengan tipe terindeks yang saya alami. Akan sangat bagus jika itu adalah default, tetapi saya mengerti bahwa itu akan merusak banyak hal di dunia nyata.

@mhegazy @RyanCavanaugh Ada pendapat tentang proposal saya? https://github.com/Microsoft/TypeScript/issues/13778#issuecomment -406316164

Bagi saya, ada solusi sederhana. Aktifkan bendera yang memeriksa ini. Kemudian, alih-alih:

const array = [1, 2, 3];
for (var i =0; i< array.length; i++) {
    array[i]+=1; // array[i] is possibly undefined
}

Anda melakukan:

const array = [1, 2, 3];
array.forEach((value, i) => array[i] = value + 1);

Kemudian, ketika melakukan akses indeks acak, Anda diminta untuk memeriksa apakah hasilnya tidak terdefinisi, tetapi tidak saat mengulangi koleksi yang disebutkan.

Saya masih berpikir ini menjamin memiliki masalah terbuka.

Sebagai seorang programmer yang baru mengenal TypeScript, saya menemukan situasi di sekitar pengindeksan objek dalam mode ketat menjadi tidak intuitif. Saya mengharapkan hasil pencarian menjadi T | undefined , mirip dengan Map.get . Bagaimanapun, saya baru saja mengalami ini baru-baru ini, dan membuka masalah di perpustakaan:

screeper/ketik-screep#107

Saya mungkin akan menutupnya sekarang, karena sepertinya tidak ada solusi yang baik. Saya pikir saya akan mencoba "ikut serta" ke T | undefined dengan menggunakan sedikit fungsi utilitas:

export function lookup<T>(map: {[index: string]: T}, index: string): T|undefined {
  return map[index];
}

Ada beberapa saran di sini untuk secara eksplisit menetapkan T | undefined sebagai tipe pengembalian operasi indeks objek, namun itu tampaknya tidak berfungsi:

const obj: {[key: string]: number | undefined} = {
  "a": 1,
  "b": 2,
};

const test = obj["c"]; // const test: number

Ini adalah VSCode versi 1.31.1

@yawaramin Pastikan Anda mengaktifkan strictNullChecks di tsconfig.json (yang juga diaktifkan oleh flag strict )

Jika kasus penggunaan Anda memerlukan pengindeksan sewenang-wenang pada array dengan panjang yang tidak diketahui, saya pikir perlu menambahkan undefined secara eksplisit (jika hanya untuk "mendokumentasikan" ketidakamanan itu).

const words = ... // some string[] that could be empty
const x = words[0] as string | undefined
console.log(x.length) // TS error

Tuple bekerja untuk array kecil dengan panjang yang diketahui. Mungkin kita bisa memiliki sesuatu seperti string[5] sebagai singkatan untuk [string, string, string, string, string] ?

Sangat mendukung ini sebagai pilihan. Ini adalah lubang yang terlihat pada sistem tipe, terutama ketika flag strictNullChecks diaktifkan. Objek biasa digunakan sebagai peta sepanjang waktu di JS, jadi TypeScript harus mendukung use case itu.

Mengalami ini dengan penghancuran array dari parameter fungsi:

function foo([first]: string[]) { /* ... */ }

Di sini saya berharap a bertipe string | undefined tapi itu hanya string , kecuali saya melakukannya

function foo([first]: (string | undefined)[]) { /* ... */ }

Saya tidak berpikir kami memiliki satu for loop di basis kode kami, jadi saya akan dengan senang hati menambahkan flag ke opsi kompiler tsconfig saya (bisa bernama strictIndexSignatures ) untuk mengaktifkan perilaku ini untuk proyek kami.

Inilah cara saya mengatasi masalah ini: https://github.com/danielnixon/total-functions/

Solusi yang bagus, saya sangat berharap ini dikunci oleh tim TypeScript.

Ketika seorang programmer membuat asumsi dalam kode tertulis, dan kompiler tidak dapat menyimpulkan bahwa itu disimpan, ini akan menghasilkan kesalahan kompiler kecuali IMHO dibungkam.

Perilaku ini juga akan sangat membantu dalam kombinasi dengan operator rantai opsional baru.

Saya mengalami masalah dengan menggunakan | undefined dengan peta hari ini saat menggunakan Object.entries() .

Saya memiliki tipe Indeks yang dijelaskan dengan cukup baik oleh {[key: string]: string[]} , dengan peringatan yang jelas bahwa tidak setiap kunci string yang mungkin diwakili dalam Indeks. Namun, saya menulis bug yang tidak ditangkap TS saat mencoba menggunakan nilai yang dicari dari Indeks; tidak menangani kasus yang tidak ditentukan.

Jadi saya mengubahnya menjadi {[key: string]: string[] | undefined} seperti yang disarankan, tetapi sekarang ini menyebabkan masalah dengan penggunaan Object.entries() . TypeScript sekarang mengasumsikan (cukup, berdasarkan spesifikasi tipe) bahwa Indeks mungkin memiliki kunci yang memiliki nilai yang tidak ditentukan, dan dengan demikian mengasumsikan hasil pemanggilan Object.entries() di dalamnya mungkin berisi nilai yang tidak ditentukan.

Namun, saya tahu ini tidak mungkin; satu-satunya saat saya harus mendapatkan undefined hasil adalah mencari kunci yang tidak ada, dan itu tidak akan terdaftar saat menggunakan Object.entries() . Jadi untuk membuat TypeScript senang, saya harus menulis kode yang tidak memiliki alasan nyata untuk ada, atau mengesampingkan peringatan, yang saya lebih suka untuk tidak melakukannya.

@RyanCavanaugh , saya menganggap balasan asli Anda untuk ini masih posisi tim TS saat ini? Keduanya sebagai benjolan karena tidak ada yang membaca utas dan memeriksa jika beberapa tahun lagi pengalaman dengan koleksi JS primitif yang lebih kuat, dan memperkenalkan beberapa opsi lain untuk meningkatkan keketatan karena telah mengubah apa pun.

(Contohnya masih agak tidak meyakinkan bagi saya, tetapi utas ini sudah membuat semua argumen, komentar lain tidak akan mengubah apa pun)

Jika seseorang ingin memodifikasi lib.d.ts mereka dan memperbaiki semua jeda hilir dalam kode mereka sendiri dan menunjukkan seperti apa perbedaan keseluruhan untuk menunjukkan bahwa ini memiliki beberapa proposisi nilai, kami terbuka untuk data itu.

@RyanCavanaugh Berikut adalah beberapa kasus dari basis kode saya di mana beberapa runtime crash tertidur. Perhatikan bahwa saya sudah memiliki kasus di mana saya mengalami crash dalam produksi karena ini dan perlu merilis perbaikan terbaru.

src={savedAdsItem.advertImageList.advertImage[0].mainImageUrl || undefined}
return advert.advertImageList.advertImage.length ? advert.advertImageList.advertImage[0].mainImageUrl : ''
birthYear: profileData.birthYear !== null ? profileData.birthYear : allowedYears[0].value,
upsellingsList.upsellingProducts[0].upsellingProducts[0].selected = true
const latitude = parseFloat(coordinates.split(',')[0])
const advert = Object.values(actionToConfirm.selectedItems)[0]
await dispatch(deactivateMyAd(advert))

Dalam hal ini akan mengganggu karena ArticleIDs extends articleNames[] menyertakan undefined dalam nilai yang dihasilkan, sementara itu hanya mengizinkan subset yang ditentukan sepenuhnya. Mudah diperbaiki dengan menggunakan ReadonlyArray<articleNames> alih-alih articleNames[] .

export enum articleNames {
    WEB_AGB = 'web_agb',
    TERMS_OF_USE = 'web_terms-of-use',
}
export const getMultipleArticles = async <ArticleIDs extends articleNames[], ArticleMap = { [key in ArticleIDs[number]]: CmsArticle }>(ids: ArticleIDs): Promise<ArticleMap> => {...}

Secara keseluruhan, saya benar-benar ingin memiliki keamanan tipe ekstra ini untuk mencegah kemungkinan crash runtime.

Saya masuk ke penalaran sedikit lebih banyak di komentar ini #11238 (komentar)

Pikirkan dua jenis kunci di dunia: Kunci yang Anda tahu _do_ memiliki properti yang sesuai di beberapa objek (aman), yang Anda _tidak_ tahu memiliki properti yang sesuai di beberapa objek (berbahaya).

Anda mendapatkan jenis kunci pertama, kunci "aman", dengan menulis kode yang benar seperti

for (let i = 0; i < arr.length; i++) {
  // arr[i] is T, not T | undefined

atau

for (const k of Object.keys(obj)) {
  // obj[k] is T, not T | undefined

Anda mendapatkan jenis kedua dari kunci, jenis "berbahaya", dari hal-hal seperti input pengguna, atau file JSON acak dari disk, atau beberapa daftar kunci yang mungkin ada tetapi mungkin tidak.

Jadi, jika Anda memiliki kunci dari jenis yang berbahaya dan diindeks olehnya, alangkah baiknya jika Anda memiliki | undefined di sini. Tapi proposalnya bukan "Perlakukan kunci _berbahaya_ sebagai berbahaya", itu "Perlakukan kunci _semua_, bahkan yang aman, sebagai berbahaya". Dan begitu Anda mulai memperlakukan kunci aman sebagai berbahaya, hidup benar-benar menyebalkan. Anda menulis kode seperti

for (let i = 0; i < arr.length; i++) {
  console.log(arr[i].name);

dan TypeScript mengeluh kepada Anda bahwa arr[i] mungkin undefined meskipun _hey lihat saya baru saja @#%#ing mengujinya_. Sekarang Anda terbiasa menulis kode seperti ini, dan rasanya bodoh:

for (let i = 0; i < arr.length; i++) {
  // TypeScript makes me use ! with my arrays, sad.
  console.log(arr[i]!.name);

Atau mungkin Anda menulis kode seperti ini:

function doSomething(myObj: T, yourObj: T) {
  for (const k of Object.keys(myObj)) {
    console.log(yourObj[k].name);
  }
}

dan TypeScript mengatakan "Hei, ekspresi indeks itu mungkin | undefined , jadi Anda dengan patuh memperbaikinya karena Anda sudah melihat kesalahan ini 800 kali:

function doSomething(myObj: T, yourObj: T) {
  for (const k of Object.keys(myObj)) {
    console.log(yourObj[k]!.name); // Shut up TypeScript I know what I'm doing
  }
}

Tapi Anda tidak memperbaiki bug . Anda bermaksud menulis Object.keys(yourObj) , atau mungkin myObj[k] . Itu adalah jenis kesalahan kompiler terburuk, karena itu tidak benar-benar membantu Anda dalam skenario apa pun - itu hanya menerapkan ritual yang sama untuk setiap jenis ekspresi, tanpa memperhatikan apakah itu sebenarnya lebih berbahaya daripada ekspresi lain dari bentuk yang sama.

Saya memikirkan yang lama "Apakah Anda yakin ingin menghapus file ini?" dialog. Jika dialog itu muncul setiap kali Anda mencoba menghapus file, Anda akan segera belajar untuk menekan del y ketika Anda biasa menekan del , dan peluang Anda untuk tidak menghapus sesuatu yang penting reset ke pra -dialog dasar. Jika dialog hanya muncul saat Anda menghapus file saat tidak masuk ke tempat sampah daur ulang, sekarang Anda memiliki keamanan yang berarti. Tapi kami tidak tahu (atau _could_ we) apakah kunci objek Anda aman atau tidak, jadi tunjukkan "Apakah Anda yakin ingin mengindeks objek itu?" dialog setiap kali Anda melakukannya sepertinya tidak akan menemukan bug pada tingkat yang lebih baik daripada tidak menampilkan semuanya.

Saya setuju dengan analogi dialog hapus file, namun saya pikir analogi ini juga dapat diperluas untuk memaksa pengguna memeriksa sesuatu yang mungkin tidak terdefinisi atau nol, jadi penjelasan ini tidak terlalu masuk akal, karena jika penjelasan ini benar, opsi strictNullChecks akan menginduksi perilaku yang sama, misalnya mendapatkan beberapa elemen dari DOM menggunakan document.getElementById .

Tapi bukan itu masalahnya, banyak pengguna TypeScript menginginkan kompiler untuk menaikkan tanda tentang kode tersebut sehingga kasus tepi tersebut dapat ditangani dengan tepat daripada melemparkan kesalahan Cannot access property X of undefined yang sangat sangat sulit untuk dilacak.

Pada akhirnya, saya berharap fitur semacam ini dapat diimplementasikan sebagai opsi kompiler TypeScript tambahan, karena itulah alasan pengguna ingin menggunakan TypeScript, mereka ingin diperingatkan tentang kode berbahaya.

Berbicara tentang mengakses array atau objek secara salah tidak mungkin terjadi, apakah Anda memiliki data untuk mencadangkan klaim ini? Atau hanya berdasarkan firasat yang sewenang-wenang?

for (let i = 0; i < arr.length; i++) {
  console.log(arr[i].name);

Analisis Jenis Berbasis Aliran Kontrol TypeScript dapat ditingkatkan untuk mengenali kasus ini agar aman dan tidak memerlukan ! . Jika manusia dapat menyimpulkan itu aman, kompiler juga bisa.
Saya sadar bahwa ini mungkin tidak sepele untuk diterapkan di kompiler.

Analisis Jenis Berbasis Aliran Kontrol TypeScript dapat ditingkatkan untuk mengenali kasus ini agar aman

Itu benar-benar tidak bisa.

declare function someFunc(arr: number[], i: number): void;
let arr = [1, 2, 3, 4];
for (let i = 0; i < arr.length; i++) {
  someFunc(arr, arr[i]);
}

Apakah fungsi ini meneruskan undefined ke someFunc pada lintasan kedua melalui loop, atau bukan? Ada banyak hal yang bisa saya tulis di someFunc yang akan menghasilkan undefined muncul nanti.

Bagaimana dengan ini?

declare function someFunc(arr: number[], i: number): void;
let arr = [1, 2, 3, 4];
let alias = arr;
for (let i = 0; i < arr.length; i++) {
  someFunc(alias, arr[i]);
}

@fabb izinkan saya memberikan contoh lain:

```
$ simpul

konstanta arr = []
tidak terdefinisi
arr[7] = 7
7
arr
[ <7 item kosong>, 7 ]
for (misalkan i = 0; i < arr.length; i++) {
... console.log(arr[i])
... }
tidak terdefinisi
tidak terdefinisi
tidak terdefinisi
tidak terdefinisi
tidak terdefinisi
tidak terdefinisi
tidak terdefinisi
7
tidak terdefinisi```

@RyanCavanaugh karena Anda di sini, bagaimana kalau menyimpulkan item :: T untuk arr :: T[] di for (const item of arr) ... , dan sebaliknya menyimpulkan arr[i] :: T | undefined saat menggunakan beberapa --strict-index ? Bagaimana dengan kasus yang saya pedulikan, obj[key] :: V | undefined tapi Object.values(obj) :: V[] untuk obj :: { [key: string]: V } ?

@yawaramin Jika Anda menggunakan array yang jarang, TypeScript sudah tidak melakukan hal yang benar. Bendera --strict-index akan memperbaikinya. Ini mengkompilasi:

const arr = []
arr[7] = 7

for (let i = 0; i < arr.length; i++) {
    console.log(Math.sqrt(arr[i]));
}

@RyanCavanaugh Ada satu contoh yang sangat umum yang dapat saya tunjukkan kepada Anda di mana pengguna cenderung mengakses array secara tidak benar.

const getBlock = (unitNumber: string): string => unitNumber.split('-')[0]

Kode di atas tidak boleh melewati pemeriksaan kompiler dengan anggun di bawah strictNullChecks , karena beberapa penggunaan getBlock akan mengembalikan tidak terdefinisi, misalnya getBlock('hello') , dalam kasus seperti itu saya sangat menginginkan mengkompilasi bendera kenaikan sehingga saya dapat menangani kasus yang tidak terdefinisi dengan anggun tanpa meledakkan aplikasi saya.

Dan ini juga berlaku untuk banyak idiom umum seperti mengakses elemen terakhir dari array dengan arr.slice(-1)[0] , mengakses elemen pertama arr[0] dll.

Pada akhirnya, saya ingin TypeScript mengganggu saya karena kesalahan seperti itu daripada harus berurusan dengan aplikasi yang meledak.

Apakah fungsi ini meneruskan undefined ke someFunc pada lintasan kedua melalui loop, atau bukan? Ada banyak hal yang bisa saya tulis di someFunc yang akan menghasilkan tampilan yang tidak ditentukan nanti.

@RyanCavanaugh Ya, JavaScript tidak cocok untuk kekekalan. Dalam hal ini ! harus diperlukan, atau array ReadonlyArray : someFunc(arr: ReadonlyArray<number>, i: number) .

@yawaramin Untuk array yang jarang, tipe elemen mungkin perlu menyertakan undefined kecuali TypeScript dapat menyimpulkan bahwa itu digunakan seperti Tuple. Dalam kode yang ditautkan oleh @danielnixon (https://github.com/microsoft/TypeScript/issues/13778#issuecomment-536248028), tupel juga diperlakukan khusus dan tidak menyertakan undefined dalam tipe elemen yang dikembalikan karena kompilator menjamin bahwa hanya set indeks yang diakses.

ini adalah keputusan sadar. akan sangat mengganggu jika kode ini menjadi kesalahan:

var a = [];
for (var i =0; i< a.length; i++) {
    a[i]+=1; // a[i] is possibly undefined
}

Hei, saya mengenali sintaks itu; saya menulis loop seperti ini mungkin sekali per tahun!

Saya menemukan bahwa dalam kebanyakan kasus di mana saya benar-benar melakukan indeks ke dalam array, saya sebenarnya ingin memeriksa undefined sesudahnya.

Kekhawatiran membuat kasus khusus ini lebih sulit untuk ditangani tampaknya berlebihan. Jika Anda hanya ingin mengulangi elemen, Anda dapat menggunakan for .. of . Jika Anda memerlukan indeks elemen karena alasan tertentu, gunakan forEach atau ulangi entries . Secara umum, sangat jarang Anda benar-benar membutuhkan perulangan for berbasis indeks.

Jika ada alasan yang lebih baik mengapa seseorang menginginkan status quo, saya ingin melihatnya, tetapi terlepas dari itu: Ini adalah inkonsistensi dalam sistem dan memiliki tanda untuk memperbaikinya akan sangat dihargai oleh banyak orang, tampaknya.

Hai semuanya, saya merasa banyak diskusi yang bisa dilakukan di sini adalah
telah. Pemilik paket sudah cukup jelas tentang alasan mereka dan
telah mempertimbangkan sebagian besar argumen ini. Jika mereka berencana untuk mengatasi
ini, saya yakin mereka akan mengumumkannya. Selain itu, saya tidak berpikir ini
benang benar-benar produktif.

Pada Jumat, 25 Oktober 2019 pukul 11:59, brunerh [email protected] menulis:

ini adalah keputusan sadar. akan sangat mengganggu jika kode ini
menjadi kesalahan:

var a = [];untuk (var i =0; i< a.length; i++) {
a[i]+=1; // a[i] mungkin tidak terdefinisi
}

Hei, saya mengenali sintaks itu; saya menulis loop seperti ini mungkin sekali per tahun!

Saya menemukan bahwa dalam kebanyakan kasus di mana saya benar-benar melakukan indeks ke dalam array, saya
sebenarnya ingin memeriksa undefined sesudahnya.

Kekhawatiran membuat kasus khusus ini lebih sulit untuk ditangani tampaknya berlebihan.
Jika Anda hanya ingin mengulangi elemen yang dapat Anda gunakan untuk .. dari. Jika
anda memerlukan indeks elemen untuk beberapa alasan, gunakan forEach atau iterate over
entri. Secara umum, sangat jarang Anda benar-benar membutuhkan
berbasis indeks untuk loop.

Jika ada alasan yang lebih baik mengapa seseorang menginginkan status quo, saya akan
ingin melihatnya, tetapi terlepas dari itu: Ini adalah inkonsistensi dalam sistem
dan memiliki bendera untuk memperbaikinya akan sangat dihargai oleh banyak orang, tampaknya.


Anda menerima ini karena Anda berkomentar.
Balas email ini secara langsung, lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/13778?email_source=notifications&email_token=ACAJU3DQ7U6Y3MUUM26J4JDQQM62XA5CNFSM4C6KEKAKYY3PNVWWK3TUL52HS4DFVREXG43VMVBWZ25JDQQM62XA5CNFSM4C6KEKAKYY3PNVWWK3TUL52HS4DFVREXG43VMVBWZ25JEM467WZ2Z25DNIMVXHJKT
atau berhenti berlangganan
https://github.com/notifications/unsubscribe-auth/ACAJU3EWVM3CUFG25UF5PGDQQM62XANCNFSM4C6KEKAA
.

Saya merasa, meskipun mungkin tidak ada pergerakan dari pemilik paket, umpan balik lanjutan dari komunitas masih berharga, karena ini menunjukkan masih ada permintaan untuk perangkat yang lebih baik seputar masalah ini.

@brunnerh Saya setuju dengan Anda, saat ini saya bahkan tidak perlu menggunakan for loop kecuali untuk penyetelan kinerja, yang terjadi 0% dari waktu di basis kode perusahaan saya, karena sebagian besar waktu mengubah peta /filter/reduce to for loop jarang meningkatkan kinerja, penyebab sebenarnya selalu tentang logika yang tidak efisien, masalah jaringan, dan koneksi database.

Saya terkejut belum ada yang membicarakan as const .

const test = [1, 2, 3] as const;

(test[100]).toFixed(5);
// Tuple type 'readonly [1, 2, 3]' of length '3' has no element at index '100'.

Lebih umum, dan tidak persis terkait dengan pesan awal masalah ini Saya telah menggunakan pola pemrograman defensif berikut selama beberapa bulan terakhir, dan itu bekerja dengan baik (untuk saya)

const xs: Array<number | undefined> = [1,2,3];

// for objects but kind of related as well
Record<string, User | undefined>

interface Something {
  [key: string]: User | undefined
}

Meskipun tidak ada notasi pendek untuk itu (seperti ? ), saya merasa itu baik-baik saja. Memberi tahu kompiler sendiri bahwa Anda mungkin tidak yakin nilainya didefinisikan tidak apa-apa.

@martpie as const sangat bagus jika Anda dapat menggunakannya, yaitu.

Setidaknya ada dua alasan mengapa saya lebih suka tidak menggunakan Array<T | undefined> :

  1. Ini adalah satu hal lagi yang harus Anda ingat untuk dilakukan setiap kali Anda menggunakan array. Juga, Anda tidak bisa hanya menggunakan pengetikan implisit lagi, yang bagus untuk dimiliki.
  2. Itu mengubah tanda tangan forEach , map dan filter , yang tidak lulus undefined sebagai argumen elemen (kecuali indeks itu secara eksplisit diatur seperti itu). Bergantung pada seberapa banyak Anda menggunakan fungsi-fungsi yang dapat mengganggu untuk ditangani.

Saya juga ingin mengatakan bahwa ini menyebabkan banyak kesalahan positif di eslint/TypeScript sekarang:

const a: string[] = [];
const foo = a[1000];
if (foo) { // eslint says this is an unnecessary conditional
  console.log(foo.length);
}

Eslint (dengan benar) menyimpulkan dari tipe bahwa ini adalah pemeriksaan yang tidak perlu, karena foo tidak pernah bisa nol. Jadi sekarang saya tidak hanya harus secara sadar mengingat untuk melakukan pemeriksaan nol pada hal-hal yang saya dapatkan kembali dari sebuah array, saya _juga_ harus menambahkan baris penonaktifan eslint! Dan mengakses hal-hal di luar for loop seperti ini sebenarnya adalah satu-satunya cara kami melakukan akses array, karena (seperti mungkin kebanyakan pengembang TS akhir-akhir ini), kami menggunakan fungsi forEach/map/filter/etc saat mengulang array.

Saya benar-benar berpikir kita harus memiliki flag kompiler baru yang disetel ke true secara default jika strict benar, dan jika orang tidak menyukainya, mereka dapat memilih keluar. Kami telah menambahkan pemeriksaan kompiler ketat yang melanggar baru di versi terbaru.

Ini adalah sumber kemungkinan besar _only_ runtime prod bug yang saya lihat di memori baru-baru ini yang merupakan kegagalan sistem tipe.

Mempertimbangkan ergonomi dari operator rantai opsional yang sekarang tersedia, mungkin sudah waktunya untuk meninjau kembali keputusan untuk _tidak_ membuat perilaku ini tersedia melalui bendera?

Jika saya mendapatkan array Thing dari server, dan saya perlu menampilkan Thing dalam array, sesuatu yang biasa saya temui adalah array tersebut sebenarnya kosong, dan mengakses properti pada Thing membuat aplikasi mogok.

Contoh:

// `things` is `Thing[]`, but is empty, i.e., `[]`
const { things } = data; 

// We are accessing `things[-1]`, which is obviously `undefined`, 
// but TypeScript thinks `latestThing` is a `Thing`
const latestThing = things[things.length - 1];

// TypeError: Cannot read property 'foo' of undefined
return latestThing.foo; 

Mungkin ini adalah masalah dengan desain API kami, tetapi rasanya TS benar-benar harus memahami bahwa ketika Anda mengakses sesuatu dalam array, itu mungkin tidak benar-benar ada di sana.

Sunting: Hanya ingin mengklarifikasi maksud saya "API kami" seperti pada "API yang dibuat oleh tim yang saya kerjakan"

ya, semua orang cukup setuju bahwa ini adalah keputusan desain yang sangat buruk, yang kontemporer hingga yang jauh ketika TS benar-benar tidak aman.

Sebagai solusinya, saya menggunakan hack kotor kecil. Saya baru saja menambahkan package.json skrip postinstall sederhana:

{
...
  "scripts": {
    "postinstall": "sed -i 's/\\[n: number\\]: T;/[n: number]: T | undefined;/g' node_modules/typescript/lib/lib.es5.d.ts",
    ...
  },
...
}

Tentu saja, itu tidak akan berfungsi di windows, tetapi itu lebih baik daripada tidak sama sekali.

Semua masalah lain yang berjalan berdekatan dengan yang ini tampaknya dialihkan ke sini, jadi saya akan menganggap ini adalah tempat terbaik untuk bertanya: apakah sintaks steno untuk tanda tangan indeks opsional tidak ada di tabel? Seperti yang ditunjukkan oleh interface Foo { [k: string]: Bar | undefined; } yang kurang ergonomis dari interface Foo { [k: string]?: Bar; } .

Bisakah kami mendapatkan dukungan operator ?: ? Jika tidak, mengapa tidak? Artinya, manfaat ergonomis cukup untuk menambahkan fitur untuk definisi properti tunggal -- bukankah "cukup membantu" untuk tanda tangan indeks?

type Foo = { [_ in string]?: Bar } juga berfungsi. Tidak begitu cantik, tapi cukup singkat. Bisa juga membuat tipe Dict Anda sendiri jika Anda mau. Namun, tidak akan menentang ekstensi ?:

Ini mulai terasa seperti salah satu pembicaraan "Javascript: The Bad Parts":

ts type Foo1 = { [_ in string]?: Bar } // Yup type Foo2 = { [_: string]?: Bar } // Nope interface Foo3 { k?: Bar } // Yup interface Foo4 { [_: string]?: Bar } // Nope

Menggunakan T | undefined untuk jenis tanda tangan benar-benar tidak berguna. Kita membutuhkan cara untuk membuatnya agar operator indeks [n] memiliki tipe T|undefined , tetapi misal menggunakan map pada array seharusnya tidak memberi kita T|undefined nilai-nilai, karena dalam situasi itu kita harus tahu mereka ada.

@radix Untuk fungsi aktual pada Array, saya rasa ini tidak akan menjadi masalah karena mereka semua memiliki definisi tipe sendiri yang menghasilkan tipe yang tepat: misalnya map : https://github.com/microsoft /TypeScript/blob/master/lib/lib.es5.d.ts#L1331

Satu-satunya penggunaan kode yang umum bahwa konstruksi | undefined menimbulkan regresi pengalaman yang sebenarnya adalah dalam loop for ... of . Sayangnya (dari perspektif masalah ini), itu adalah konstruksi yang agak umum.

@riggs Saya pikir Anda sedang berbicara tentang membuat interface Array<T> { } have [index: number]: T | undefined , tetapi @radix kemungkinan berbicara tentang apa yang tampaknya menjadi rekomendasi saat ini, yaitu menggunakan Array<T | undefined> di kode Anda sendiri.

Yang terakhir ini buruk karena beberapa alasan, paling tidak di antaranya adalah Anda tidak mengontrol jenis paket lain, tetapi yang pertama juga memiliki beberapa masalah, yaitu Anda dapat menetapkan undefined ke array, dan itu memberikan undefined bahkan dalam kasus yang diketahui aman. ️

Ahh, ya, kesalahpahaman saya. Saya, memang, hanya mengacu pada penggunaan definisi [index: number]: T | undefined . Saya sepenuhnya setuju bahwa mendefinisikan tipe sebagai Array<T | undefined> adalah solusi yang buruk yang menyebabkan lebih banyak masalah daripada yang dipecahkan.

Apakah ada cara yang bagus untuk mengganti lib.es5.d.ts atau apakah skrip pascainstal adalah cara terbaik untuk melakukannya?

@nicu-chiciuc https://www.npmjs.com/package/patch-package Benar-benar cocok dengan kotak peralatan bidat bersama https://www.npmjs.com/package/yalc

Apakah ada cara yang anggun untuk mengganti lib.es5.d.ts atau apakah skrip pascainstal adalah cara terbaik untuk melakukannya?

Dalam proyek kami, kami memiliki file global.d.ts yang kami gunakan untuk 1) menambahkan definisi tipe untuk API bawaan yang belum ada dalam definisi tipe default TypeScript (mis. API terkait WebRTC yang terus berubah dan tidak konsisten di seluruh browser ) dan 2) mengesampingkan beberapa definisi tipe default TypeScript (misalnya mengganti tipe Object.entries sehingga mengembalikan array yang berisi unknown alih-alih any ).

Saya tidak yakin apakah pendekatan ini dapat digunakan untuk mengganti tipe array TypeScript untuk menyelesaikan masalah ini, tetapi mungkin patut dicoba.

Di atas berfungsi ketika penggabungan deklarasi memberikan hasil yang Anda inginkan, tetapi untuk menyederhanakannya, mereka berpotongan dengan antarmuka, tetapi di sini opsi yang tidak terlalu membatasi adalah yang Anda inginkan.

Anda dapat mencoba menyalin-menempelkan semua file lib.*.d.ts Anda gunakan ke dalam proyek Anda, termasuk dalam tsconfig.json 's files atau include , lalu mengeditnya untuk apa yang Anda inginkan. Karena mereka menyertakan /// <reference no-default-lib="true"/> , Anda seharusnya tidak memerlukan sihir lain, tetapi saya tidak yakin apakah Anda harus menghapus /// <reference lib="..."/> mereka miliki ke dependensinya. Ini buruk untuk semua alasan pemeliharaan yang jelas, tentu saja.

@butchler Kami juga menggunakan pendekatan ini tetapi tampaknya mengganti operator pengindeksan tidak dimungkinkan, meskipun kami telah berhasil mengganti beberapa fungsi Array lainnya ( filter dengan penjaga tipe).

Saya mengalami kasus aneh dengan mengganti operator pengindeksan:

function test() {
  const arr: string[] = [];

  const [first] = arr;
  const zero = arr[0];

  const str1: string = first;
  const str2: string = zero;
}

Screenshot 2020-02-05 at 10 39 20 AM

Penugasan kedua salah (sebagaimana mestinya), namun yang pertama tidak.
Yang lebih aneh lagi adalah bahwa melayang di atas first saat sedang dirusak menunjukkan bahwa ia memiliki tipe string | undefined namun melayang di atasnya saat ditugaskan menunjukkan bahwa ia memiliki tipe string .
Screenshot 2020-02-05 at 10 40 25 AM
Screenshot 2020-02-05 at 10 40 32 AM

Apakah ada definisi tipe yang berbeda untuk perusakan array?

Mencari solusi untuk ini karena kesalahan yang berkaitan dengan indeks yang hilang sering menjadi sumber bug dalam kode saya.

Menentukan tipe seperti { [index: string]: string | undefined } bukanlah solusi, karena akan mengacaukan pengetikan untuk iterator seperti Object.values(x).forEach(...) yang tidak akan pernah menyertakan nilai undefined .

Saya ingin melihat TypeScript menghasilkan kesalahan ketika saya tidak memeriksa undefined setelah melakukan someObject[someKey] , tetapi tidak ketika melakukan Object.values(someObject).forEach(...) .

@danielnixon Itu bukan solusi, itu solusi. Tidak ada yang menghentikan Anda atau pengembang lain dari kesalahan (jika Anda bahkan dapat menyebutnya begitu) menggunakan alat bawaan bahasa untuk tujuan yang sama. Maksud saya, saya menggunakan fp-ts untuk hal ini, tetapi masalah ini masih valid dan perlu diperbaiki.

Sementara saya dapat mengikuti argumen kontra dari @RyanCavanaugh dengan:

for (let i = 0; i < arr.length; i++) {
  // TypeScript makes me use ! with my arrays, sad.
  console.log(arr[i]!.name);

Saya akan memberikan beberapa pemikiran singkat tentang hal itu. Pada intinya, TypeScript bertujuan untuk membuat pengembangan, yah... lebih aman. Untuk mengingat tujuan pertama TypeScript:

1. Secara statis mengidentifikasi konstruksi yang mungkin kesalahan.

Jika kita melihat kompiler, kita sudah memiliki keajaiban di balik inferensi tipe. Dalam kasus tertentu, alasan sebenarnya adalah: "kami tidak dapat menyimpulkan tipe yang tepat pada konstruksi ini" dan itu baik-baik saja. Seiring waktu kami memiliki semakin sedikit kasus, di mana kompilasi tidak dapat menyimpulkan jenisnya. Dengan kata lain, bagi saya hanya masalah waktu (dan menghabiskan waktu untuk ini) untuk mendapatkan inferensi tipe yang lebih canggih.

Dari sudut pandang teknis, konstruksi seperti:

const x = ['a', 'b', 'c']
console.log(x[3]) // type: string, reality: undefined

memecahkan tujuan pertama TypeScript. Tetapi ini tidak terjadi jika TypeScript mengetahui tipe yang lebih tepat:

const x = ['a', 'b', 'c'] as const
console.log(x[3]) // compile error: Tuple type 'readonly ["a", "b", "c"]' of length '3' has no element at index '3'.ts(2493)

Dari sudut pandang praktis, ini adalah trade-off yet . Masalah ini dan beberapa masalah tertutup menunjukkan bahwa ada permintaan yang tinggi dari komunitas untuk perubahan ini: ATM 238 upvoters vs 2 downvoters. Tentu saja sangat disayangkan bahwa for loop di atas tidak menyimpulkan tipe yang "benar", tapi yah, saya cukup yakin bahwa sebagian besar upvoter dapat hidup dengan ! dan ? sebagai tanda perhatian dan paksa dalam kasus yang aman. Tetapi di sisi lain dapatkan tipe yang tepat pada akses "berbahaya".

Karena saya setuju bahwa ini adalah perubahan yang sangat sensitif untuk basis kode besar, saya memilih properti konfigurasi, seperti yang diusulkan di sini . Setidaknya untuk mendapatkan umpan balik dari komunitas. Jika tidak mungkin, maka, TS 4.0 harus mendapatkannya.

Ini bukan solusi yang diusulkan, tetapi hanya percobaan: Saya mencoba memodifikasi lib.es5.d.ts di dalam node_modules pada proyek yang ada yang menggunakan TypeScript hanya untuk melihat seperti apa jadinya jika kita _did_ memiliki kompiler pilihan untuk ini. Saya memodifikasi Array dan ReadonlyArray :

interface ReadonlyArray<T> {
  ...
  [n: number]: T | undefined; // was just T
}

interface Array<T> {
  ...
  [n: number]: T | undefined; // was just T
}

Karena ini adalah proyek yang sangat besar, ini menyebabkan beberapa ratus kesalahan ketik. Saya hanya membahas beberapa dari mereka untuk mendapatkan gambaran tentang masalah seperti apa yang akan saya hadapi dan seberapa sulit mereka untuk mengatasinya jika ada opsi kompiler untuk ini. Berikut beberapa masalah yang saya alami:

  1. Ini menyebabkan kesalahan tipe tidak hanya di basis kode kami, tetapi di salah satu dependensi kami: io-ts. Karena io-ts memungkinkan Anda membuat tipe yang Anda gunakan dalam kode Anda sendiri, saya rasa tidak mungkin untuk hanya menerapkan opsi ini ke basis kode Anda sendiri dan tidak juga menerapkannya ke tipe io-ts. Ini berarti bahwa io-ts dan mungkin beberapa perpustakaan lain masih harus diperbarui untuk bekerja dengan opsi ini meskipun itu hanya diperkenalkan sebagai opsi kompiler. Pada awalnya saya pikir menjadikan ini opsi kompiler akan membuat ini kurang kontroversial, tetapi jika orang yang memilih untuk menggunakan opsi mulai mengeluh kepada sekelompok penulis perpustakaan yang berbeda tentang ketidakcocokan, itu mungkin bahkan lebih kontroversial daripada sekadar menjadi TS 4.0 melanggar perubahan.

  2. Terkadang saya harus menambahkan beberapa pelindung tipe tambahan untuk menghilangkan kemungkinan tidak terdefinisi. Ini tidak selalu merupakan hal yang buruk, dan merupakan inti dari keseluruhan proposal ini, tetapi saya hanya menyebutkannya untuk kelengkapan.

  3. Saya mengalami kesalahan ketik di dalam for (let i = 0; i < array.length; i++) loop yang berulang Array<T> . Saya tidak bisa hanya menambahkan penjaga tipe untuk memeriksa undefined , karena T itu sendiri mungkin termasuk undefined . Satu-satunya solusi yang dapat saya pikirkan adalah A) menggunakan pernyataan tipe atau @ts-ignore untuk membungkam kesalahan tipe atau B) menggunakan loop for-of sebagai gantinya. Secara pribadi saya tidak menganggap ini terlalu buruk (mungkin ada beberapa kasus di mana menggunakan for-of loop untuk beralih pada array tidak lebih baik), tetapi mungkin kontroversial.

  4. Ada banyak kasus di mana kode yang ada sudah membuat beberapa pernyataan tentang nilai .length dan kemudian mengakses elemen dalam array. Ini sekarang menyebabkan kesalahan jenis meskipun pemeriksaan .length , jadi saya harus mengubah kode agar tidak bergantung pada cek .length atau hanya menambahkan cek !== undefined berlebihan. Akan sangat bagus jika TypeScript dapat mengizinkan penggunaan cek .length untuk menghindari perlunya cek !== undefined . Saya berasumsi sebenarnya menerapkan itu tidak akan sepele.

  5. Beberapa kode menggunakan A[number] untuk mendapatkan tipe elemen dari tipe array generik. Namun, ini sekarang mengembalikan T | undefined alih-alih hanya T , menyebabkan kesalahan ketik di tempat lain. Saya membuat pembantu untuk mengatasi ini:

    type ArrayValueType<A extends { [n: number]: unknown }> = (
      A extends Array<infer T> ? T :
      A extends ReadonlyArray<infer T> ? T :
      A[number] // Fall back to old way of getting array element type
    );
    

    tetapi bahkan dengan bantuan ini untuk memudahkan transisi, ini masih merupakan perubahan besar. Mungkin TypeScript dapat memiliki semacam kasus khusus untuk A[number] untuk menghindari masalah ini, tetapi akan lebih baik untuk menghindari kasus khusus yang aneh seperti itu jika memungkinkan.

Saya hanya mengalami segelintir kecil dari ratusan kesalahan ketik, jadi ini mungkin daftar yang sangat tidak lengkap.

Bagi siapa pun yang tertarik untuk memperbaiki masalah ini, mungkin berguna untuk mencoba melakukan hal yang sama dengan beberapa proyek yang ada dan membagikan masalah lain yang Anda hadapi. Semoga contoh-contoh spesifik dapat memberikan beberapa panduan bagi siapa saja yang benar-benar mencoba menerapkan ini.

@butchler Saya juga mengalami beberapa masalah ini, saya mulai melihat something[i] sebagai something(i) , yaitu, saya tidak bisa hanya menggunakan sesuatu seperti if (Meteor.user() && Meteor.user()._id) {...} , jadi saya tidak' t berharap memiliki pendekatan ini untuk pengindeksan array; Saya harus terlebih dahulu mendapatkan nilai yang ingin saya periksa dari array. Apa yang saya coba katakan adalah bahwa mengandalkan TS untuk memahami bahwa memeriksa properti panjang dan membuat beberapa pernyataan berdasarkan itu mungkin memberi terlalu banyak tekanan pada sistem tipe. Satu hal yang membuat saya menunda transisi (selain fakta bahwa beberapa perpustakaan lain juga memiliki kesalahan, seperti yang Anda katakan) adalah bahwa destrukturisasi array belum berfungsi dengan benar dan nilai yang dirusak tidak akan menjadi T | undefined tetapi T (lihat komentar saya yang lain).
Selain itu, saya pikir mengganti lib.es5.d.ts adalah pendekatan yang baik. Bahkan jika sesuatu akan berubah di masa mendatang, mengembalikan perubahan tidak akan menambah kesalahan tambahan, tetapi akan memastikan bahwa beberapa kasus tepi sudah tercakup.
Kami sudah mulai menggunakan patch-package untuk mengubah beberapa definisi tipe di react dan pendekatan ini tampaknya bekerja dengan baik.

Saya juga mengalami beberapa masalah ini, saya mulai melihat sesuatu[i] sebagai sesuatu(i), yaitu, saya tidak bisa hanya menggunakan sesuatu seperti if (Meteor.user() && Meteor.user()._id) { ...}, jadi saya tidak berharap memiliki pendekatan ini untuk pengindeksan array; Saya harus terlebih dahulu mendapatkan nilai yang ingin saya periksa dari array.

Ya, saya juga akhirnya menggunakan pendekatan ini ketika saya mencoba eksperimen saya. Misalnya, kode yang sebelumnya ditulis sebagai:

if (array[i]) {
  array[i].doSomething(); // causes a type error with our modified Array types
}

harus diubah menjadi sesuatu seperti:

const arrayValue = array[i]
if (arrayValue) {
  arrayValue.doSomething();
}

Apa yang saya coba katakan adalah bahwa mengandalkan TS untuk memahami bahwa memeriksa properti panjang dan membuat beberapa pernyataan berdasarkan itu mungkin memberi terlalu banyak tekanan pada sistem tipe.

Mungkin benar. Sebenarnya mungkin lebih mudah untuk menulis codemod untuk menulis ulang kode secara otomatis yang bergantung pada pernyataan tentang .length untuk menggunakan beberapa pendekatan lain, daripada membuat TypeScript cukup pintar untuk menyimpulkan lebih banyak tentang jenis berdasarkan pernyataan tentang .length

Bahkan jika sesuatu akan berubah di masa mendatang, mengembalikan perubahan tidak akan menambah kesalahan tambahan, tetapi akan memastikan bahwa beberapa kasus tepi sudah tercakup.

Ini adalah poin yang bagus. Meskipun memasukkan undefined dalam tipe indeks adalah perubahan besar, pergi ke arah lain bukanlah perubahan besar. Kode yang dapat menangani mendapatkan T | undefined juga harus dapat menangani T tanpa perubahan apa pun. Ini berarti, misalnya, pustaka dapat diperbarui untuk menangani kasus T | undefined jika ada flag compiler dan masih digunakan oleh proyek yang tidak mengaktifkan flag compiler tersebut.

Mengabaikan array, dan fokus pada Record<string, T> sejenak, daftar keinginan pribadi saya adalah menulis hanya memungkinkan T tetapi membaca mungkin T|undefined .

declare const obj : Record<string, T>;
declare const t : T;
obj["k"] = t; //ok
obj["k"] = undefined; //error, undefined not assignable to T

//T|undefined inferred,
//since we don't know if "k2" is an "ownProperty" of obj
const v = obj["k2"];

Satu-satunya cara agar hal di atas menjadi ergonomis adalah semacam pengetikan dependen, dan pelindung tipe dependen. Karena kami tidak memilikinya, menambahkan perilaku ini akan menyebabkan semua jenis masalah.

//Shouldn't just be string[]
//Should also be something like (keyof valueof obj)[],
//A dependent type
const keys = Object.keys(obj);

Kembali ke array, masalahnya adalah tanda tangan indeks untuk array tidak memiliki maksud yang sama dengan Record<number, T> .

Jadi, Anda akan membutuhkan penjaga tipe dependen yang sama sekali berbeda seperti,

for (let i=0; i<arr.length; ++i) {
  //i is not just number
  //i should also be something like keyof valueof arr 
}

Jadi, tanda tangan indeks untuk array sebenarnya bukan Record<number, T> . Ini lebih seperti Record<(int & (0 <= i < this["length"]), T> (rentang dan tipe bilangan bulat yang didukung angka)


Jadi, sementara posting asli hanya berbicara tentang array, judulnya tampaknya menyarankan tanda tangan indeks "hanya" string atau "hanya" number . Dan itu adalah dua diskusi yang sama sekali berbeda.

TL;DR Posting dan judul asli berbicara tentang hal-hal yang berbeda, penerapannya terlihat sangat bergantung (ha) pada pengetikan yang bergantung.

Mengingat bahwa fungsi-fungsi seperti forEach , map , filter , dll ada (dan IMHO jauh lebih disukai), saya tidak akan mengharapkan tim TS untuk sangat memperumit mesin inferensi mereka mendukung perulangan array dengan perulangan for biasa. Saya merasa bahwa ada terlalu banyak kerumitan tak terduga dari mencoba mencapainya. Misalnya, karena i bukan konstanta, apa yang terjadi jika seseorang mengubah nilai i di dalam loop? Tentu, ini adalah kasus tepi, tetapi itu adalah sesuatu yang perlu mereka tangani dengan cara yang (semoga) intuitif.

Memperbaiki tanda tangan indeks, bagaimanapun, harus relatif sederhana (ish), seperti yang ditunjukkan oleh komentar di atas.

4. Ada banyak kasus di mana kode yang ada sudah membuat beberapa pernyataan tentang nilai .length dan kemudian mengakses elemen dalam array. Ini sekarang menyebabkan kesalahan jenis meskipun pemeriksaan .length , jadi saya harus mengubah kode untuk tidak bergantung pada cek .length atau hanya menambahkan cek !== undefined berlebihan. Akan sangat bagus jika TypeScript dapat mengizinkan penggunaan cek .length untuk menghindari perlunya cek !== undefined . Saya berasumsi sebenarnya menerapkan itu tidak akan sepele.

@tukang daging
Karena penasaran, berapa banyak yang mengakses dengan variabel yang berubah, vs mengakses dengan nomor tetap? Karena, dalam kasus terakhir, Anda mungkin menggunakan array sebagai Tuple, yang TS melacak panjang array. Jika Anda sering menggunakan array sebagai tupel, saya ingin tahu apa yang dilakukan dengan mengetik ulang mereka sebagai tupel yang sebenarnya terhadap jumlah kesalahan.

Jika Anda sering menggunakan array sebagai tupel, saya ingin tahu apa yang dilakukan dengan mengetik ulang mereka sebagai tupel yang sebenarnya terhadap jumlah kesalahan.

Saya memang mengalami satu kasus di mana saya memperbaiki kesalahan tipe hanya dengan menambahkan as const ke literal array untuk mengubah tipe variabel menjadi Tuple. Saya sama sekali tidak tahu seberapa umum itu dalam basis kode kami, karena saya hanya melihat sekitar 10 kesalahan dari beberapa ratus.

Saya juga mengalami masalah dengan ini hari ini.
Kami mengakses dari array melalui indeks yang dihitung. Indeks itu bisa berada di luar jangkauan.
Jadi kami memiliki sesuatu seperti ini dalam kode kami:

const noNext = !items[currentIndex + 1];

Ini menghasilkan noNext didefinisikan sebagai false . Yang salah. Itu bisa benar.
Saya juga tidak ingin mendefinisikan items sebagai Array<Item | undefined> karena itu memberikan harapan yang salah.
Jika ada indeks, seharusnya tidak pernah undefined . Tetapi jika Anda menggunakan indeks yang salah, itu _is_ undefined .
Tentu, hal di atas mungkin dapat diselesaikan dengan menggunakan cek .length atau mendefinisikan noNext secara eksplisit sebagai boolean .
Tetapi pada akhirnya ini adalah sesuatu yang mengganggu saya sejak saya mulai menggunakan TypeScript dan saya tidak pernah mengerti mengapa | undefined tidak disertakan secara default.

Karena saya berharap sebagian besar menggunakan TypeScript-eslint, apakah ada aturan yang dapat menegakkan bahwa nilai harus diperiksa setelah pengindeksan sebelum dapat digunakan? Ini mungkin berguna sebelum dukungan dari TS diterapkan.

Dalam kasus itu mungkin menarik bagi seseorang. Dalam komentar sebelumnya saya menyebutkan bahwa pendekatan menambal definisi pengindeksan Array di lib.es5.d.ts memiliki beberapa perilaku aneh pada perusakan array karena tampaknya tidak dipengaruhi oleh perubahan. Tampaknya itu tergantung pada opsi target di tsconfig.json dan berfungsi dengan benar ketika es5 (https://github.com/microsoft/TypeScript/issues/37045 ).

Untuk proyek kami, itu tidak masalah karena kami menggunakan opsi noEmit dan transpilasi ditangani oleh meteor . Tetapi untuk proyek yang mungkin menjadi masalah, solusi yang mungkin berhasil adalah safe-array-destructuring (https://github.com/typescript-eslint/typescript-eslint/pull/1645).
Ini masih berupa draf dan mungkin tidak bernilai ketika seluruh masalah akan diperbaiki di kompiler TypeScript tetapi jika menurut Anda ini mungkin berguna, jangan ragu untuk mengatasi masalah/perbaikan apa pun dalam PR aturan typescript-eslint

Sayangnya, perbaikan dengan eslint di PR Anda hanya mendukung Tuple dan bukan array. Masalah utama dan dampak utama yang saya miliki dengan ini adalah dalam destrukturisasi array.

const example = (args: string[]) => {
  const [userID, nickname] = args
}

Saya pikir seluruh masalah ini berpihak pada saya yang tidak setuju. Saya tidak berpikir harus ada pemeriksaan yang dipaksakan di dalam forEach, map dll ... Saya juga mencoba dan menghindari semua penggunaan for jika memungkinkan sehingga alasan untuk menghindari ini kurang masuk akal bagi saya. Meskipun demikian, saya masih berpikir ini sangat penting untuk mendukung destrukturisasi array.

Yah, itu hanya salah untuk perusakan array apa pun, jadi Anda terpaksa melakukannya menggunakan pengindeksan.

Bukankah itu buruk? Penghancuran array adalah fitur yang luar biasa. Masalahnya adalah pengetikan tidak akurat. Menonaktifkan fitur bukanlah solusi yang baik. Menggunakan indeks menurut saya lebih buruk (lebih sulit dibaca dan dipahami) kode dan lebih rawan kesalahan.

Ini lebih jelek, tetapi memaksa pengembang untuk memeriksa apakah elemen tersebut didefinisikan atau tidak. Saya telah mencoba memberikan solusi yang lebih elegan, tetapi tampaknya ini adalah solusi yang memiliki kerugian paling sedikit. Setidaknya untuk usecase kami.

@caseyhoward Seperti disebutkan sebelumnya dalam masalah ini, ini menyebabkan perilaku yang tidak diinginkan dengan berbagai fungsi Array.prototype:

x.forEach( (i: string) => { ... } )  // Error because i has type string | undefined

Ini tidak perlu diperbaiki dalam fungsi array.prototype. Ini adalah masalah besar untuk destrukturisasi array!

Berlari ke ini lagi hari ini. Saya masih berpikir kompromi yang tepat adalah ini , yang ingin saya dapatkan umpan baliknya.

Kasus penggunaan lain untuk masalah ini—Saya mengalami masalah ini saat menggunakan TypeScript-eslint dan mengaktifkan no-unnecessary-condition . Di masa lalu, ketika mengakses array dengan indeks untuk melakukan beberapa operasi pada elemen pada indeks itu, kami menggunakan rantai opsional untuk memastikan bahwa indeks itu ditentukan (jika indeks di luar batas), seperti pada array[i]?.doSomething() . Namun, dengan masalah yang diuraikan dalam masalah ini, no-unnecessary-condition menandai bahwa rangkaian opsional sebagai tidak perlu (karena jenisnya tidak dapat dibatalkan menurut TypeScript) dan perbaikan otomatis menghapus rangkaian opsional, yang menyebabkan kesalahan runtime saat array mengakses di indeks i sebenarnya tidak terdefinisi.

Tanpa fitur ini aplikasi saya menjadi sangat buggy karena saya berurusan dengan array multi-dimensi, saya harus selalu mengingatkan diri saya untuk mengakses elemen menggunakan xs[i]?.[j] alih-alih xs[i][j] , saya juga harus secara eksplisit melemparkan elemen yang diakses seperti const element = xs[i]?.[j] as Element | undefined untuk memastikan keamanan jenis.

Menghadapi ini adalah wtf besar pertama saya di TypeScript. Jika tidak, bahasa yang saya temukan sangat andal dan ini mengecewakan saya, dan cukup rumit untuk menambahkan "as T | undefined" ke akses array. Akan senang melihat salah satu proposal diimplementasikan.

Ya, sama di sini. Itu memaksa kami untuk meluncurkan tipe kami sendiri yang memiliki ( | undefined ) untuk memiliki keamanan tipe. Sebagian besar untuk akses objek (mungkin ini adalah masalah terbuka yang terpisah), tetapi logika yang sama berlaku untuk mengakses indeks array.

Tidak yakin apa yang Anda maksud, bisakah Anda menautkan masalah atau menunjukkan contoh?

Pada hari Rabu, 29 April 2020 pukul 02:41 Kirill Groshkov [email protected]
menulis:

Ya, sama di sini. Itu memaksa kami untuk meluncurkan tipe kami sendiri yang memiliki ( |
undefined) untuk memiliki keamanan tipe. Sebagian besar untuk akses objek (mungkin itu a
masalah terbuka yang terpisah), tetapi logika yang sama berlaku untuk mengakses indeks array.


Anda menerima ini karena Anda berkomentar.
Balas email ini secara langsung, lihat di GitHub
https://github.com/microsoft/TypeScript/issues/13778#issuecomment-621096030 ,
atau berhenti berlangganan
https://github.com/notifications/unsubscribe-auth/AAAGFJJT37N54I7EH2QLBYDRO7Y4NANCNFSM4C6KEKAA
.

@alangpierce

Satu ide yang sepertinya menjanjikan: bagaimana jika Anda dapat menggunakan ?: saat mendefinisikan tanda tangan indeks untuk menunjukkan bahwa Anda mengharapkan nilai terkadang hilang dalam penggunaan normal? Itu akan bertindak seperti | undefined disebutkan di atas, tetapi tanpa kerugian yang canggung. Itu perlu melarang nilai undefined eksplisit, yang saya kira berbeda dari biasanya ?: .

Ini adalah ide menarik yang menunjuk ke arah yang benar. Namun, saya akan membalikkan logika: Kasus defaultnya adalah tanda tangan indeks menyertakan | undefined . Jika Anda ingin memberi tahu kompiler bahwa kasus yang tidak ditentukan tidak akan pernah terjadi dalam kode Anda (sehingga Anda tidak pernah mengakses kunci yang tidak valid), Anda dapat menambahkan !: ke tanda tangan, seperti ini:

type AlwaysDefined = {[key: string]!: string};
const t: AlwaysDefined = {};

t['foo'] // Has type string

dan kasus default tanpa !: akan terlihat seperti yang kita ketahui, tetapi akan lebih aman:

type MaybeUndefined = {[key: string]: string};
const t: MaybeUndefined = {};

t['foo'] // Has type string | undefined

Dengan begitu Anda memiliki keamanan secara default dan dengan cara yang sama Anda tidak perlu secara eksplisit memasukkan undefined yang memungkinkan penulisan undefined secara tidak sengaja.

Maaf jika itu sudah disarankan, saya tidak bisa membaca semua komentar di utas ini.

Secara umum, saya benar-benar berpikir bahwa perilaku default yang kita miliki sekarang tidak seperti yang diharapkan pengguna saat menggunakan TypeScript. Lebih baik aman daripada menyesal, terutama ketika kesalahan dapat ditangkap semudah ini oleh kompiler.

Saya tidak dapat membuat index.d.ts bekerja dengan proyek saya dan tidak benar-benar ingin menanganinya.

Saya membuat retasan kecil ini yang memaksa penjaga tipe:

 const firstNode = +!undefined && nodes[0];

Meskipun jenisnya berakhir sebagai: 0 | T , ia masih berfungsi.

Tidakkah const firstNode = nodes?.[0] berfungsi?

@ricklove alasan kamu lebih suka +!undefined daripada 1 ?

Tidakkah const firstNode = nodes?.[0] berfungsi?

Tidak, karena TypeScript akan (menurut saya salah) tidak memperlakukannya sebagai opsional (lihat #35139).

Menurut dokumentasi Flow, perilaku dengan tanda tangan indeks sama dengan saat ini dengan TypeScript:

Ketika tipe objek memiliki properti pengindeks, akses properti diasumsikan memiliki tipe beranotasi, bahkan jika objek tidak memiliki nilai di slot itu saat runtime. Merupakan tanggung jawab pemrogram untuk memastikan akses aman, seperti halnya array.

var obj: { [number]: string } = {};
obj[42].length; // No type error, but will throw at runtime

Jadi dalam masalah ini TS dan Flow mengharuskan pengguna untuk memikirkan kunci yang tidak ditentukan alih-alih kompiler yang membantu mereka :(

Akan menjadi keuntungan TS jika kompiler mencegah pengguna membuat bug semacam ini. Membaca semua komentar di utas ini, tampaknya sebagian besar orang di sini ingin memiliki fitur ini. Apakah tim TS masih 100% menentangnya?

Tampaknya konyol bahwa TS melakukan yang terbaik untuk mencegah akses ke anggota yang tidak ditentukan, kecuali ini. Sejauh ini, ini adalah bug paling umum yang telah saya perbaiki di basis kode kami.

[memindahkan diskusi ke sini dari #38470]
Menerima semua argumen mengapa ini tidak praktis untuk array..
Saya pikir ini harus ditujukan untuk Tuples:

// tuple 
var str = '';
var num = 100
var aa = [str, num] as const

// Awesome
var shouldBeString = aa[0] // string
var shouldBeNumber = aa[1] // number
var shouldError = aa[10000]; // type error

// Not so awesome 
var foo = aa[num] // string | number

mengapa tidak membuat foo string | number | undefined ?

Memberikan ini ke tupel seharusnya tidak mempengaruhi sebagian besar pengguna, dan pengguna yang terpengaruh harus mengaktifkan strictNullChecks , dan mendeklarasikan array mereka sebagai konstanta...
Mereka jelas mencari jenis keamanan yang lebih ketat.

juga bisa membantu dengan ini

var notString1 = aa[Infinity]; // no error, not undefined
var notString2 = aa[NaN]; // no error, not undefined

Yang awalnya menyebabkan kesalahan runtime saya sebagai number menjadi NaN , lalu mengembalikan undefined dari Tuple..
Semuanya adalah tipe aman

Saya tidak yakin bahwa tupel benar-benar sangat berbeda. Mereka harus mengakomodasi tipe istirahat (mis. [str, ...num[]] ), dan secara teknis aa[Infinity] adalah Javascript yang benar-benar valid, jadi saya bisa melihatnya semakin rumit untuk mengukir kasus khusus untuk mereka.

Posting Anda juga membuat saya berpikir tentang seperti apa jadinya jika kami mendapatkan dukungan untuk memperlakukan pengembalian indeks sebagai tidak terdefinisi. Jika, seperti dalam contoh Anda, aa[num] benar-benar mengetik string | number | undefined , apakah saya harus menulis for (let i = 0; i < 2; i++) { foo(aa[i]!); } meskipun saya tahu indeks akan tetap dalam batas? Bagi saya, ketika pemeriksaan nol ketat menandai sesuatu yang saya tulis, saya ingin dapat memperbaikinya dengan mengetikkan hal-hal yang lebih baik di tempat pertama, atau menggunakan penjaga runtime -- jika saya harus menggunakan pernyataan non-null, saya biasanya melihatnya sebagai kegagalan di pihak saya. Saya tidak melihat jalan keluar dalam kasus ini, dan itu mengganggu saya.

Jika tuple[number] selalu diketik ke T | undefined , bagaimana Anda ingin menangani kasus di mana Anda tahu indeks dibatasi dengan benar?

@thw0rted Saya tidak ingat kapan terakhir kali saya menggunakan loop "klasik" for di TypeScript ( .map dan .reduce ftw). Itu sebabnya opsi kompiler untuk ini akan menjadi imo yang bagus (yang dapat dimatikan secara default). Banyak proyek tidak pernah menemukan hal seperti kasus yang Anda berikan, dan hanya menggunakan tanda tangan indeks untuk destrukturisasi array, dll.

(komentar asli saya: https://github.com/microsoft/TypeScript/issues/13778#issuecomment-517759210)

Oh sejujurnya, saya juga jarang mengindeks dengan for-loop. Saya menggunakan Object.entries atau Array#map secara ekstensif, tetapi sesekali saya perlu memberikan kunci untuk mengindeks ke objek atau (mungkin lebih jarang lagi) mengindeks ke dalam array atau Tuple. Loop for adalah contoh yang dibuat-buat, tentu saja, tetapi ini menunjukkan bahwa salah satu opsi ( undefined atau tidak) memiliki kelemahan.

tetapi ini menunjukkan bahwa salah satu opsi ( undefined atau tidak) memiliki kelemahan.

Ya dan alangkah baiknya jika pengguna TypeScript dapat memilih kerugian mana yang mereka sukai :wink:

Oh sejujurnya, saya juga jarang mengindeks dengan for-loop. Saya menggunakan Object.entries atau Array#map secara ekstensif, tetapi sesekali saya perlu memberikan kunci untuk mengindeks ke objek atau (mungkin lebih jarang lagi) mengindeks ke dalam array atau Tuple.

Dalam kasus di mana Anda perlu, Anda dapat menggunakan Array#entries :

for (const [index, foo] of array.entries()) {
    bar(index, foo)
}

Saya pribadi tidak terlalu sering menggunakan Array#forEach , dan saya tidak pernah menggunakan Array#map untuk iterasi (saya selalu menggunakannya untuk pemetaan). Saya lebih suka menjaga kode saya tetap datar, dan saya menghargai kemampuan untuk keluar dari for...of loop.

Saya tidak yakin bahwa tupel benar-benar sangat berbeda. Mereka harus mengakomodasi tipe istirahat (mis. [str, ...num[]] ), dan secara teknis aa[Infinity] adalah Javascript yang benar-benar valid, jadi saya bisa melihatnya semakin rumit untuk mengukir kasus khusus untuk mereka.

Posting Anda juga membuat saya berpikir tentang seperti apa jadinya jika kami mendapatkan dukungan untuk memperlakukan pengembalian indeks sebagai tidak terdefinisi. Jika, seperti dalam contoh Anda, aa[num] benar-benar mengetik string | number | undefined , apakah saya harus menulis for (let i = 0; i < 2; i++) { foo(aa[i]!); } meskipun saya tahu indeks akan tetap dalam batas? Bagi saya, ketika pemeriksaan nol ketat menandai sesuatu yang saya tulis, saya ingin dapat memperbaikinya dengan mengetikkan hal-hal yang lebih baik di tempat pertama, atau menggunakan penjaga runtime -- jika saya harus menggunakan pernyataan non-null, saya biasanya melihatnya sebagai kegagalan di pihak saya. Saya tidak melihat jalan keluar dalam kasus ini, dan itu mengganggu saya.

Jika tuple[number] selalu diketik ke T | undefined , bagaimana Anda ingin menangani kasus di mana Anda tahu indeks dibatasi dengan benar?

Saya tidak yakin ada banyak iterasi for(let i..) pada tupel..
Jika Tuple memiliki satu tipe maka Pengguna akan menggunakan Array,

Dalam pengalaman saya, tupel sangat bagus untuk tipe campuran dan jika itu adalah kasus umum maka Anda tetap mengetik checking

var str = '';
var num = 100
var aa = [str, num] as const
for (let i = 0; i < aa.length; i++) {
 aa[i] // needs some type check anyway to determine if 'string' or 'number'
}

Juga dengan asumsi perubahan yang diusulkan ini terjadi, apa yang menghentikan orang melakukan ini?

// this sucks
for (let i = 0; i < 2; i++) { 
   foo(aa[i]!); // ! required
}

// this would work
for (let i = 0 as 0 | 1; i < 2; i++) { 
   foo(aa[i]); //  ! not required 
}

anda dapat menggunakan keyof typeof aa alih-alih 0 | 1 ketika mereka memperbaiki masalah ini

Tentu tidak akan ada keamanan jenis apa pun saat melakukan aritmatika i ,
tapi itu benar-benar memungkinkan orang untuk memilih ini untuk keamanan tipe daripada array default "Tidak benar-benar mengetik aman tapi nyaman"

Saya pikir memiliki ?? dan ?. operator berarti tidak lagi terlalu rumit untuk melakukan akses acak ke Array s.
Tapi juga, mungkin jauh lebih umum untuk

  • iterate (misalnya dengan .map atau .forEach , di mana tidak perlu " T | undefined ", callback tidak pernah dijalankan pada indeks di luar batas) atau
  • melintasi Array dalam loop, beberapa di antaranya dapat ditentukan secara statis untuk aman ( for...of .. hampir, kecuali untuk _sparse arrays_, for...in Saya yakin aman).

Dan juga, sebuah variabel dapat dianggap sebagai "indeks aman" jika diperiksa dengan <index> in <array> terlebih dahulu.


_Clarification_: Saya berbicara tentang alasan asli Array<T> tidak memiliki tipe indeks [number]: T | undefined (tetapi hanya [number]: T ), yang akan terlalu rumit untuk digunakan.
Maksud saya tidak lagi demikian, jadi undefined dapat ditambahkan.

@vp2177 Masalahnya bukan pada fiturnya (ya, ?. berfungsi), masalahnya adalah kompilator tidak memperingatkan kami tentang hal itu dan mencegah kami menembak diri sendiri.

Mungkin aturan linting bisa melakukannya, tapi tetap saja.

ya, ?. berhasil

Saya mohon untuk berbeda. ?. akan berfungsi untuk mengakses properti pada nilai yang diindeks, tetapi kompilator akan tetap mengatakan bahwa nilainya ditentukan terlepas dari apakah itu benar atau tidak (lihat #35139).

Untuk menambah itu, jika Anda menggunakan eslint dan ingin menggunakan no-unnecessary-condition , akses tersebut akan ditandai sebagai tidak perlu dan dihapus karena kompilator mengatakan itu tidak pernah terdefinisi dan itu akan membuat ?. tidak perlu.

@martpie Maaf saya pikir Anda salah paham dengan komentar saya, menambahkan klarifikasi.

Maksud saya... sepertinya ide yang bagus untuk memiliki --strictIndexChecks atau serupa yang memungkinkan orang untuk ikut serta dalam jenis perilaku ini. Itu tidak akan menjadi perubahan besar bagi semua orang dan itu memberikan lingkungan tipe yang lebih ketat.

Saya percaya Flow bekerja dengan cara ini secara default untuk indeks dan tidak memiliki masalah yang disebutkan di atas tetapi sudah lama sejak saya menggunakannya sehingga saya bisa salah.

@bradennapier Flow tidak menggunakan T | undefined baik dalam tanda tangan indeks untuk Array s (sayangnya sama seperti TypeScript.)

Saya sebelumnya telah menyarankan pendekatan yang bisa membuat semua orang bahagia. Semoga tidak apa-apa untuk mengulanginya dengan kata lain.

Pada dasarnya perilaku default dari tanda tangan indeks akan diubah menjadi

  • Sertakan | undefined saat membaca dari array atau objek
  • Jangan sertakan | undefined saat menulis (karena kita hanya menginginkan nilai T pada objek kita)

Definisinya akan seperti itu:

type MaybeUndefined = {[key: string]: string};
const t: MaybeUndefined = {};

const x = t['foo'] // Has type string | undefined
t['foo'] = undefined // ERROR! 
t['foo'] = "test" // Ok

Untuk orang yang tidak ingin undefined disertakan dalam tipe, mereka dapat menonaktifkannya menggunakan opsi kompiler atau menonaktifkannya hanya untuk beberapa tipe menggunakan operator ! :

type AlwaysDefined = {[key: string]!: string};
const t: AlwaysDefined = {};

const x = t['foo'] // Has type string
t['foo'] = undefined // ERROR! 
t['foo'] = "test" // Ok

Untuk tidak merusak kode yang ada, mungkin juga lebih baik untuk memperkenalkan opsi kompiler baru yang menyebabkan undefined disertakan (seperti yang ditunjukkan dengan tipe MaybeUndefined atas). Jika opsi itu tidak ditentukan, semua jenis tanda tangan indeks berperilaku seolah-olah operator ! digunakan dalam deklarasi.

Ide lain yang muncul: Seseorang mungkin ingin menggunakan tipe yang sama di beberapa tempat dalam kode dengan dua perilaku yang berbeda (termasuk undefined atau tidak). Pemetaan Tipe baru dapat ditentukan:

type MakeDefined<T> = {[K in keyof T]!: T[K]}

Apakah itu ekstensi yang bagus dari TypeScript?

Saya suka idenya tapi saya curiga kita harus mendapatkan https://github.com/microsoft/TypeScript/issues/2521 terlebih dahulu -- yang, jangan salah paham, saya masih yakin kita harus melakukannya , tapi saya' m tidak menahan napas.

@bradennapier Flow tidak menggunakan T | undefined baik dalam tanda tangan indeks untuk Array s (sayangnya sama seperti TypeScript.)

Hmm bagaimana dengan indeks objek? Saya tidak pernah mengalami kebutuhan akan array tetapi cukup yakin itu membuat Anda memeriksa saat menggunakan objek.

@bradennapier Indeks objek? Seperti pada o[4] mana o diketik object ? TypeScript tidak mengizinkan Anda melakukan itu .....

ekspresi tipe '4' tidak bisa digunakan untuk mengindeks tipe '{}'
Properti '4' tidak ada pada tipe '{}'.ts(7053)

@RDGthree tidak menjadi ksatria putih untuk Microsoft dalam segala hal, tetapi Anda tampaknya melewatkan sedikit ini dari mhegazy di balasan pertama:

Dengan pengecualian strictNullChecks, kami tidak memiliki tanda yang mengubah perilaku sistem tipe. flag biasanya mengaktifkan/menonaktifkan pelaporan kesalahan.

dan beberapa saat kemudian RyanCavanaugh memiliki:

Kami tetap cukup skeptis bahwa siapa pun akan mendapatkan manfaat dari bendera ini dalam praktiknya. Peta dan hal-hal seperti peta sudah dapat ikut serta dalam | tidak terdefinisi di situs definisi mereka, dan menegakkan perilaku seperti EULA pada akses array sepertinya bukan kemenangan. Kami mungkin perlu secara substansial meningkatkan CFA dan tipe pelindung untuk membuat ini enak.

Jadi pada dasarnya - mereka sangat menyadari fakta bahwa sebuah bendera sedang diminta, dan sejauh ini mereka belum yakin itu sepadan dengan pekerjaan mereka untuk manfaat yang agak meragukan yang telah disarankan orang akan dapatkan dari ini. Sejujurnya, saya tidak terkejut, karena semua orang terus datang ke sini dengan saran dan contoh yang hampir sama dengan yang sudah dibahas. Idealnya, Anda sebaiknya menggunakan for of atau metode untuk array, dan Map sebagai ganti objek (atau kurang efektif, nilai objek 'T | undefined`).

Saya di sini karena saya memelihara paket DT yang populer dan orang-orang terus meminta |undefined untuk ditambahkan ke hal-hal seperti peta header http, yang benar-benar masuk akal, kecuali untuk bagian yang akan merusak hampir setiap penggunaan yang ada , dan fakta bahwa itu membuat penggunaan aman lainnya seperti Object.entries() jauh lebih buruk untuk digunakan. Selain mengeluh tentang bagaimana pendapat Anda lebih baik daripada pembuat TypeScript, apa sebenarnya kontribusi Anda di sini?

Simon, mungkin saya salah membaca komentar Anda, tetapi kedengarannya seperti argumen yang mendukung saran aslinya. Kemampuan "last mile" yang hilang adalah untuk memperlakukan properti sebagai | undefined kadang-kadang , tergantung pada konteksnya, tetapi tidak dalam kasus lain. Itu sebabnya saya membuat analogi dengan #2521 upthread.

Dalam skenario yang ideal, saya bisa mendeklarasikan array atau objek sedemikian rupa sehingga, mengingat

ts const arr: Array<T>; const n: number; const obj: {[k: K]: V}; const k: K;

Aku entah bagaimana bisa berakhir dengan

  • arr[n] diketik sebagai T | undefined
  • arr[n] = undefined adalah kesalahan
  • Saya memiliki akses ke beberapa jenis iterasi pada arr yang memberi saya nilai yang diketik sebagai T , bukan T | undefined
  • obj[k] diketik sebagai V | undefined
  • obj[k] = undefined adalah kesalahan
  • Object.entries() misalnya harus memberi saya tupel [K, V] dan tidak ada yang bisa tidak ditentukan

Sifat masalah yang asimetris itulah yang mendefinisikan masalah ini dalam pandangan saya.

Saya akan mengatakan bahwa pernyataan bahwa itu adalah masalah bagi pengembang yang menggunakan for loop gagal untuk memperhitungkan pengembang yang menggunakan Array Destructuring. Itu rusak di TypeScript dan tidak akan diperbaiki juga karena masalah ini. Ini memberikan pengetikan yang tidak akurat. IMO Array Destructuring yang dilakukan adalah masalah yang jauh lebih besar daripada yang masih menggunakan for loop.

const example = (args: string[]) => {
  const [userID, duration, ...reason] = args
  // userID and duration is AUTOMATICALLy inferred to be a string here. 
 // However, if for whatever reason args is an empty array 
// userID is actually `undefined` and NOT a `string`. 

// This is valid but it should not be because userID could be undefined
userID.toUpperCase()
}

Saya tidak ingin terlalu jauh dari titik awal masalah, tetapi Anda harus benar-benar mendeklarasikan metode contoh itu menggunakan tipe Tuple untuk argumen fungsi. Jika fungsi dideklarasikan sebagai ([userId, duration, ...reason]: [string, number, ...string[]]) => {} maka Anda tidak perlu khawatir tentang ini sejak awal.

Maksud saya yakin saya bisa melemparkannya tetapi itu bukan solusi yang sebenarnya. Demikian pula, siapa pun di sini dapat dengan mudah menggunakan pengindeksan array loop for serta membuat seluruh masalah ini menjadi bisu.

for (let i = 0; i < array.length; i++) {
  const value = array[i] as string | undefined
}

Masalahnya adalah TypeScript tidak memperingatkan untuk perilaku ini. Sebagai pengembang, kita harus ingat untuk memasukkan ini secara manual sebagai tidak terdefinisi yang merupakan perilaku buruk bagi alat untuk menyebabkan lebih banyak pekerjaan bagi pengembang. Juga untuk bersikap adil, para pemeran sebenarnya harus terlihat seperti ini:

const example = (args: [string | undefined, string | undefined, ...string[] | ...undefined[]]) => {

}

Itu tidak bagus sama sekali. Tetapi seperti yang saya katakan di atas, masalah utama bahkan tidak perlu mengetiknya seperti ini, karena TS tidak memperingatkannya sama sekali. Itu mengarah ke pengembang yang melupakan ini untuk mendorong kode, CI keren tidak menemukan masalah pada penggabungan dan penyebaran tsc. TS memberikan rasa percaya diri dalam kode dan memiliki pengetikan yang tidak akurat yang diberikan memberikan rasa percaya diri yang salah. => Kesalahan saat runtime!

Itu bukan "pemeran", itu mengetik dengan benar parameter formal Anda. Maksud saya adalah jika Anda mengetik metode Anda dengan benar, maka siapa pun yang memanggilnya secara tidak aman dengan tanda tangan lama ( args: string[] ) sekarang akan mendapatkan kesalahan pada waktu kompilasi, sebagaimana mestinya , ketika mereka tidak dijamin untuk berikan jumlah argumen yang tepat. Jika Anda ingin terus membiarkan orang lolos karena gagal memberikan semua argumen yang Anda butuhkan, dan memeriksanya sendiri saat run-time, sebenarnya sangat mudah untuk menulis sebagai (args: [string?, number?, ...string[]]) . Itu sudah berfungsi dengan baik dan tidak terlalu berkaitan dengan masalah ini.

Itu hanya akan memindahkan masalah dari fungsi itu ke fungsi lain yang memanggil fungsi ini. Karena Anda masih perlu mengurai string yang diberikan pengguna ke dalam nilai terpisah dan jika Anda menggunakan perusakan array untuk menguraikannya di dalam commandHandler yang memanggil fungsi ini, Anda akan berakhir dengan masalah yang sama.

Tidak ada cara lain untuk memiliki pengetikan yang tidak akurat yang disediakan untuk Penghancuran Array.

Saya tidak ingin terlalu jauh dari titik awal masalah

Catatan: Meskipun saya setuju dengan Anda, mereka harus diperlakukan sebagai masalah terpisah, setiap kali seseorang membuka masalah yang menunjukkan masalah dengan perusakan susunan, itu sedang ditutup dan pengguna diteruskan ke sini. #38259 #36635 #38287 dst... Jadi saya rasa kita tidak menyimpang dari topik masalah ini.

Beberapa frustrasi saya dengan masalah ini akan diselesaikan dengan beberapa sintaks sederhana untuk menerapkan | undefined ke tipe yang sebaliknya akan disimpulkan dari akses objek. Misalnya:

const complexObject: { [key: string]: ComplexType } = { ... };
const maybeKey = 'two';

// From:
const maybeValue = complexObject[maybeKey] as
  | (Some<Complex<Type>> & Pick<WithPlentyOf, 'Utility'> & Types)
  | undefined;

// To:
const maybeValue2 = complexObject[maybeKey] as ?;

Jadi pemeriksaan jenis yang lebih ketat tetap ikut serta. Di bawah ini berfungsi tetapi membutuhkan duplikasi (dan dengan cepat menambahkan jumlah kebisingan visual yang serupa):

const maybeValue2 = complexObject[maybeKey] as
  | typeof complexObject[typeof maybeKey]
  | undefined;

Saya membayangkan sintaks "ketik tegaskan untuk mungkin" semacam ini ( as ? ) juga akan mempermudah penerapan aturan eslint require-safe-index-signature-access yang tidak terlalu membingungkan bagi pengguna baru. (Misalnya "Anda dapat memperbaiki kesalahan lint dengan menambahkan as ? di bagian akhir.") Aturan bahkan dapat menyertakan autofixer yang aman karena hanya dapat menyebabkan kompilasi rusak, tidak pernah masalah runtime.

function eh<T>(v: T): T | undefined {
    return v;
}

const arr = [0, 1, 2, 3, 4, 5];
const thing1 = arr[1]; // number
const thing2 = eh(arr[1]); // number | undefined

Saya rasa tidak ada satu aplikasi pun selain fitur TS kejutan ini hehe

function eh<T>(v: T): T | undefined {
    return v;
}

const arr = [0, 1, 2, 3, 4, 5];
const thing1 = arr[1]; // number
const thing2 = eh(arr[1]); // number | undefined

Terima kasih atas tanggapannya. Sementara fungsi seperti ini akan berfungsi (dan hampir pasti akan dioptimalkan oleh sebagian besar mesin JS), saya lebih suka menerima kebisingan sintaks tambahan yang disebutkan ( as ComplexType | undefined ) daripada file JS yang dikompilasi mempertahankan metode ini "wrapper "di mana pun itu digunakan.

Opsi sintaks lainnya (yang akan kurang spesifik kasus penggunaan daripada as ? atas) dapat memungkinkan penggunaan kata kunci infer dalam pernyataan tipe sebagai pengganti untuk tipe yang disimpulkan sebaliknya, misalnya:

const maybeValue = complexObject[maybeKey] as infer | undefined;

Saat ini deklarasi infer hanya diizinkan dalam klausa extends dari tipe bersyarat ( ts(1338) ), jadi memberi arti kata kunci infer dalam konteks as [...] jenis pernyataan tidak akan mengganggu.

Itu masih masuk akal untuk ditegakkan menggunakan infer | undefined melalui aturan eslint sementara cukup umum untuk kasus lain, misalnya di mana jenis yang dihasilkan dapat disempurnakan dengan jenis utilitas:

// Strange example, maybe from an unusual compiler originally written in JS:
if(someRegularExpression.test(maybeKey)) {
  /**
  * If we are inside this code block and this `maybeKey` exists on `partialObject`, 
  * we know its `regexSuccess` must also be defined, though this knowledge 
  * cannot be encoded using TypeScript's type system.
  */
  const maybeValue = partialObject[maybeKey] as (Required<Pick<infer, 'regexSuccess'>> & infer) | undefined;
  // ...
}
// Here we don't know if `regexSuccess` is defined on `maybeValue2`:
const maybevalue2 = partialObject[maybeKey] as infer | undefined;
function eh<T>(v: T): T | undefined {
    return v;
}

const arr = [0, 1, 2, 3, 4, 5];
const thing1 = arr[1]; // number
const thing2 = eh(arr[1]); // number | undefined

Percuma jika kita harus terus mengingat untuk menggunakan pola lain yang lebih aman. Intinya adalah agar TypeScript mendukung kita, apakah kita sedang dalam hari yang sangat baik, lelah atau baru mengenal basis kode.

Perilaku kami saat ini adalah bahwa ekspresi tipe Array<string>[number] ditafsirkan sebagai "Tipe yang muncul saat Anda mengindeks array dengan angka". Anda diizinkan untuk menulis

const t: Array<string>[number] = "hello";

sebagai cara memutarbalikkan penulisan const t: string .

Jika bendera ini ada, apa yang harus terjadi?

Jika Array<string>[number] adalah string | undefined , maka Anda memiliki masalah kesehatan penulisan:

function write<T extends Array<unknown>>(arr: T, v: T[number]) {
    arr[0] = v;
}
const arr = ["a", "b", "c"];
// Would be OK
write(arr, undefined);

Jika Array<string>[number] adalah string , maka Anda memiliki masalah yang sama seperti yang dijelaskan pada bacaan:

function read<T extends Array<unknown>>(arr: T): T[number] {
    return arr[14];
}
const arr = ["a", "b", "c"];
// Would be OK
const k: string = read(arr);

Tampaknya terlalu rumit untuk menambahkan sintaks tipe untuk "Jenis baca indeks" dan "Jenis penulisan indeks". Pikiran?

Saya harus menambahkan bahwa mengubah arti Array<string>[number] benar-benar bermasalah karena itu akan menyiratkan bahwa bendera ini mengubah interpretasi file deklarasi. Ini menegangkan karena ini sepertinya jenis bendera yang akan diaktifkan oleh sejumlah besar orang, tetapi dapat menyebabkan kesalahan terjadi/tidak terjadi pada hal-hal yang diterbitkan dari PastiTyped, jadi kami mungkin harus memastikan bahwa semuanya dibangun bersih di kedua arah. Jumlah flag yang dapat kita tambahkan dengan perilaku ini jelas sangat kecil (kita tidak dapat menguji 64 konfigurasi berbeda dari setiap jenis paket) jadi saya pikir kita harus meninggalkan Array<T>[number] sebagai T hanya untuk menghindari masalah itu.

@RyanCavanaugh Dalam contoh Anda dengan write(arr, undefined) , panggilan ke write akan diterima, tetapi bukankah tugas arr[0] = v; tidak lagi dikompilasi?

Jika fungsi write atas berasal dari library, dan merupakan JS yang dikompilasi tanpa flag, maka pasti akan tidak sehat, Namun, itu sudah menjadi masalah karena library dapat dikompilasi dengan flag apa pun yang mereka pilih. Jika perpustakaan dikompilasi tanpa pemeriksaan nol yang ketat, maka fungsi di dalamnya dapat mengembalikan string , sedangkan itu benar-benar harus mengembalikan string|undefined . Tetapi sebagian besar perpustakaan hari ini tampaknya menerapkan pemeriksaan nol, jika tidak, orang akan mengeluh. Demikian juga, begitu tanda baru ini ada, semoga perpustakaan mulai mengimplementasikannya dan akhirnya sebagian besar perpustakaan akan mengaturnya.

Juga, sementara saya tidak dapat menemukan contoh konkret dari atas kepala saya, saya pasti harus mengatur skipLibCheck untuk mendapatkan aplikasi saya untuk dikompilasi. Ini adalah opsi tsconfig standar di boilerplate kami setiap kali membuat repo TS baru, karena kami memiliki banyak masalah dengannya.

Secara pribadi (dan mungkin egois), saya baik-baik saja menerima kasus Edge, jika itu membuat kode _my_ umumnya lebih aman. Saya masih percaya saya akan mengalami pointer nol dari mengakses array dengan indeks lebih sering daripada saya akan mengalami kasus Edge lainnya, meskipun saya bisa diyakinkan sebaliknya.

Jika Array[angka] adalah string | tidak terdefinisi, maka Anda memiliki masalah kesehatan pada penulisan:

Menurut pendapat saya, Array<string>[number] harus selalu string|undefined . Kenyataannya, jika saya mengindeks ke dalam array dengan nomor apa pun, saya akan mendapatkan tipe item array atau tidak terdefinisi. Anda tidak bisa lebih spesifik di sana kecuali Anda menyandikan panjang array seperti yang Anda bisa dengan tupel. Contoh penulisan Anda tidak akan mengetik centang, karena Anda tidak dapat menetapkan string|undefined ke indeks array.

Tampaknya terlalu rumit untuk menambahkan sintaks tipe untuk "Jenis baca indeks" dan "Jenis penulisan indeks". Pikiran?

Ini tampaknya persis seperti yang seharusnya karena mereka adalah dua hal yang berbeda. Tidak ada array yang indeksnya pernah ditentukan, jadi Anda akan selalu memiliki potensi untuk tidak terdefinisi. Jenis Array<string>[number] akan menjadi string|undefined . Untuk menentukan apa yang Anda inginkan T dalam Array<T> , tipe utilitas dapat digunakan (penamaan tidak bagus): ArrayItemType<Array<string>> = string . Ini tidak membantu dengan tipe Record , yang mungkin memerlukan sesuatu seperti RecordValue<Record<string, number>, string> = string .

Saya setuju bahwa tidak ada solusi hebat di sini, tetapi saya cukup yakin saya lebih suka kesehatan pada indeks yang dibaca.

Saya tidak merasa sangat kuat tentang perlunya ini pada array, karena banyak bahasa lain (termasuk yang "aman" seperti Rust) menyerahkan tanggung jawab untuk pemeriksaan batas kepada pengguna, jadi saya dan banyak pengembang sudah terbiasa melakukannya . Juga, sintaks cenderung membuatnya cukup jelas dalam kasus ini, karena mereka _always_ menggunakan notasi braket seperti foo[i] .

Yang mengatakan, saya _do_ merasa sangat ingin menambahkan ini ke objek yang diindeks string, karena sangat sulit untuk mengetahui apakah tanda tangan foo.bar benar (karena bidang bar didefinisikan secara eksplisit), atau mungkin undefined (karena merupakan bagian dari tanda tangan indeks). Jika kasus ini (yang menurut saya bernilai cukup tinggi) diselesaikan, maka kasus array kemungkinan menjadi sepele dan mungkin layak dilakukan juga.


Tampaknya terlalu rumit untuk menambahkan sintaks tipe untuk "Jenis baca indeks" dan "Jenis penulisan indeks". Pikiran?

Sintaks pengambil dan penyetel dari javascript dapat diperluas ke definisi tipe. Misalnya, sintaks berikut sepenuhnya dipahami oleh TypeScript:

const foo = {
  _bar: "",
  get bar(): string {
    return this._bar;
  },
  set bar(value: string) {
    this._bar = value;
  }
}

Namun, TS saat ini "menggabungkan" ini menjadi satu jenis, karena tidak melacak jenis bidang "mendapatkan" dan "mengatur" secara terpisah. Pendekatan ini berfungsi untuk sebagian besar kasus umum, tetapi memiliki kekurangannya sendiri, seperti mengharuskan pengambil dan penyetel untuk berbagi jenis yang sama, dan salah menetapkan jenis pengambil ke bidang yang hanya mendefinisikan penyetel.

Jika TS melacak "get" dan "set" secara terpisah untuk semua bidang (yang kemungkinan memiliki beberapa biaya kinerja nyata) itu akan menyelesaikan kebiasaan ini, dan juga menyediakan mekanisme untuk menggambarkan fitur yang dijelaskan dalam masalah ini:

type foo = {
  [key: string]: string
}

pada dasarnya akan menjadi singkatan untuk:

type foo = {
  get [key: string](): string | undefined;
  set [key: string](string): string;
}

Saya harus menambahkan bahwa mengubah arti Array[nomor] benar-benar bermasalah karena itu akan menyiratkan bahwa bendera ini mengubah interpretasi file deklarasi.

Jika file deklarasi yang dihasilkan selalu menggunakan sintaks pengambil+penyetel lengkap, ini akan _only_ menjadi masalah untuk yang ditulis tangan. Menerapkan aturan saat ini ke sintaks steno yang tersisa _dalam file definisi saja_ mungkin menjadi solusi di sini. Itu pasti akan memecahkan masalah kompatibilitas, tetapi juga akan menambah biaya mental untuk membaca file .ts vs file .d.ts .


Ke samping:

Sekarang tentu saja, ini tidak membahas _all_ peringatan halus dengan getter/setter:

foo.bar = "hello"

// TS assumes that bar is now a string, which technically isn't guaranteed when
// custom setters and getters are used.
const result: string = foo.bar

Ini adalah kasus tepi lebih lanjut, dan mungkin layak dipertimbangkan jika itu dalam lingkup tujuan TS sama sekali ... tetapi bagaimanapun itu mungkin dapat diselesaikan secara terpisah, karena proposal saya di sini konsisten dengan perilaku TypeScript saat ini.

Saya benar-benar merasa seperti saya hanya perlu muncul di setiap beberapa halaman komentar dan menjatuhkan tautan ke #2521 lagi.

Tampaknya terlalu rumit untuk menambahkan sintaks tipe untuk "Jenis baca indeks" dan "Jenis penulisan indeks". Pikiran?

Tidak! Dan setidaknya (klik link...) 116 orang lain di sini setuju dengan saya. Saya akan sangat senang setidaknya memiliki opsi untuk mengetik membaca dan menulis secara berbeda. Ini hanyalah kasus penggunaan hebat lainnya untuk fitur itu.

Saya akan _sangat senang_ setidaknya memiliki opsi untuk mengetik membaca dan menulis secara berbeda. Ini hanyalah _yet another_ kasus penggunaan yang bagus untuk fitur itu.

Saya percaya maksudnya adalah untuk mempertanyakan apakah harus ada cara untuk merujuk ke tipe baca dan tulis, misalnya haruskah ada sesuatu seperti Array<string>[number]= untuk merujuk ke tipe tulis?

Saya akan senang dengan resolusi #2521 dan masalah ini jika dua hal terjadi:

  • Pertama, ketik getter dan setter secara berbeda, sesuai #2521
  • Kemudian, buat tanda seperti yang dibahas dalam masalah ini, sehingga "tipe tulis" dari Array<string>[number] adalah string sedangkan "tipe baca" adalah string | undefined .

Saya (keliru?) berasumsi bahwa yang pertama akan meletakkan dasar untuk yang terakhir, lihat https://github.com/microsoft/TypeScript/issues/13778#issuecomment -630770947 . Saya tidak memiliki kebutuhan khusus untuk sintaks eksplisit untuk mendefinisikan "tipe tulis" sendiri.

@RyanCavanaugh meta-pertanyaan cepat: apakah akan lebih berguna untuk memiliki saran saya yang agak spesifik di atas sebagai masalah terpisah untuk tujuan memperdebatkan dan melacak kekuatan/kelemahan/biayanya dengan lebih jelas (terutama karena biaya perf akan menjadi nontrivial)? Atau apakah ini hanya menambah masalah kebisingan?

@RyanCavanaugh

const t: Array<string>[number] = "hello";

sebagai cara memutarbalikkan penulisan const t: string .

Saya kira ini adalah alasan yang sama untuk tupel?

const t : [string][number] = 'hello' // const t: string

namun tupel mengetahui batasan dan mengembalikan undefined untuk jenis nomor yang lebih spesifik:

const t0: [string][0] = 'hello' // ok
const t1: [string][1] = 'hello' // Type '"hello"' is not assignable to type 'undefined'.

// therefore this is already true!
const t2: [string][0|1] // string | undefined 

mengapa mengetik number bertindak berbeda?

Sepertinya Anda memiliki mekanisme untuk melakukan ini: https://github.com/microsoft/TypeScript/issues/38779

Ini akan memenuhi kebutuhan saya tanpa menambahkan bendera tambahan.

Jika ini membantu siapa pun, inilah plugin ESLint yang menandai akses array yang tidak aman dan perusakan array/objek. Itu tidak menandai penggunaan yang aman seperti tupel dan objek (non-rekam).

https://github.com/danielnixon/eslint-plugin-total-functions

Cheers, tapi saya harap jelas ini masih perlu ditambahkan pada tingkat bahasa karena murni dalam bidang typechecking dan beberapa proyek tidak memiliki linter atau aturan yang tepat.

@RyanCavanaugh

kami juga telah menguji seperti apa fitur ini dalam praktiknya dan saya dapat memberi tahu Anda bahwa itu tidak cantik

Hanya ingin tahu, bisakah Anda memberi tahu kami bagian yang tidak cantik ? Mungkin kita bisa mengatasinya jika Anda bisa berbagi lebih banyak tentang ini.

Ini seharusnya tidak menjadi opsi, itu harus menjadi default .

Yaitu, jika sistem tipe TypeScript adalah untuk menggambarkan pada waktu kompilasi tipe yang dapat diambil oleh nilai JavaScript saat run-time:

  • mengindeks Array tentu saja dapat mengembalikan undefined dalam JavaScript (indeks di luar batas atau larik jarang), jadi tanda tangan baca yang benar adalah T | undefined .
  • "menyebabkan" indeks dalam Array menjadi undefined juga dimungkinkan melalui kata kunci delete .

Seperti yang dicegah oleh TypeScript 4

const a = [1, 2]
delete a[1]

ada kasus bagus untuk juga mencegah a[1] = undefined .
Ini menunjukkan bahwa Array<T>[number] memang harus berbeda untuk membaca dan menulis.

Ini mungkin "kompleks" tetapi akan memungkinkan untuk memodelkan kemungkinan run-time dalam JavaScript dengan lebih akurat;
Untuk apa TypeScript _dibuat untuk_, bukan?

Tidak ada gunanya membahas gagasan membuat | undefined mengembalikan perilaku default -- mereka tidak akan

Saya setuju dengan sisa posting sekalipun.

Tidak ada gunanya mendiskusikan ide membuat | undefined mengembalikan perilaku default -- mereka akan _tidak pernah_ merilis versi TypeScript yang meledakkan jutaan baris kode lama karena pembaruan kompiler. Perubahan seperti itu akan menjadi satu-satunya perubahan yang paling merusak dalam proyek apa pun yang pernah saya ikuti.

Saya setuju dengan sisa posting sekalipun.

Tidak jika itu adalah opsi konfigurasi.

Tidak jika itu adalah opsi konfigurasi.

Itu yang adil, tapi setidaknya saya harapkan "memimpin dalam" lama periode di mana itu off secara default, orang-orang memberikan banyak waktu (6 bulan? Tahun? Lebih?) Ke kode warisan convert sebelum menjadi on-oleh -bawaan. Seperti, tambahkan flag di v4.0, setel ke on-by-default di v5.0, hal semacam itu.

Tapi itu jauh di masa depan, pada titik ini, karena kami bahkan tidak memiliki persetujuan bahwa fitur prasyarat (jenis yang berbeda untuk setter/getter) layak dilakukan. Jadi saya mendukung pernyataan bahwa tidak ada gunanya membahas perilaku default untuk waktu yang sangat lama.

Tidak ada gunanya mendiskusikan ide membuat | undefined mengembalikan perilaku default -- mereka akan _tidak pernah_ merilis versi TypeScript yang meledakkan jutaan baris kode lama karena pembaruan kompiler. Perubahan seperti itu akan menjadi satu-satunya perubahan yang paling merusak dalam proyek apa pun yang pernah saya ikuti.

Saya setuju dengan sisa posting sekalipun.

Untuk poin Anda @ thw0rted saya mengusulkan kompromi di sini: https://github.com/microsoft/TypeScript/issues/38779

Hal ini memungkinkan fungsi ini, dalam fitur yang diperkenalkan jauh kemudian, yang masih belum banyak digunakan, dan sepertinya sudah "hampir" ada dan hanya perlu perubahan kecil.

Untuk konsistensi "strict" sesuatu dari Array<T> harus dilihat sebagai "a list of type T that is infinitly long with no gaps" Kapan pun ini tidak diinginkan, Anda harus menggunakan tupel..

Ya ada kekurangan untuk ini, tetapi sekali lagi saya merasa ini adalah "kompromi" yang adil antara fitur ini yang tidak pernah diimplementasikan dan diimplementasikan persis seperti yang kami minta di sini.

Jika Anda setuju, silakan upvote

Saya tidak setuju bahwa ini membutuhkan opsi/konfigurasi yang sama sekali baru.

Saya tidak berpikir itu harus menjadi perilaku default ketika opsi ketat dinonaktifkan tetapi ketika saya mengaktifkan opsi ketat BE STRICT dan menegakkannya bahkan pada proyek yang lebih lama. Mengaktifkan ketat adalah apa yang orang pilih ke dalam pengetikan yang paling ketat/akurat. Jika saya mengaktifkan opsi yang ketat, saya ingin percaya pada kode saya dan itulah bagian terbaik tentang TS. Tetapi pengetikan yang tidak akurat ini bahkan dengan pengaktifan yang ketat hanya membuat pusing.

Demi menjaga agar utas ini dapat dibaca, saya telah menyembunyikan sejumlah komentar tangensial yang tidak menambah percakapan secara bermakna. Secara umum, Anda dapat berasumsi bahwa komentar dalam nada berikut telah dibuat sebelumnya di 212 komentar sebelumnya dan tidak perlu diulang:

  • TypeScript harus memperkenalkan perubahan besar yang tidak praktis dalam memeriksa perilaku untuk pertama kalinya - tidak, kami belum pernah melakukannya sebelumnya, dan tidak bermaksud melakukannya di masa mendatang, harap lebih terlibat dengan tujuan desain jangka panjang proyek sebelum datang ke sini untuk mengadvokasi perubahan yang belum pernah terjadi sebelumnya
  • Argumen didasarkan pada gagasan bahwa tidak ada yang mempertimbangkan fakta bahwa bendera konfigurasi ada - kami sadar bahwa meletakkan sesuatu di belakang bendera berarti mereka tidak perlu diaktifkan secara default, ini bukan rodeo pertama siapa pun
  • Tunggu apa lagi, lakukan saja! - sekarang setelah Anda mengatakannya seperti itu, saya yakin

Ada tantangan praktis dan teknis yang sangat nyata yang perlu dipecahkan di sini dan sangat mengecewakan bahwa kami tidak dapat melakukan pekerjaan yang berarti di utas ini karena dipenuhi dengan orang-orang yang mampir untuk menyarankan bahwa apa pun di balik bendera tidak memiliki konsekuensi nyata atau kesalahan apa pun yang dapat be ts-ignore 'd bukan beban upgrade.

@RyanCavanaugh

terima kasih atas ringkasan Anda.

harap lebih terlibat dengan tujuan desain jangka panjang proyek sebelum datang ke sini untuk mengadvokasi perubahan yang belum pernah terjadi sebelumnya

Saya telah menghapus komentar saya dan jika Anda mau, saya juga dapat menghapus komentar ini. Tetapi bagaimana memahami tujuan pertama proyek:

1. Secara statis mengidentifikasi konstruksi yang mungkin kesalahan.

(Sumber: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals )

Saya tidak mengerti komentar Anda yang dikutip, karena akses indeks yang tidak dicentang dapat menyebabkan kemungkinan kesalahan runtime. Jika di JavaScript Anda mengharapkan hal seperti itu, di TypeScript Anda mendapatkan kepercayaan yang berbahaya dan salah. Jenis yang salah lebih buruk daripada any .

nah sekarang setelah Anda mengatakannya seperti itu, saya yakin

Tentu saja, Anda adalah salah satu pengelola inti dan terima kasih serta tim dan kontributor untuk ts. Tapi ini bukan hanya tentangmu lagi. Bandingkan upvotes: 365 dengan downvotes: 6! Hanya 6! Ini menunjukkan permintaan besar untuk typesafety.

Tapi mari kita bicara tentang solusi. Apakah ada solusi yang akan dibuat atau dapat dipikirkan oleh tim?

Bisakah Anda menjelaskan lebih lanjut tentang apa yang salah dengan ts-ignore dalam kasus ini? Maksud saya itu dapat diotomatisasi dengan alat seperti codemod dan tidak mengubah perilaku kode saat ini. Tentu saja ini bukan solusi yang bagus, tapi yah, ini salah satu cara yang mungkin untuk memperkenalkan perubahan ini tanpa flag.

Apa pendapat Anda tentang penerbitan otomatis versi ts yang ditambal, misalnya 4.0.0-breaking ? Ini memperkenalkan beberapa (banyak?) bekerja pada konflik, tetapi memungkinkan semua orang untuk menguji perubahan dan menyiapkan basis kode (tidak hanya untuk permintaan fitur ini). Ini bisa dilakukan untuk jangka waktu terbatas, seperti 3-6 bulan. Kami akan menjadi yang pertama, yang akan menggunakan versi ini.

lagi. Bandingkan upvotes: 365 dengan downvotes: 6! Hanya 6! Ini menunjukkan permintaan besar untuk typesafety.
- @Bessonov

ha ha.
Bukannya saya setuju dengan seluruh postingan @RyanCavanaugh .. tapi... ayolah..
ada apa jutaan? Pengguna TS di luar sana?!? 365 ingin fitur ini cukup untuk berkomentar dan memberi suara di utas ini...

Tidak ada pemberitahuan dari github, tetapi untuk semua orang yang akan mencoba dan bermain dengan indeks yang tidak ditentukan dari masalah ini, lihat draf PR di atas komentar saya.

@RyanCavanaugh terima kasih banyak telah memberi kami kesempatan untuk bermain dengannya. Saya menjalankannya terhadap basis kode yang cukup kecil (~105 ts(x) file) dan memainkannya sedikit. Saya tidak menemukan masalah penting. Satu baris yang diubah dari:

const refHtml = useRef(useMemo(() => document.getElementsByTagName('html')[0], []))

ke:

const refHtml = useRef(useMemo(() => document.getElementsByTagName('html')[0] ?? null, []))

Saya akan mencobanya pada proyek menengah minggu depan.

@lonewarrior556 60 kali lebih banyak dan ada di halaman pertama jika Anda mengurutkan berdasarkan upvotes :)

@Bessonov Saya pikir Anda harus mencobanya pada basis kode kompiler TypeScript, itu mungkin akan menyebabkan kerusakan besar karena penggunaan for-loop yang tidak sepele.

Anda bahkan dapat memiliki tanda untuk memilih masuk/keluar untuk memberikan kompatibilitas mundur.

Setiap flag yang Anda tambahkan adalah matriks konfigurasi lain yang perlu diuji dan didukung. Untuk alasan itu TypeScript ingin menjaga flag seminimal mungkin.

@MatthiasKunnen Jika ini adalah beberapa fitur kasus tepi kecil maka saya setuju bahwa itu adalah alasan yang tepat untuk tidak menambahkan bendera untuk ini. Tetapi akses array langsung muncul dalam kode cukup sering, dan merupakan lubang mencolok dalam sistem tipe dan juga perubahan yang harus diperbaiki, jadi saya merasa bendera akan dijamin.

Bendera adalah pendekatan yang salah untuk ini, sebagian karena kombinatorial
masalah. Kita harus memanggil TypeScript v5 ini... pendekatan semacam itu
mengubah jumlah kombinasi yang dapat diuji dari 2^N menjadi hanya N...

Pada Sabtu, 15 Agustus 2020 pukul 15:56, Michael Burkman [email protected]
menulis:

@MatthiasKunnen https://github.com/MatthiasKunnen Jika ini beberapa
fitur case tepi kecil maka saya setuju bahwa itu adalah alasan yang layak untuk tidak
tambahkan bendera untuk ini. Tetapi akses array langsung muncul dalam kode cukup
sering, dan merupakan lubang mencolok dalam sistem tipe dan juga a
melanggar perubahan untuk diperbaiki, jadi saya merasa bendera akan dijamin.


Anda menerima ini karena Anda berlangganan utas ini.
Balas email ini secara langsung, lihat di GitHub
https://github.com/microsoft/TypeScript/issues/13778#issuecomment-67448067 ,
atau berhenti berlangganan
https://github.com/notifications/unsubscribe-auth/AAADY5AXEY65S5HDGNGIPZDSA2O3FANCNFSM4C6KEKAA
.

Baru saja terjadi kesalahan tak terduga yang tidak terduga menurunkan aplikasi saya untuk ratusan pengguna, kesalahan yang akan ditangkap pada waktu pembuatan jika pemeriksaan jenis ini dilakukan. TypeScript telah menjadi cara yang luar biasa bagi kami untuk menghadirkan perangkat lunak yang lebih andal, tetapi utilitasnya yang hebat dirusak oleh kelalaian tunggal ini.

Bagaimana dengan 10 orang berikutnya yang ingin berkomentar di sini hanya menguji draf PR #39560 saja?

Ini sangat mengganggu jika Anda ingin mengaktifkan aturan @typescript-eslint/no-unnecessary-condition ESLint karena kemudian mengeluh tentang semua instance

if (some_array[i] === undefined) {

Ia berpikir itu adalah kondisi yang tidak perlu (karena TypeScript mengatakan itu!) tetapi tidak. Saya tidak benar-benar ingin menambahkan // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition seluruh basis kode saya.

Jika memperbaiki ini dengan benar terlalu banyak untuk bekerja untuk programmer Javascript yang malas, mungkin kita bisa memiliki sintaks akses array alternatif yang akan menambahkan undefined , misalnya

if (some_array[?i] === undefined) {

(saran sintaks acak; alternatif diterima)

@Timmmm silakan baca komentar tepat di atas yang Anda buat. Ada bangunan yang dapat Anda coba yang mengimplementasikan versi saran ini -- Anda dapat memberi tahu kami di sana jika itu menyelesaikan masalah eslint .

@ the0rted saya memang membaca komentar itu. Itu tidak mengimplementasikan versi saran saya. Mungkin Anda hanya harus membacanya lagi.

Maaf, ketika saya mengatakan saran "ini" yang saya maksud adalah kemampuan dalam OP, yaitu memperlakukan array_of_T[i] sebagai T | undefined . Saya melihat bahwa Anda bertanya tentang sintaks untuk menandai operator indeks tertentu sebagai "mungkin tidak terdefinisi", tetapi jika Anda menggunakan implementasi di PR Ryan, Anda tidak memerlukannya, karena semua operator indeks akan menjadi "mungkin tidak terdefinisi" . Bukankah itu memenuhi kebutuhan Anda?

Ya itu akan - jika/ketika mendarat saya pasti akan menggunakannya. Tetapi saya mendapat kesan dari diskusi ini bahwa banyak orang yang menolaknya karena menambahkan tanda tambahan dan membuat kode yang sepertinya harus bekerja lebih rumit (contoh for loop sederhana).

Jadi saya ingin menawarkan saran alternatif, tetapi tampaknya orang-orang membencinya. :-/

Semua masukan yang bermaksud baik dihargai, jangan salah paham @Timmmm. Saya pikir downvotes hanya menyampaikan bahwa ini sudah diinjak dengan baik di utas ini pada saat ini.

Bagi saya, menambahkan tanda yang meningkatkan keamanan jenis di seluruh papan adalah biaya yang jauh lebih rendah daripada memperkenalkan sintaks keikutsertaan baru.

Saya tidak menyebutkannya di atas, tetapi sintaks untuk penggantian satu kali sudah ada: if ((some_array[i] as MyType | undefined) === undefined) . Ini tidak singkat sebagai singkatan baru tapi mudah-mudahan itu bukan membangun Anda harus menggunakan sangat sering.

_Awalnya diposting oleh @osyrisrblx di https://github.com/microsoft/TypeScript/issues/40435#issuecomment -690017567_

Meskipun ini tampak seperti opsi yang jauh lebih aman, alangkah baiknya untuk menyisih dari perilaku ini dengan sintaks terpisah untuk kejadian langka di mana Anda 100% yakin bahwa itu selalu ditentukan.

Mungkin !: dapat menegaskan selalu didefinisikan?

interface X {
    [index: string]!: number; // -> number
}

interface Y {
    [index: string]: number; // -> number | undefined
}

Saran itu salah mencirikan masalah. Masalahnya bukan pada pemahaman TypeScript tentang tipe itu sendiri. Masalahnya berasal dari _mengakses_ data, di mana sintaks baru telah diusulkan sebelumnya di utas ini.

Anda dapat mencoba ini di TypeScript 4.1 beta, segera keluar ™ (atau malam ini jika Anda membutuhkannya sekarang! )

Maaf jika ini ditawarkan sebelumnya, tetapi bisakah {[index: string]?: number} // -> number | undefined didukung? Ini konsisten dengan sintaks properti opsional antarmuka - diperlukan secara default, mungkin tidak ditentukan dengan "?".

Opsi kompiler di 4.1 keren, tetapi memiliki kontrol yang lebih terperinci juga bagus.

Jika Anda ingin mencobanya di repo Anda (butuh sedikit waktu untuk mencari tahu):

  1. Instal TypeScript@next yarn (add|upgrade) typescript@next
  2. Tambahkan bendera (untuk saya di tsconfig.json) "noUncheckedIndexedAccess": true

Dalam proses mengaktifkan aturan ini di proyek saya, saya menemukan kesalahan yang menarik ini:

type MyRecord = { a: number; b: string };

declare const myRecord: MyRecord;

declare const key: 'a' | 'b';
const value = myRecord[key]; // string | number ✅

// ❌ Unexpected error
// Type 'MyRecord[Key] | undefined' is not assignable to type 'MyRecord[Key]'
const fn = <Key extends keyof MyRecord>(key: Key): MyRecord[Key] => myRecord[key];

Dalam hal ini saya tidak mengharapkan myRecord[key] untuk mengembalikan tipe MyRecord[Key] | undefined , karena key dibatasi ke keyof MyRecord .

Pembaruan : masalah yang diajukan https://github.com/microsoft/TypeScript/issues/40666

Itu mungkin bug/pengawasan!

Dalam proses mengaktifkan aturan ini di proyek saya, saya menemukan kesalahan yang menarik ini:

type MyRecord = { a: number; b: string };

declare const myRecord: MyRecord;

declare const key: 'a' | 'b';
const value = myRecord[key]; // string | number ✅

// ❌ Unexpected error
// Type 'MyRecord[Key] | undefined' is not assignable to type 'MyRecord[Key]'
const fn = <Key extends keyof MyRecord>(key: Key): MyRecord[Key] => myRecord[key];

Dalam hal ini saya tidak mengharapkan myRecord[key] untuk mengembalikan tipe MyRecord[Key] | undefined , karena key dibatasi ke keyof MyRecord .

Saya akan mengatakan itu bug. Pada dasarnya jika keyof Type hanya menyertakan tipe literal string/angka (berlawanan dengan sebenarnya termasuk string atau number ), maka Type[Key] mana Key extends keyof Type seharusnya tidak menyertakan undefined , menurut saya.

tautan silang ke #13195, yang juga melihat perbedaan dan persamaan antara "tidak ada properti di sini" dan "ada properti di sini tetapi undefined "

Melacak beberapa masalah yang terkait dengan batasan desain di sini

  • #41612

FYI: Saya menangkap dua bug di basis kode saya berkat flag ini, terima kasih banyak!
Agak menjengkelkan bahwa memeriksa arr.length > 0 tidak cukup untuk menjaga arr[0] , tapi itu ketidaknyamanan kecil (saya dapat menulis ulang cek untuk membuat tsc senang) dibandingkan dengan keamanan ekstra, IMO.

@rubenlg Saya setuju tentang arr.length . Untuk hanya memeriksa elemen pertama, saya telah menulis ulang kode kami seperti

const firstEl = arr[0];
if (firstEl !== undefined) {
  ...
}

Tetapi ada beberapa tempat di mana kami melakukan if (arr.length > 2) atau lebih, yang agak canggung. Namun saya tidak berpikir pemeriksaan .length akan benar-benar mengetik aman karena Anda bisa memodifikasinya:

const a: number[] = [];

a.length = 1;

if (a.length > 0) {
    const b: number = a[0];
    console.log(b);
}

Mencetak undefined .

Namun saya tidak berpikir pemeriksaan .length akan benar-benar mengetik aman karena Anda bisa memodifikasinya:

const a: number[] = [];

a.length = 1;

if (a.length > 0) {
    const b: number = a[0];
    console.log(b);
}

Mencetak undefined .

Itu pada dasarnya akan menjadi array yang jarang, yang berada di luar cakupan untuk hal semacam ini. Selalu ada hal-hal non-standar atau licik yang bisa dilakukan untuk menyiasati pemeriksa tipe.

Selalu ada cara untuk membatalkan asumsi sebelumnya, dan itulah masalahnya. Menganalisis setiap jalur eksekusi yang mungkin akan menjadi terlalu mahal.

const a: number[] = [1]
if (a.length > 0) {
    a.pop();
    console.log(a[0])
}
Apakah halaman ini membantu?
0 / 5 - 0 peringkat