Typescript: Saran: tipe yang tidak dapat dibatalkan

Dibuat pada 22 Jul 2014  ·  358Komentar  ·  Sumber: microsoft/TypeScript

Perkenalkan dua sintaks baru untuk deklarasi tipe berdasarkan JSDoc

var myString: !string = 'hello world'; //non-nullable
var myString1: ?string = 'hello world'; // nullable 
var myString2: string = 'hello world'; // nullable 
var myString3 = 'hello world'; // nullable

dengan tipe default adalah nullable.

Dua flag kompiler baru:

  • inferNonNullableType membuat kompiler menyimpulkan tipe non-nullable :
var myString3 = 'hello world' // typeof myString is '!string', non-nullable
  • nonNullableTypeByDefault (saya kira mungkin ada nama yang lebih baik):
var myString: !string = 'hello world'; // non-nullable
var myString1: string = 'hello world'; // non-nullable 
var myString2: ?string = 'hello world'; // nullable 
var myString3 = 'hello world' // non-nullable
Committed Fixed Suggestion

Komentar yang paling membantu

Maaf jika saya mengomentari masalah yang ditutup tetapi saya tidak tahu tempat yang lebih baik untuk bertanya dan saya tidak berpikir ini layak untuk masalah baru jika tidak ada minat.
Apakah layak untuk menangani null implisit berdasarkan per-file?
Seperti, menangani banyak file td dengan noImplicitNull (karena mereka berasal dari pasti diketik dan dikandung seperti itu) tetapi menangani sumber saya sebagai implisitNull?
Apakah ada yang menganggap ini berguna?

Semua 358 komentar

Saya sarankan menggunakan tipe selain string sebagai contoh karena pada dasarnya tidak dapat dibatalkan. :P
Saya dapat melihat tipe yang tidak dapat dibatalkan menjadi bermasalah karena pengguna dan kompiler "!" mengharapkan tipe untuk selalu menjadi non-null, yang tidak pernah dapat benar-benar dinyatakan dalam JavaScript. Seorang pengguna mungkin mendefinisikan sesuatu seperti ini:

function(myNonNull:!myClass):void {
  myNonNull.foo();
}

Dan karena itu didefinisikan sebagai non-null, semuanya mungkin menyenangkan untuk kompiler, tetapi kemudian orang lain yang menggunakannya dalam javascript melewati sesuatu yang null dan kaboom.

Sekarang dikatakan, mungkin solusinya adalah untuk metode yang menghadap publik, itu bisa secara otomatis menegaskan bukan nol. Tetapi kemudian kompilator juga dapat menyatakan bahwa Anda tidak dapat memiliki properti publik (atau pribadi dalam hal ini benar-benar) yang dapat memiliki !deklarasi nonnull karena tidak dapat ditegakkan.

Ini mungkin masuk lebih dalam ke diskusi tentang kontrak kode agar ini dapat ditegakkan dengan benar.

Maafkan kritik saya, saya pikir ada sangat sedikit kebutuhan dalam tipe yang tidak dapat dibatalkan jika/segera setelah tipe data aljabar ada di sini. Alasan orang menggunakan null untuk mewakili nilai yang hilang adalah karena tidak ada cara yang lebih baik untuk melakukannya di JavaScript dan di sebagian besar bahasa OOP. Jadi pencitraan ADT sudah ada di sini. Kemudian, untuk lib lama yang ditulis sebelum non-nullables, memilikinya tidak akan membuat hidup lebih baik. Adapun lib baru, dengan ADT di tempat, seseorang dapat dengan sangat akurat memodelkan nilai yang dapat diambil sesuai dengan spesifikasi domain bisnis tanpa menggunakan nol sama sekali. Jadi saya kira apa yang saya katakan adalah bahwa ADT adalah alat yang jauh lebih kuat untuk mengatasi masalah yang sama.

Secara pribadi, saya baru saja menulis sedikit antarmuka Maybe<T> dan menggunakan disiplin untuk memastikan bahwa variabel jenis itu tidak pernah nol.

Saya sarankan menggunakan tipe selain string sebagai contoh karena pada dasarnya tidak dapat dibatalkan. :P
Saya dapat melihat tipe yang tidak dapat dibatalkan menjadi bermasalah karena pengguna dan kompiler "!" mengharapkan tipe untuk selalu menjadi non-null, yang tidak pernah dapat benar-benar dinyatakan dalam JavaScript. Seorang pengguna mungkin mendefinisikan sesuatu seperti ini:

function(myNonNull:!myClass):void {
myNonNull.foo();
}
Dan karena itu didefinisikan sebagai non-null, semuanya mungkin menyenangkan untuk kompiler, tetapi kemudian orang lain yang menggunakannya dalam javascript melewati sesuatu yang null dan kaboom.

Saya tidak begitu mengerti Anda juga dapat mendefinisikan:

function myFunc(str: string): int {
 return str && str.length;
}

dan jika seseorang meneruskan int ke fungsi itu, itu akan berakhir dengan kesalahan juga, keuntungan dari TypeScript adalah mendelegasikan ke kompiler meneruskan hal-hal yang akan Anda periksa secara manual di javascript, memiliki pemeriksaan lain untuk nullable/non -nullable type tampaknya masuk akal bagi saya. Omong-omong, SaferTypeScript dan ClosureCompiler sudah melakukan pemeriksaan semacam itu.

Dengan tipe union, kita bisa memiliki spesifikasi yang lebih sederhana untuk itu.
Katakanlah kita sekarang memiliki tipe dasar 'null', kita dapat memiliki mode 'lebih ketat' di mana 'null' dan 'undefined' tidak kompatibel dengan tipe apa pun, jadi jika kita ingin mengekspresikan nilai nullable yang akan kita lakukan:

var myNullableString: (null | string);
var myString = "hello";
myNullableString = myString //valid
myString = myNullableString // error null is not assignable to string;

Dengan 'mode ketat' TypeScript yang diaktifkan harus memeriksa bahwa setiap variabel yang tidak dapat dibatalkan diinisialisasi, juga secara default parameter opsional dapat dibatalkan.

var myString: string; // error
var myNullableString: (null | string); // no error

function myString(param1: string, param2?: string) {
  // param1 is string
  // param2 is (null | string)
}

@fdecampredon +1

IIRC dari apa yang ditunjukkan Facebook tentang Flow yang menggunakan sintaks TypeScript tetapi dengan tipe yang tidak dapat dibatalkan secara default, mereka mendukung singkatan untuk (null | T) seperti pada posting asli Anda - saya pikir itu ?T atau T? .

var myString: string; // error

Itu berpotensi sangat mengganggu jika Anda ingin menginisialisasi variabel secara kondisional, misalnya .:

var myString: string;
if (x) {
myString = a;
} else if (y) {
myString = b;
} else {
myString = c;
}

Di Rust misalnya, ini baik-baik saja selama kompiler dapat melihat bahwa myString akan diinisialisasi sebelum digunakan tetapi inferensi TypeScript tidak mendukung ini saat ini.

Jujur melakukan sesuatu seperti var myString = '' alih-alih var myString: string tidak terlalu mengganggu saya, tetapi tentu saja aturan seperti itu selalu memungkinkan.

@fdecampredon +1 untuk ini - Saya sangat menyukai ide itu. Untuk basis kode yang 100% JavaScript, ini akan menjadi batasan waktu kompilasi yang berguna. (Seperti yang saya pahami proposal Anda, tidak ada niat untuk kode yang dihasilkan untuk menegakkan ini?)

Adapun singkatan untuk (null | string) yakin ?string baik-baik saja.
Dan tentu saja @johnnyreilly itu hanya pemeriksaan waktu kompilasi

Jenis jumlah membuat jenis yang tidak dapat dibatalkan secara default menjadi kemungkinan yang sangat menarik. Properti keamanan non-nullable secara default tidak dapat dilebih-lebihkan. Jumlah jenis ditambah "jika/jenis perusakan" yang direncanakan (tidak yakin apa ini harus disebut) bahkan membuatnya aman untuk mengintegrasikan API nullable dan non-nullable.

Namun, membuat tipe non-nullable secara default adalah perubahan besar, yang akan membutuhkan perubahan hampir setiap file definisi tipe pihak ketiga yang ada. Sementara saya 100% untuk perubahan yang melanggar, tidak ada orang yang dapat memperbarui definisi tipe yang ada di alam liar.

Ada baiknya bahwa konsensus besar dari definisi ini dikumpulkan dalam repo PastiTyped, tetapi saya masih memiliki kekhawatiran praktis tentang fitur ini.

@samwgoldman idenya adalah untuk memiliki tipe non-nullable hanya di bawah flag compiler khusus seperti nonImplicitAny flag ini dapat diberi nama strict atau nonNullableType . Jadi tidak akan ada perubahan yang melanggar.

@fdecampredon Bagaimana dengan definisi tipe untuk pustaka non-TypeScript, seperti yang ada di PastiTyped? Definisi tersebut tidak diperiksa oleh kompiler, sehingga kode pihak ke-3 yang dapat mengembalikan null perlu dianotasi ulang agar berfungsi dengan benar.

Saya bisa membayangkan definisi tipe untuk fungsi yang saat ini dianotasi sebagai "mengembalikan string," tetapi terkadang mengembalikan nol. Jika saya bergantung pada fungsi itu dalam kode nonNullableType , kompiler tidak mengeluh (bagaimana bisa?) dan kode saya tidak lagi aman untuk nol.

Kecuali saya melewatkan sesuatu, saya rasa ini bukan fungsi yang dapat dihidupkan dan dimatikan dengan bendera. Tampaknya bagi saya bahwa ini adalah perubahan semantik semua-atau-tidak sama sekali untuk memastikan interoperabilitas. Saya akan senang jika terbukti salah, karena saya pikir fitur flag-switched lebih mungkin terjadi.

Selain itu, belum banyak informasi yang tersedia di Flow compiler Facebook, tetapi dari rekaman video presentasi, sepertinya mereka menggunakan non-nullable secara default. Jika demikian, setidaknya ada beberapa prioritas di sini.

Ok mari kita asumsikan ada singkatan ? type untuk type | null | undefined .

@fdecampredon Bagaimana dengan definisi tipe untuk pustaka non-TypeScript, seperti yang ada di PastiTyped? Definisi tersebut tidak diperiksa oleh kompiler, sehingga kode pihak ke-3 yang dapat mengembalikan null perlu dianotasi ulang agar berfungsi dengan benar.

Saya bisa membayangkan definisi tipe untuk fungsi yang saat ini dianotasi sebagai "mengembalikan string," tetapi terkadang mengembalikan nol. Jika saya bergantung pada fungsi itu dalam kode nonNullableType saya, kompiler tidak mengeluh (bagaimana bisa?) dan kode saya tidak lagi aman untuk nol.

Saya tidak melihat masalah, yakin beberapa file definisi tidak akan valid dengan mode nonNullableType , tetapi sebagian besar waktu perpustakaan yang baik menghindari untuk mengembalikan null atau undefined jadi definisinya akan tetap benar dengan sebagian besar kasus.
Pokoknya saya pribadi jarang dapat memilih definisi PastiTyped tanpa harus memeriksa/memodifikasinya Anda hanya akan memiliki sedikit kerja ekstra untuk menambahkan awalan ? dengan beberapa definisi.

Kecuali saya melewatkan sesuatu, saya rasa ini bukan fungsi yang dapat dihidupkan dan dimatikan dengan bendera. Tampaknya bagi saya bahwa ini adalah perubahan semantik semua-atau-tidak sama sekali untuk memastikan interoperabilitas. Saya akan senang jika terbukti salah, karena saya pikir fitur flag-switched lebih mungkin terjadi.

Saya tidak mengerti mengapa kami tidak dapat memiliki fitur flag-switch, aturannya sederhana:

  • dalam mode normal ? string setara dengan string dan null atau undefined dapat ditetapkan ke semua tipe
  • dalam mode nonNullableType ? string setara dengan string | null | undefined dan null atau undefined tidak dapat dialihkan ke jenis lain selain null atau undefined

Di mana ketidakcocokan dengan fitur flag-switched?

Bendera yang mengubah semantik suatu bahasa adalah hal yang berbahaya. Satu masalah adalah bahwa efeknya berpotensi sangat non-lokal:

function fn(x: string): number;
function fn(x: number|null): string;

function foo() {
    return fn(null);
}

var x = foo(); // x: number or x: string?

Penting bahwa seseorang yang melihat sepotong kode dapat "mengikuti" sistem tipe dan memahami kesimpulan yang dibuat. Jika kita mulai memiliki banyak flag yang mengubah aturan bahasa, ini menjadi tidak mungkin.

Satu-satunya hal yang aman untuk dilakukan adalah menjaga semantik penugasan tetap sama dan mengubah apa yang salah vs apa yang tidak bergantung pada sebuah bendera, seperti cara kerja noImplicitAny hari ini.

Saya tahu itu akan merusak kompatibilitas retro, dan saya mengerti sudut pandang flowtype, itu benar-benar fitur yang sangat berharga, saya harap ini akan menjadi bagian dari TypeScript

Selain komentar RyanCavanaugh -> Dari apa yang saya baca di suatu tempat, spesifikasi/proposal ES7 menyebutkan penggunaan fungsi yang berlebihan (Nama fungsi yang sama tetapi tipe data parameter input berbeda). Itu adalah fitur yang sangat dibutuhkan untuk Javascript.

Dari aliran dokumen :

Flow menganggap null sebagai nilai berbeda yang bukan merupakan bagian dari tipe lainnya

var o = null;
print(o.x); // Error: Property cannot be accessed on possibly null value

Setiap tipe T dapat dibuat untuk memasukkan null (dan nilai terkait tidak terdefinisi) dengan menulis ?T

var o: ?string = null;
print(o.length); // Error: Property cannot be accessed on possibly null or undefined value

[Flow] memahami efek dari beberapa tes tipe dinamis

(yaitu dalam istilah TS memahami jenis penjaga)

var o: ?string = null;
if (o == null) {
  o = 'hello';
}
print(o.length); // Okay, because of the null check

Keterbatasan

  • Pemeriksaan pada properti objek terbatas karena kemungkinan aliasing:

Selain dapat menyesuaikan jenis variabel lokal, Flow terkadang juga dapat menyesuaikan jenis properti objek, terutama jika tidak ada operasi perantara antara pemeriksaan dan penggunaan. Namun, secara umum, aliasing objek membatasi ruang lingkup bentuk penalaran ini, karena pemeriksaan pada properti objek dapat dibatalkan dengan menulis ke properti itu melalui alias, dan sulit bagi analisis statis untuk melacak alias secara tepat.

  • Jenis pemeriksaan gaya penjaga bisa berlebihan untuk properti objek.

[D] jangan berharap bidang nullable dikenali sebagai non-null dalam beberapa metode karena pemeriksaan nol dilakukan di beberapa metode lain dalam kode Anda, bahkan ketika jelas bagi Anda bahwa pemeriksaan nol sudah cukup untuk keamanan di run time (katakanlah, karena Anda tahu bahwa panggilan ke metode sebelumnya selalu mengikuti panggilan ke metode yang terakhir).

  • undefined tidak dicentang.

Nilai yang tidak ditentukan, seperti null, juga dapat menyebabkan masalah. Sayangnya, nilai yang tidak ditentukan ada di mana-mana dalam JavaScript dan sulit untuk menghindarinya tanpa sangat memengaruhi kegunaan bahasa. Misalnya, array dapat memiliki lubang untuk elemen; properti objek dapat ditambahkan dan dihapus secara dinamis. Flow membuat tradeoff dalam kasus ini: ia mendeteksi variabel lokal yang tidak ditentukan dan mengembalikan nilai, tetapi mengabaikan kemungkinan hasil yang tidak ditentukan dari properti objek dan akses elemen array

Bagaimana jika opsi ditambahkan pada saat yang sama saat memperkenalkan tipe null (dan singkatan tanda tanya)? Kehadiran tipe null dalam file akan memaksa kompiler ke mode non-nullable untuk file itu bahkan jika flag tidak ada di baris perintah. Atau apakah itu agak terlalu ajaib?

@jbondc sepertinya bagus. namun masalahnya adalah itu akan berakhir dengan ! mana-mana :p

It's tempting to want to change JavaScript but the reality is a 'string' is nullable or can be undefined.

Apa artinya ini? Tidak ada tipe statis di js. Jadi, ya, string adalah "nullable", tetapi jangan lupa bahwa string juga dapat diberi nomor dan objek dan fooable, dll. Nilai apa pun dapat memiliki tipe apa pun.

Jadi ketika melapisi sistem tipe statis di atas javascript, memilih apakah tipe statis dapat dibatalkan atau tidak hanyalah keputusan desain. Bagi saya tipe yang tidak dapat dibatalkan adalah default yang lebih baik, karena biasanya hanya dalam kasus khusus Anda menginginkan tanda tangan fungsi, misalnya, untuk menerima nilai nol selain tipe yang ditentukan.

Arahan seperti "gunakan ketat" yang menyebabkan perubahan cakupan semantik sudah menjadi bagian dari bahasa; Saya pikir masuk akal untuk memiliki arahan "gunakan tipe yang tidak dapat dibatalkan" di TypeScript.

@metaweta Saya rasa itu tidak cukup, misalnya apa yang terjadi jika _non null module_ mengkonsumsi nullable :

//module A
export function getData(): string[] {
  return null;
}
//module B
'use nonnull'
import A = require('./A');

var data: string[] = A.getData();

data dalam modul B sebenarnya dapat dibatalkan, tetapi karena 'use nonnull' tidak digunakan dalam modul A haruskah kita melaporkan kesalahan ?
Saya tidak melihat cara untuk menyelesaikan masalah itu dengan fitur berbasis arahan.

Ya,

var data: string[] = A.getData();

akan menyebabkan kesalahan. Sebagai gantinya, Anda harus memberikan nilai default ketika getData() mengembalikan null :

var data: string[] = A.getData() || [];

@metaweta ok tapi bagaimana Anda tahu itu kesalahan? :)
jenis getData masih '() => string[]' apakah Anda akan secara otomatis memperlakukan semua yang berasal dari 'nullable module' sebagai 'nullable '?

Ya, persis (kecuali jenis dari modul nullable secara eksplisit ditandai sebaliknya).

Kedengarannya seperti Anda sekarang menginginkan flag per file yang menentukan apakah file itu tidak default ke nullable atau tidak.

Secara pribadi saya pikir agak terlambat untuk memperkenalkan perubahan ini, dan @RyanCavanaugh benar, perubahan itu akan membuat TypeScript kurang dapat diprediksi karena Anda tidak akan dapat menentukan apa yang sedang terjadi hanya dengan melihat file.

Apakah proyek dimulai dengan flag kompiler ini aktif atau nonaktif secara default? Jika seseorang sedang mengerjakan proyek yang tidak dapat dibatalkan secara default dan membuat/beralih ke proyek yang dapat dibatalkan secara default, apakah itu akan menyebabkan kebingungan?
Saat ini saya bekerja dengan No Implicit Any di sebagian besar proyek saya, dan setiap kali saya menemukan proyek yang tidak mengaktifkan opsi itu, saya terkejut.

No impicit any itu bagus, tetapi dalam hal flag yang mengubah cara bahasa berperilaku, saya pikir itu harusnya. Lebih dari itu dan orang-orang yang mengerjakan banyak proyek yang dimulai oleh orang yang berbeda dengan aturan yang berbeda akan kehilangan banyak produktivitas karena asumsi yang salah dan kesalahan.

@RyanCavanaugh prihatin dengan non-lokalitas, dan arahan dicakup secara leksikal. Anda tidak bisa mendapatkan lebih lokal kecuali Anda membubuhi keterangan setiap situs. Saya tidak terlalu mendukung arahan tersebut; Saya baru saja menunjukkan bahwa opsi itu ada dan setidaknya sama masuk akalnya dengan "gunakan ketat" di ES5. Saya pribadi mendukung tipe non-nullable secara default, tetapi praktis, sudah terlambat untuk itu. Mengingat kendala itu, saya lebih suka menggunakan ! bagaimanapun. @jbondc 's proposal memungkinkan Anda membedakan null dari undefined; mengingat bahwa backend Java terus membuat orang menggunakan kedua nilai, tampaknya itu yang paling berguna bagi saya.

Maaf jika saya tidak jelas, saya setuju dengan Ryan dan menambahkan kekhawatiran saya sendiri.

Sejujurnya jika menambahkan use not-null adalah harga untuk menghindari semua pengecualian pointer nol, saya akan membayarnya tanpa masalah, mengingat null atau undefined sebagai dapat dialihkan ke jenis apa pun adalah kesalahan yang lebih buruk TypeScript yang dibuat menurut saya.

@jbondc Saya belum pernah menggunakan 'gunakan ketat' dan karena itu saya membuat beberapa asumsi, mohon perbaiki saya jika asumsi saya salah:

Bukan null tidak memengaruhi sintaks yang ditulis oleh programmer, tetapi kemampuan programmer berikutnya yang mencoba menggunakan kode itu (dengan asumsi bahwa pembuat dan pengguna adalah orang yang terpisah).

Jadi kodenya:

function myfoo (mynumber: number) {
    return !!mynumber;
} 

(mengetik di ponsel jadi mungkin salah)
Apakah kode yang valid dalam proyek normal dan proyek tidak null. Satu-satunya cara pembuat kode mengetahui apakah kode berfungsi atau tidak adalah jika mereka melihat argumen baris perintah.

Di tempat kerja, kami memiliki proyek pengujian (yang mencakup pembuatan prototipe fitur baru) dan proyek utama (dengan kode kami yang sebenarnya). Ketika prototipe siap untuk dipindahkan dari satu proyek ke proyek lainnya (biasanya dengan refraktor besar), tidak akan ada kesalahan dalam kode, tetapi kesalahan dalam penggunaan kode. Perilaku ini berbeda dengan tanpa implisit apa pun dan menggunakan ketat yang keduanya akan segera error.

Sekarang saya memiliki cukup banyak pengaruh dalam proyek-proyek ini, jadi saya dapat memperingatkan orang-orang yang bertanggung jawab untuk tidak menggunakan 'fitur' baru ini karena tidak akan menghemat waktu, tetapi saya tidak memiliki kapasitas itu untuk semua proyek di bekerja.
Jika kami ingin mengaktifkan fitur ini bahkan di satu proyek, maka kami harus mengaktifkannya di semua proyek kami yang lain karena kami memiliki jumlah yang sangat signifikan untuk berbagi kode dan migrasi kode antar proyek, dan 'fitur' ini akan menyebabkan banyak masalah bagi kami. waktu akan kembali dan 'memperbaiki' fungsi yang sudah selesai.

Benar @jbondc. @Griffork : Maaf saya tidak menangkap kesalahpahaman itu; direktif adalah ekspresi string literal yang muncul sebagai baris pertama dari produksi program atau produksi fungsi dan efeknya dicakupkan ke produksi itu.

"use not-null";
// All types in this program production (essentially a single file) are not null

melawan

function f(n: number) {
  "use not-null";
  // n is not null and local variables are not null
  function g(s: string) {
    // s is not null because g is defined in the scope of f
    return s.length;
  }
  return n.toFixed(2);
}

function h(n: number) {
  // n may be null
  if (n) { return n.toFixed(3); }
  else { return null; }
}

Jenis yang tidak dapat dibatalkan tidak berguna. Jenis yang tidak dapat dibatalkan adalah kegunaan. Mereka tidak berguna. Tidak berguna! Anda tidak menyadarinya, tetapi Anda tidak benar-benar membutuhkannya. Tidak ada gunanya membatasi diri Anda dengan menyatakan bahwa mulai sekarang kita tidak akan menggunakan NULL. Bagaimana Anda mewakili nilai yang hilang, misalnya dalam situasi ketika Anda mencoba menemukan substring yang tidak ada? Tidak dapat mengekspresikan nilai yang hilang (apa yang NULL lakukan sekarang untuk saat ini) tidak akan menyelesaikan masalah Anda. Anda akan menukar dunia yang keras dengan NULL di mana-mana untuk dunia yang sama kerasnya tanpa nilai yang hilang sama sekali. Apa yang benar-benar Anda butuhkan disebut tipe data aljabar yang (di antara banyak hal keren lainnya) menampilkan kemampuan untuk mewakili nilai yang hilang (apa yang Anda cari sejak awal dan apa yang diwakili oleh NULL di dunia imperatif). Saya sangat menentang menambahkan non-nullables ke bahasa, karena sepertinya sampah sintaksis/semantik tidak berguna yang merupakan solusi naif dan canggung untuk masalah terkenal. Silakan baca tentang Opsional di F# dan Mungkin di Haskell serta varian (alias serikat pekerja yang ditandai, serikat pekerja yang didiskriminasi) dan pencocokan pola.

@aleksey-bykov Sepertinya Anda tidak menyadari bahwa JavaScript memiliki dua nilai nol, undefined dan null . Nilai null dalam JavaScript hanya dikembalikan pada regexp yang tidak cocok dan saat membuat serial tanggal di JSON. Satu-satunya alasan itu ada dalam bahasa itu adalah untuk interaksi dengan applet Java. Variabel yang telah dideklarasikan tetapi tidak diinisialisasi adalah undefined , bukan null . Properti yang hilang pada objek mengembalikan undefined , bukan null . Jika Anda secara eksplisit ingin undefined menjadi nilai yang valid, maka Anda dapat menguji propName in obj . Jika Anda ingin memeriksa apakah ada properti pada objek itu sendiri daripada apakah properti itu diwariskan, gunakan obj.hasOwnProperty(propName) . Substring yang hilang mengembalikan -1: 'abc'.indexOf('d') === -1 .

Di Haskell, Maybe berguna justru karena tidak ada subtipe universal. Tipe bawah Haskell mewakili non-penghentian, bukan subtipe universal. Saya setuju bahwa tipe data aljabar diperlukan, tetapi jika saya ingin pohon berlabel bilangan bulat, saya ingin setiap simpul memiliki bilangan bulat, bukan null atau undefined . Jika saya menginginkannya, saya akan menggunakan pohon berlabel Maybe int atau ritsleting.

Jika kita mengadopsi direktif "use not-null", saya juga ingin "use not-void" (bukan null atau undefined).

Jika Anda ingin menjamin kode Anda sendiri dari nulls, cukup larang null
literal. Ini jauh lebih mudah daripada mengembangkan tipe non-nullable. Tidak terdefinisi adalah
sedikit lebih rumit, tetapi jika Anda tahu dari mana asalnya
Anda tahu bagaimana menghindarinya. Bawah di Haskell sangat berharga! saya harap
JavaScript (TypeScript) memiliki tipe super global tanpa nilai. aku merindukannya
buruk ketika saya harus mengeluarkan ekspresi. Saya telah menggunakan TypeScript
sejak v 0.8 dan tidak pernah menggunakan null apalagi membutuhkannya. Abaikan saja
mereka seperti yang Anda lakukan dengan fitur bahasa tidak berguna lainnya seperti with
penyataan.

@aleksey-bykov Jika saya sedang menulis perpustakaan dan ingin menjamin bahwa input tidak nol, saya harus melakukan tes runtime untuk itu di mana-mana. Saya ingin tes waktu kompilasi untuk itu, tidak sulit untuk menyediakannya, dan baik Penutupan maupun Aliran memberikan dukungan untuk tipe non-null/tidak terdefinisi.

@metaweta , Anda tidak dapat menjamin diri Anda dari nol. Sebelum kode Anda dikompilasi, ada trilyun cara untuk membuat lib Anda menangis: pleaseNonNullablesNumbersOnly(<any> null) . Setelah dikompilasi ke js tidak ada aturan sama sekali. Kedua, mengapa Anda peduli? Katakan dengan keras dan jelas di muka nulls are not supported, you put a null you will get a crash , seperti penafian, Anda tidak dapat menjamin diri Anda dari semua jenis orang di luar sana, tetapi Anda dapat menguraikan cakupan tanggung jawab Anda. Ketiga, saya hampir tidak bisa memikirkan lib arus utama yang antipeluru untuk apa pun yang dimasukkan pengguna sebagai input, namun masih sangat populer. Jadi, apakah usaha Anda sepadan dengan masalah?

@aleksey-bykov Jika klien perpustakaan saya juga diperiksa jenisnya, maka saya pasti dapat menjamin saya tidak akan mendapatkan nol. Itulah inti dari TypeScript. Dengan alasan Anda, tidak perlu tipe sama sekali: cukup "katakan dengan keras dan jelas" dalam dokumentasi Anda apa tipe yang diharapkan.

Di luar topik, nol sangat berharga bagi kami karena memeriksanya lebih cepat daripada memeriksa yang tidak ditentukan.
Meskipun kami tidak menggunakannya di mana-mana, kami mencoba menggunakannya jika memungkinkan untuk mewakili nilai yang tidak diinisialisasi dan angka yang hilang.

Pada topik:
Kami tidak pernah memiliki masalah dengan nulls 'melarikan diri' ke kode lain, tetapi kami memiliki masalah dengan undefined acak atau NaN yang muncul. Saya percaya bahwa manajemen kode yang cermat lebih baik daripada bendera dalam skenario ini.

Namun, untuk pengetikan pustaka, akan lebih baik jika memiliki tipe null yang berlebihan sehingga kita dapat memilih untuk membubuhi keterangan fungsi yang dapat mengembalikan null (ini tidak boleh diterapkan oleh kompiler, tetapi dengan praktik pengkodean).

@metaweta , menurut alasan saya, klien Anda tidak boleh menggunakan nol dalam basis kode mereka, itu tidak sulit, lakukan pencarian penuh untuk nol (peka huruf besar kecil, seluruh kata) dan hapus semuanya. Terlalu kikuk? Tambahkan sakelar kompiler --noNullLiteral untuk kemewahan. Segala sesuatu yang lain tetap utuh, kode yang sama, jangan khawatir, solusi yang jauh lebih ringan dengan jejak minimal. Kembali ke poin saya, misalkan tipe non-nullable Anda menemukan jalan mereka ke TS dan tersedia dalam 2 rasa berbeda:

  • seseorang dapat menggunakan sintaks ! untuk menunjukkan tipe yang tidak dapat mengambil null, misalnya string! tidak dapat mengambil null
  • sakelar noNullsAllowed aktif

kemudian Anda mendapatkan sepotong json dari server Anda melalui ajax dengan nol di mana-mana, moral: sifat dinamis javascript tidak dapat diperbaiki dengan anotasi jenis di atasnya

@aleksey-bykov Dengan cara yang sama, jika saya mengharapkan objek dengan properti numerik x dan saya mendapatkan {"x":"foo"} dari server, sistem tipe tidak akan dapat mencegahnya . Itu pasti kesalahan runtime dan masalah yang tak terhindarkan saat menggunakan sesuatu selain TypeScript di server.

Namun, jika server ditulis dalam TypeScript dan berjalan pada node, maka server dapat ditranspilasikan dengan adanya file .d.ts untuk kode ujung depan saya dan pemeriksaan jenis akan menjamin bahwa server tidak akan pernah mengirim JSON dengan nol di dalamnya atau objek yang properti x bukan angka.

@metaweta , tipe yang tidak dapat

Ya, kami sangat membutuhkan ini. Lihat Kesalahan miliaran dolar oleh Hoare. Pengecualian penunjuk nol (NPE) adalah kesalahan paling umum yang ditemui dalam bahasa pemrograman yang diketik yang tidak membedakan nullable dari jenis non-nullable. Ini sangat umum sehingga Java 8 menambahkan Optional dalam upaya putus asa untuk melawannya.

Pemodelan nullables dalam sistem tipe bukan hanya masalah teoretis, ini adalah peningkatan besar. Bahkan jika Anda sangat berhati-hati untuk menghindari nol dalam kode Anda, perpustakaan yang Anda gunakan mungkin tidak dan oleh karena itu berguna untuk dapat memodelkan datanya dengan benar dengan sistem tipe.

Sekarang ada serikat pekerja dan penjaga tipe di TS, sistem tipe cukup kuat untuk melakukan ini. Pertanyaannya adalah apakah itu dapat dilakukan dengan cara yang kompatibel ke belakang. Secara pribadi saya merasa bahwa fitur ini cukup penting untuk TypeScript 2.0 agar tidak kompatibel dalam hal ini.

Menerapkan fitur ini dengan benar kemungkinan akan menunjuk ke kode yang sudah rusak daripada merusak kode yang ada: itu hanya akan menunjuk ke fungsi yang membocorkan nol di luarnya (kemungkinan besar tidak sengaja) atau kelas yang tidak menginisialisasi anggotanya dengan benar (bagian ini lebih sulit karena sistem tipe mungkin perlu membuat kelonggaran nilai anggota untuk diinisialisasi dalam konstruktor).

Ini bukan tentang _tidak menggunakan_ null. Ini tentang memodelkan semua jenis yang terlibat dengan benar. Infact fitur ini akan memungkinkan penggunaan null dengan cara yang aman - tidak akan ada alasan untuk menghindarinya lagi! Hasil akhirnya akan sangat mirip dengan pencocokan pola pada tipe aljabar Maybe (kecuali akan dilakukan dengan pemeriksaan if daripada ekspresi huruf besar-kecil)

Dan ini bukan hanya tentang literal nol. null dan undefined secara struktural sama (afaik tidak ada fungsi/operator yang bekerja pada satu tetapi tidak yang lain) oleh karena itu mereka dapat dimodelkan dengan cukup baik dengan satu tipe null di TS.

@metaweta ,

Nilai null dalam JavaScript hanya dikembalikan pada regexp yang tidak cocok dan saat membuat serial tanggal di JSON.

Tidak benar sama sekali.

  • Interaksi dengan DOM menghasilkan null:
  console.log(window.document.getElementById('nonExistentElement')); // null
  • Seperti yang ditunjukkan @aleksey-bykov di atas, operasi ajax dapat mengembalikan nol. Sebenarnya undefined bukan nilai JSON yang valid:
 JSON.parse(undefined); // error
 JSON.parse(null); // okay
 JSON.stringify({ "foo" : undefined}); // "{}"
 JSON.stringify({ "foo" : null}); // '{"foo":null}'

NB: Kita dapat berpura-pura bahwa undefined dikembalikan melalui ajax, karena mengakses properti yang tidak ada akan menghasilkan undefined - itulah sebabnya undefined tidak serial.

Namun, jika server ditulis dalam TypeScript dan berjalan pada node, maka server dapat ditranspilasikan dengan adanya file .d.ts untuk kode ujung depan saya dan pemeriksaan jenis akan menjamin bahwa server tidak akan pernah mengirim JSON dengan nol di dalamnya atau objek yang properti x bukan angka.

Ini tidak sepenuhnya benar. Bahkan jika server ditulis dalam TypeScipt, seseorang sama sekali tidak dapat menjamin null untuk diperkenalkan tanpa memeriksa setiap properti dari setiap objek yang diperoleh dari penyimpanan persisten.

Saya agak setuju dengan @aleksey-bykov tentang ini. Meskipun akan benar-benar brilian jika kita dapat membuat TypeScript mengingatkan kita pada waktu kompilasi tentang kesalahan yang diperkenalkan oleh null dan tidak terdefinisi, saya khawatir itu hanya akan menimbulkan rasa percaya diri yang salah dan akhirnya menangkap hal-hal sepele sementara sumber sebenarnya dari nol tidak terdeteksi.

Bahkan jika server ditulis dalam TypeScipt, seseorang sama sekali tidak dapat menjamin null untuk diperkenalkan tanpa memeriksa setiap properti dari setiap objek yang diperoleh dari penyimpanan persisten.

Ini sebenarnya adalah argumen _for_ tipe non-nullable. Jika penyimpanan Anda dapat mengembalikan null Foo, maka jenis objek yang diambil dari penyimpanan itu adalah Nullable<Foo>, bukan Foo. Jika Anda kemudian memiliki fungsi yang mengembalikan yang dimaksudkan untuk mengembalikan Foo, maka Anda _memiliki_ untuk bertanggung jawab dengan menangani nol (baik Anda melemparkannya karena Anda lebih tahu atau Anda memeriksa nol).

Jika Anda tidak memiliki tipe yang tidak dapat dibatalkan, Anda tidak perlu berpikir untuk memeriksa nol saat mengembalikan objek yang disimpan.

Saya khawatir itu hanya akan menimbulkan rasa percaya diri yang salah dan akhirnya menangkap hal-hal sepele sementara sumber nol yang sebenarnya tidak terdeteksi.

Jenis non-trivia apa yang menurut Anda akan dilewatkan oleh tipe non-nullable?

Ini tidak sepenuhnya benar. Bahkan jika server ditulis dalam TypeScipt, seseorang sama sekali tidak dapat menjamin null untuk diperkenalkan tanpa memeriksa setiap properti dari setiap objek yang diperoleh dari penyimpanan persisten.

Jika penyimpanan persisten mendukung data yang diketik, maka tidak perlu. Tetapi bahkan jika bukan itu masalahnya, Anda hanya akan memeriksa pada titik pengambilan data dan kemudian memiliki jaminan di seluruh _semua_ kode Anda yang lain.

Saya agak setuju dengan @aleksey-bykov tentang ini. Meskipun akan benar-benar brilian jika kita dapat membuat TypeScript mengingatkan kita pada waktu kompilasi tentang kesalahan yang diperkenalkan oleh null dan tidak terdefinisi, saya khawatir itu hanya akan menimbulkan rasa percaya diri yang salah dan akhirnya menangkap hal-hal sepele sementara sumber sebenarnya dari nol tidak terdeteksi.

Menggunakan tipe nullable tidak akan menjadi persyaratan mutlak. Jika Anda merasa tidak perlu memodelkan kasus di mana suatu metode mengembalikan nol karena "tidak signifikan", Anda tidak bisa menggunakan tipe nullable dalam definisi tipe itu (dan mendapatkan ketidakamanan yang sama seperti biasa). Tetapi tidak ada alasan untuk berpikir bahwa pendekatan ini akan gagal - ada contoh bahasa yang telah berhasil mengimplementasikannya (mis. Kotlin oleh JetBrains )

@aleksey-bykov Sejujurnya Anda salah, salah satu hal terbaik tentang tipe non-nullable adalah _kemungkinan untuk mengekspresikan tipe sebagai nullable_.
Dengan strategi Anda untuk tidak pernah menggunakan null untuk mencegah kesalahan penunjuk nol, Anda benar-benar kehilangan kemungkinan menggunakan null karena takut menimbulkan kesalahan, itu benar-benar konyol.

Hal lain tolong dalam diskusi tentang fitur bahasa jangan pergi dengan komentar bodoh seperti:

Jenis yang tidak dapat dibatalkan tidak berguna. Jenis yang tidak dapat dibatalkan adalah kegunaan. Mereka tidak berguna. Tidak berguna! Anda tidak menyadarinya, tetapi Anda tidak benar-benar membutuhkannya.

Itu hanya membuat saya merasa seperti saya harus mengabaikan apa pun yang Anda akan pernah posting di mana saja di web, kami di sini untuk membahas tentang fitur, saya dapat memahami dan dengan senang hati menerima bahwa sudut pandang Anda bukan milik saya, tetapi tidak berperilaku seperti anak.

Saya tidak menentang memperkenalkan anotasi tipe non-null sama sekali. Ini telah terbukti berguna dalam C# dan bahasa lainnya.

OP telah mengubah arah diskusi dengan yang berikut:

Sejujurnya jika menambahkan use not-null adalah harga untuk menghindari semua pengecualian pointer nol, saya akan membayarnya tanpa masalah, mengingat null atau undefined sebagai dapat dialihkan ke jenis apa pun adalah kesalahan terburuk yang dibuat TypeScript menurut saya.

Saya hanya menunjukkan prevalensi null dan undefined di alam liar.

Saya juga harus menambahkan bahwa salah satu hal yang sangat saya hargai tentang TypeScript adalah sikap bahasa yang laissez-faire. Ini telah menghirup udara segar.

Bersikeras bahwa tipe tidak dapat dibatalkan secara default bertentangan dengan inti semangat itu.

Saya telah melihat sejumlah argumen beredar tentang mengapa kita melakukan/tidak membutuhkan ini dan saya ingin melihat apakah saya memahami semua kasus mendasar yang telah dibahas sejauh ini:

1) ingin tahu apakah suatu fungsi dapat mengembalikan null (disebabkan oleh pola eksekusinya, bukan pengetikannya).
2) ingin tahu apakah suatu nilai bisa nol.
3) ingin tahu apakah objek data dapat berisi nilai nol.

Sekarang, hanya ada dua kasus di mana situasi 2 dapat terjadi: Anda menggunakan nol atau jika suatu fungsi mengembalikan nol. Jika Anda menghilangkan semua nol dari kode Anda (dengan asumsi Anda tidak menginginkannya) maka situasi 2 sebenarnya hanya dapat terjadi sebagai akibat dari situasi 1.

Situasi 1 Saya pikir paling baik diselesaikan dengan membubuhi keterangan tipe pengembalian fungsi untuk menunjukkan keberadaan nilai nol. _Ini tidak berarti bahwa Anda memerlukan tipe yang bukan null_. Anda dapat membubuhi keterangan fungsi (misalnya dengan menggunakan jenis serikat pekerja) dan tidak memiliki jenis non null, itu seperti dokumentasi, tapi mungkin lebih jelas dalam kasus ini.

Solusi 2 juga diselesaikan dengan ini.

Ini memungkinkan pemrogram yang bekerja di perusahaan mereka untuk menggunakan proses dan standar untuk menegakkan bahwa tipe null ditandai, dan bukan tim TypeScript (persis dengan cara yang sama bahwa seluruh sistem pengetikan adalah opt-in sehingga tipe nullable eksplisit menjadi pilihan di).

Adapun skenario 3, kontrak antara server dan klien bukan untuk TypeScript untuk ditegakkan, mampu menandai nilai yang terpengaruh sebagai mungkin null mungkin merupakan peningkatan, tetapi pada akhirnya Anda akan mendapatkan garentee yang sama dari TypeScript. perkakas memberi Anda pada setiap nilai lain (artinya, tidak ada kecuali Anda memiliki standar atau praktik pengkodean yang baik!

(posting dari ponsel, maaf jika ada kesalahan)

@fdecampredon , itu bukan ketakutan sejak awal, itu menggunakan null tidak perlu. Saya tidak membutuhkan mereka. Sebagai bonus yang bagus, saya mendapat masalah pengecualian referensi nol dihilangkan. Bagaimana itu semua mungkin? Dengan menggunakan tipe sum dengan kasing kosong. Tipe-jumlah adalah fitur asli dari semua bahasa FP seperti F#, Scala, Haskell dan bersama-sama dengan tipe produk yang disebut tipe data aljabar. Contoh standar tipe jumlah dengan kasing kosong adalah Opsional dari F# dan Mungkin dari Haskell. TypeScript tidak memiliki ADT, tetapi memiliki diskusi yang sedang berlangsung tentang menambahkan non-nullables yang akan memodelkan satu kasus khusus dari apa yang akan dibahas oleh ADT. Jadi pesan saya adalah, singkirkan non-nullables untuk ADT.

@spion , Berita buruk: F# mendapat nol (sebagai warisan .NET). Kabar baik: tidak ada yang menggunakannya. Bagaimana Anda tidak bisa menggunakan null saat itu ada? Mereka memiliki Opsional (seperti Java terbaru seperti yang Anda sebutkan). Jadi, Anda tidak perlu null jika Anda memiliki pilihan yang lebih baik. Inilah yang pada akhirnya saya sarankan: biarkan nulls (non-nulls) saja, lupakan saja mereka ada, dan terapkan ADT sebagai fitur bahasa.

Kami menggunakan null tetapi tidak dengan cara yang sama seperti yang Anda gunakan. Kode sumber perusahaan saya datang dalam 2 bagian.

1) Ketika datang ke data (seperti data database), kami mengganti null dengan data kosong pada saat deklarasi variabel.

2) Semua yang lain, seperti objek yang dapat diprogram, kami menggunakan null sehingga kami tahu ketika ada bug dan celah dalam kode sumber di mana objek tidak dibuat atau dapat dialihkan yang bukan merupakan objek javascript yang diperlukan. Yang tidak terdefinisi adalah masalah objek javascript di mana ada bug atau celah dalam kode sumber.

Data yang tidak ingin kami batalkan karena itu adalah data pelanggan dan mereka akan melihat kata-kata nol.

@aleksey-bykov TypeScript memiliki adt dengan tipe union, satu-satunya hal yang hilang adalah pencocokan pola tetapi itu adalah fitur yang tidak dapat diimplementasikan dengan filosofi TypeScript untuk menghasilkan javascript yang dekat dengan sumber aslinya.

Di sisi lain tidak mungkin untuk menentukan dengan jenis serikat pengecualian nilai nol, itu sebabnya kami membutuhkan jenis non-null.

@fdecampredon , TS tidak memiliki ADT, ia memiliki serikat pekerja yang bukan tipe penjumlahan, karena, seperti yang Anda katakan, 1. mereka tidak dapat memodelkan kotak kosong dengan benar karena tidak ada tipe unit, 2. tidak ada cara yang dapat diandalkan untuk merusak struktur mereka

pencocokan pola untuk ADT dapat diimplementasikan dengan cara yang selaras dengan JavaScript yang dihasilkan, bagaimanapun saya berharap argumen ini bukan titik balik

@aleksey-bykov ini bukan F#. Ini adalah bahasa yang bertujuan untuk memodelkan jenis JavaScript. Pustaka JavaScript menggunakan nilai nol dan tidak terdefinisi. Oleh karena itu, nilai-nilai tersebut harus dimodelkan dengan tipe yang sesuai. Karena bahkan ES6 tidak mendukung tipe data aljabar, tidak masuk akal untuk menggunakan solusi itu mengingat tujuan desain TypeScript

Selain itu, pemrogram JavaScript biasanya menggunakan pemeriksaan if (bersamaan dengan pengujian typeof dan kesetaraan), alih-alih pencocokan pola. Ini sudah dapat mempersempit jenis penyatuan TypeScript. Dari titik ini hanya langkah kecil untuk mendukung jenis non-nullable dengan manfaat yang sebanding dengan aljabar Mungkin dll.

Saya terkejut bahwa tidak ada yang benar-benar menyebutkan perubahan besar yang mungkin perlu diperkenalkan oleh lib.d.ts dan masalah potensial dari status nol sementara bidang kelas selama pemanggilan konstruktor. Itu adalah beberapa masalah potensial aktual dan nyata untuk mengimplementasikan tipe yang tidak dapat dibatalkan ...

@Griffork idenya adalah untuk menghindari pemeriksaan nol di mana-mana dalam kode Anda. Katakanlah Anda memiliki fungsi berikut:

declare function getName(personId:number):string|null;

Idenya adalah Anda memeriksa apakah namanya null hanya sekali , dan mengeksekusi semua kode lainnya bebas dari kekhawatiran bahwa Anda harus menambahkan cek nol.

function doSomethingWithPersonsName(personId:number) {
  var name = getName(personId);
  if (name != null) return doThingsWith(name); // type guard narrows string|null to just string
  else { return handleNullCase(); }
}

Dan sekarang kamu hebat! Sistem tipe menjamin bahwa doThingsWith akan dipanggil dengan nama yang bukan null

function doThingsWith(name:string) {
  // Lets create some funny versions of the name
  return [uppercasedName(name), fullyLowercased(name), funnyCased(name)]
}

Tak satu pun dari fungsi-fungsi ini perlu memeriksa nol, dan kode akan tetap berfungsi tanpa melempar. Dan, segera setelah Anda mencoba meneruskan string yang dapat dibatalkan ke salah satu fungsi ini, sistem tipe akan segera memberi tahu Anda bahwa Anda telah membuat kesalahan:

function justUppercased(personId:number) {
  var name = getName(personId);
  return uppercasedName(name); // error, passing nullable to a function that doesn't check for nulls.
}

Ini adalah keuntungan besar: sekarang sistem tipe melacak apakah fungsi dapat menangani nilai nullable atau tidak, dan lebih jauh lagi, melacak apakah mereka benar-benar perlu atau tidak. Kode yang jauh lebih bersih, lebih sedikit pemeriksaan, lebih aman. Dan ini bukan hanya tentang string - dengan pustaka seperti runtime-type-checks, Anda juga dapat membuat pelindung tipe untuk data yang jauh lebih kompleks

Dan jika Anda tidak menyukai pelacakan karena Anda merasa tidak layak memodelkan kemungkinan nilai nol, Anda dapat kembali ke perilaku lama yang tidak aman:

declare function getName(personId:number):string;

dan dalam kasus tersebut, TypeScript hanya akan memperingatkan Anda jika Anda melakukan sesuatu yang jelas-jelas salah

uppercasedName(null);

Terus terang saya tidak melihat kerugian, kecuali untuk kompatibilitas ke belakang.

@fdecampredon Jenis serikat hanya itu, serikat pekerja. Mereka bukan _disjoint_ union alias penjumlahan. Lihat #186.

@aleksey-bykov

Perhatikan bahwa menambahkan jenis opsi masih akan menjadi perubahan besar.

// lib.d.ts
interface Document {
    getElementById(id: string): Maybe<Element>;
}

...

// Code that worked with 1.3
var myCanvas = <HTMLCanvasElement>document.getElementById("myCanvas");
// ... now throws the error that Maybe<Element> can't be cast to an <HTMLCanvasElement>

Lagi pula, Anda bisa mendapatkan tipe opsi orang miskin sekarang dengan destrukturisasi

class Option<T> {
    hasValue: boolean;
    value: T;
}

var { hasValue, myCanvas: value } = <Option<HTMLCanvasElement>> $("myCanvas");
if (!hasValue) {
    throw new Error("Canvas not found");
}
// Use myCanvas here

tetapi satu-satunya nilai dari ini adalah jika lib.d.ts (dan .d.ts lainnya, dan seluruh basis kode Anda tetapi kami akan menganggap kami dapat memperbaikinya) juga menggunakannya, jika tidak, Anda kembali tidak mengetahui apakah a fungsi yang tidak menggunakan Option dapat mengembalikan null atau tidak kecuali Anda melihat kodenya.

Perhatikan bahwa saya juga tidak mendukung tipe yang non-null secara default (bukan untuk TS 1.x). Ini adalah perubahan yang terlalu besar.

Tapi katakanlah kita sedang berbicara tentang 2.0. Jika kita tetap akan memiliki perubahan yang melanggar (menambahkan tipe opsi), mengapa tidak membuat tipe non-nullable secara default juga? Membuat tipe non-nullable secara default dan menambahkan tipe opsi tidak eksklusif. Yang terakhir dapat berdiri sendiri (mis. di F# seperti yang Anda tunjukkan) tetapi yang pertama membutuhkan yang terakhir.

@Arnavion , ada beberapa kesalahpahaman, saya tidak mengatakan kita perlu mengganti tanda tangan, semua tanda tangan yang ada tetap utuh, itu untuk perkembangan baru Anda bebas menggunakan ADT atau apa pun yang Anda inginkan. Jadi tidak ada perubahan yang melanggar. Tidak ada yang dibuat non-null secara default.

Jika ATD ada di sini, terserah pengembang untuk membungkus semua tempat di mana null dapat bocor ke dalam aplikasi dengan mengubahnya menjadi opsional. Ini bisa menjadi ide untuk proyek mandiri.

@aleksey-bykov

Saya tidak mengatakan kita perlu mengganti tanda tangan, semua tanda tangan yang ada tetap utuh

_I_ mengatakan bahwa tanda tangan perlu diganti, dan saya sudah memberikan alasannya:

tetapi satu-satunya nilai dari ini adalah jika lib.d.ts (dan .d.ts lainnya, dan seluruh basis kode Anda tetapi kami akan menganggap kami dapat memperbaikinya) juga menggunakannya, jika tidak, Anda kembali tidak mengetahui apakah a fungsi yang tidak menggunakan Option dapat mengembalikan null atau tidak kecuali Anda melihat kodenya.


Tidak ada yang dibuat non-null secara default. Pernah.

Untuk TS 1.x, saya setuju, hanya karena terlalu besar perubahannya. Untuk 2.0, menggunakan tipe opsi dalam tanda tangan default (lib.d.ts dll.) sudah merupakan perubahan yang melanggar. Membuat tipe non-nullable secara default _selain itu_ menjadi sepadan dan tidak membawa kerugian.

Saya tidak setuju, memperkenalkan opsional tidak boleh merusak apa pun, itu tidak seperti kami menggunakan opsional atau nullables atau non-nullables. Semua orang menggunakan apa pun yang mereka inginkan. Cara lama dalam melakukan sesuatu seharusnya tidak bergantung pada fitur baru. Terserah pengembang untuk menggunakan alat yang sesuai untuk kebutuhan mendesaknya.

Jadi Anda mengatakan bahwa jika saya memiliki fungsi foo yang mengembalikan Option<number> dan bilah fungsi yang mengembalikan angka, saya tidak boleh yakin bahwa bilah tidak dapat mengembalikan nol kecuali saya melihat implementasi bilah atau memelihara dokumentasi "Fungsi ini tidak pernah mengembalikan null."? Tidakkah menurut Anda ini menghukum fungsi yang tidak akan pernah mengembalikan nol?

function bar dari contoh Anda dikenal sebagai nullable sejak awal, itu digunakan di sekitar 100.500 aplikasi di sekitar dan semua orang memperlakukan hasilnya sebagai nullable, sekarang Anda datang dan melihat ke dalamnya dan menemukan bahwa null tidak mungkin, apakah itu berarti Anda harus melanjutkan dan mengubah tanda tangan dari nullable menjadi non-nullable? Saya pikir Anda tidak harus. Karena pengetahuan ini, meskipun berharga, tidak layak untuk mengerem 100.500 aplikasi. Apa yang harus Anda lakukan adalah membuat lib baru dengan tanda tangan yang direvisi yang melakukannya seperti ini:

old_lib.d.ts

...
declare function bar(): number; // looks like can return a potentially nullable number
...

direvisi_lib.d.ts

declare function bar(): !number; // now thank to the knowledge we are 100% certain it cannot return null

sekarang aplikasi lama tetap menggunakan old_lib.d.ts , untuk aplikasi baru pengembang bebas memilih revised_libs.d.ts

Sayangnya revisi_libs.d.ts memiliki 50 fungsi lain yang belum saya lihat, semuanya mengembalikan angka (tapi saya tidak tahu apakah itu benar-benar angka atau angka nullable). Apa sekarang?

Baiklah, luangkan waktu Anda, minta bantuan, gunakan versi (tergantung pada tingkat pengetahuan yang Anda peroleh sejauh ini, Anda mungkin ingin merilisnya secara bertahap dengan nomor versi yang semakin meningkat: revised_lib.v-0.1.12.d.ts )

Itu tidak perlu sebenarnya. Fungsi yang mengembalikan tipe nullable dalam tanda tangan tetapi tipe non-nullable dalam implementasi hanya menghasilkan pemeriksaan kesalahan yang berlebihan oleh pemanggil. Itu tidak membahayakan keselamatan. Secara bertahap membuat anotasi lebih banyak fungsi dengan ! saat Anda menemukan mereka akan bekerja dengan baik, seperti yang Anda katakan.

Saya bukan penggemar! hanya karena lebih banyak bagasi untuk mengetik (baik dari segi penekanan tombol dan perlu ingat untuk menggunakannya). Jika kita ingin non-nullability di 1.x maka ! adalah salah satu opsi yang sudah dibahas di atas, tetapi saya masih akan mengatakan bahwa memiliki perubahan akhirnya dengan 2.0 dan menjadikan non-nullability sebagai default tidak sia-sia.

Di sisi lain, mungkin itu akan mengarah ke situasi Python 2/3-esque, di mana tidak ada yang memutakhirkan ke TS 2.0 selama bertahun-tahun karena mereka tidak mampu melalui basis kode jutaan baris memastikan bahwa setiap deklarasi variabel dan kelas anggota dan parameter fungsi dan... dijelaskan dengan ? jika bisa nol. Bahkan 2to3 (alat migrasi Python 2 hingga 3) tidak harus berurusan dengan perubahan luas seperti itu.

Apakah 2.0 mampu menjadi perubahan besar tergantung pada tim TS. Saya akan memilih ya, tetapi kemudian saya tidak memiliki basis kode sejuta baris yang perlu diperbaiki untuk itu, jadi mungkin suara saya tidak dihitung.

Mungkin kita harus bertanya kepada orang-orang Funscript bagaimana mereka merekonsiliasi DOM API yang mengembalikan nol dengan F# (Funscript menggunakan lib.d.ts TypeScript dan .d.ts lainnya untuk digunakan dari kode F#). Saya tidak pernah menggunakannya, tetapi melihat http://funscript.info/samples/canvas/index.html misalnya sepertinya penyedia tipe tidak berpikir bahwa document.getElementsByTagName("canvas")[0] bisa menjadi tidak terdefinisi.

Sunting: Di sini sepertinya document.getElementById() tidak diharapkan untuk mengembalikan nol. Setidaknya tampaknya tidak mengembalikan Option<Element> melihatnya mengakses .onlick pada hasilnya.

@spion
Terima kasih, saya tidak memikirkan itu.

Di tempat kerja, basis kode kami tidak kecil, dan perubahan besar yang diinginkan orang ini akan membuat kami mundur banyak waktu dengan keuntungan yang sangat kecil. Melalui standar yang baik dan komunikasi yang jelas antara pengembang kami, kami tidak memiliki masalah dengan nol yang muncul di tempat yang seharusnya tidak.
Sejujurnya saya terkejut bahwa beberapa orang mendorongnya dengan sangat buruk.
Tak perlu dikatakan ini akan memberi kita sedikit manfaat dan akan menghabiskan banyak waktu.

@Griffork telah melihat daftar masalah yang difilter ini dihasilkan oleh perubahan ini. Semua bug "crash" yang tercantum di tautan itu dapat dihindari dengan menggunakan tipe yang tidak dapat dibatalkan. Dan kita berbicara tentang tingkat standar, komunikasi, dan tinjauan kode Microsoft yang mengagumkan di sini.

Mengenai kerusakan, saya pikir jika Anda terus menggunakan definisi tipe yang ada, kemungkinan Anda tidak akan mendapatkan kesalahan sama sekali, kecuali kompiler menunjukkan variabel dan bidang yang berpotensi tidak diinisialisasi dan bocor. Di sisi lain, Anda mungkin mendapatkan banyak kesalahan, terutama misalnya jika bidang kelas sering dibiarkan tidak diinisialisasi dalam konstruktor dalam kode Anda (akan diinisialisasi nanti). Oleh karena itu saya memahami kekhawatiran Anda, dan saya tidak mendorong perubahan yang tidak kompatibel ke belakang untuk TS 1.x. Saya masih berharap bahwa saya telah berhasil meyakinkan Anda bahwa jika ada perubahan pada bahasa yang layak untuk mematahkan kompatibilitas ke belakang, ini adalah yang satu ini.

Bagaimanapun, Alur Facebook memang memiliki tipe yang tidak dapat dibatalkan. Setelah lebih dewasa, mungkin perlu diselidiki sebagai pengganti TS bagi kita yang peduli dengan masalah ini.

@spion , satu-satunya nomor yang diberikan daftar Anda kepada kami adalah berapa kali null atau undefined disebutkan karena alasan apa pun di luar sana, pada dasarnya itu hanya mengatakan bahwa null dan undefined telah dibicarakan, saya harap jelas bahwa Anda tidak dapat menggunakannya sebagai argumen

@aleksey-bykov Saya tidak - saya melihat _semua_ masalah pada daftar yang difilter itu dan setiap yang memiliki kata "crash" di dalamnya terkait dengan jejak tumpukan yang menunjukkan bahwa suatu fungsi mencoba mengakses properti atau metode dari nilai yang tidak ditentukan atau nol.

Saya mencoba mempersempit filter dengan kata kunci yang berbeda dan saya (menurut saya) berhasil mendapatkan semuanya

@spion
Pertanyaan: berapa banyak dari kesalahan itu yang disebabkan di lokasi yang mereka perlukan untuk menandai variabel sebagai nullable atau undefinable?

EG Jika sebuah objek dapat memiliki parent, dan Anda menginisialisasi parent ke null, dan Anda akan selalu memiliki satu objek dengan parent yang null, Anda masih harus mendeklarasikan parent sebagai kemungkinan null.
Masalahnya di sini adalah jika seorang programmer menulis beberapa kode dengan asumsi bahwa sebuah loop akan selalu putus sebelum mencapai null parent. Itu bukan masalah dengan null berada dalam bahasa, hal yang sama persis akan terjadi dengan undefined .

Alasan untuk menjaga agar null mudah digunakan seperti sekarang:
• Ini adalah nilai default yang lebih baik daripada yang tidak ditentukan.
. 1) lebih cepat untuk memeriksa dalam beberapa kasus (kode kami harus sangat berkinerja)
. 2) itu membuat loop in bekerja lebih dapat diprediksi pada objek.
. 3) itu membuat penggunaan array lebih masuk akal saat menggunakan nol untuk nilai kosong (sebagai lawan dari nilai yang hilang). Perhatikan bahwa delete array[i] dan array[i] = undefined memiliki perilaku yang berbeda saat menggunakan indexOf (dan mungkin metode array populer lainnya).

Apa yang saya rasakan sebagai hasil dari membuat nol memerlukan mark up tambahan untuk digunakan dalam bahasa:
• Saya mendapatkan kesalahan yang tidak ditentukan alih-alih kesalahan nol (yang akan terjadi di sebagian besar skenario TypeScript).

Ketika saya mengatakan kami tidak memiliki masalah dengan pelarian nol, maksud saya bahwa variabel yang tidak diinisialisasi ke nol tidak pernah menjadi nol, kami masih mendapatkan kesalahan pengecualian nol di tempat yang persis sama dengan kami akan mendapatkan kesalahan yang tidak ditentukan (seperti halnya tim TypeScript ). Membuat null lebih sulit untuk digunakan (dengan memerlukan sintaks tambahan) dan membiarkannya tidak terdefinisi sebenarnya akan menyebabkan lebih banyak masalah bagi beberapa pengembang (misalnya kami).

Menambahkan sintaks tambahan untuk menggunakan null berarti bahwa selama beberapa minggu/bulan pengembang yang menggunakan banyak null akan membuat kesalahan kiri, kanan dan tengah saat mereka mencoba mengingat sintaks baru. Dan itu akan menjadi cara lain untuk tergelincir di masa depan (dengan memberi anotasi sesuatu yang sedikit salah). [Waktunya menunjukkan bahwa saya benci ide menggunakan simbol untuk mewakili tipe, itu membuat bahasanya kurang jelas]

Sampai Anda dapat menjelaskan kepada saya situasi di mana null menyebabkan kesalahan atau masalah yang tidak akan terdefinisi, maka saya tidak akan setuju dengan membuat null secara signifikan lebih sulit untuk digunakan daripada tidak terdefinisi. Ini memiliki kasus penggunaannya, dan hanya karena itu tidak membantu Anda , tidak berarti bahwa perubahan besar yang Anda inginkan (yang _akan_ melukai alur kerja pengembang lain) harus dilanjutkan.

Kesimpulan:
Tidak ada gunanya mendeklarasikan tipe non-nullable tanpa bisa mendefinisikan tipe non-undefined. Dan tipe yang tidak terdefinisi tidak dimungkinkan karena cara kerja javascript.

@Griffork ketika saya mengatakan non-nullable, maksud saya non-nullable-or-undefined. Dan tidak benar bahwa itu tidak mungkin karena cara kerja JS. Dengan fitur pelindung tipe baru, setelah Anda menggunakan pelindung tipe, Anda tahu bahwa nilai yang mengalir dari sana tidak dapat menjadi nol atau tidak terdefinisi lagi. Ada tradeoff yang terlibat di sana juga, dan saya menyerahkan implementasi Facebook Flow sebagai bukti bahwa itu cukup bisa dilakukan.

Kasus _only_ yang akan menjadi sedikit lebih sulit untuk digunakan adalah yang ini: Saya akan menetapkan sementara nol ke variabel ini kemudian saya akan menggunakannya dalam metode lain ini seolah-olah itu bukan nol, tetapi saya tahu bahwa saya tidak akan pernah memanggil metode lain ini sebelumnya menginisialisasi variabel terlebih dahulu, jadi tidak perlu memeriksa nol. Ini adalah kode yang sangat rapuh, dan saya pasti akan menyambut kompiler yang memperingatkan saya tentang hal itu: ini adalah refactor yang jauh dari bug.

@spion
Saya percaya bahwa saya akhirnya mengerti dari mana Anda berasal.

Saya percaya bahwa Anda menginginkan penjaga tipe untuk membantu menentukan kapan suatu nilai tidak boleh nol, dan memungkinkan Anda memanggil fungsi yang tidak memeriksa nol di dalamnya. Dan jika pernyataan if penjaga dihapus, maka itu menjadi kesalahan.

Saya bisa melihat itu berguna.

Saya juga percaya bahwa ini tidak akan menjadi peluru perak yang Anda harapkan.

Kompiler yang tidak menjalankan kode Anda dan menguji setiap aspeknya tidak akan lebih baik dalam menentukan di mana undefined/nulls daripada programmer yang menulis kode. Saya khawatir bahwa perubahan ini akan meninabobokan orang ke dalam rasa aman yang salah, dan sebenarnya membuat kesalahan nol/tidak terdefinisi lebih sulit untuk dilacak ketika itu benar-benar terjadi.
Sungguh, saya pikir solusi yang Anda butuhkan adalah seperangkat alat pengujian yang mendukung TypeScript, yang dapat mereproduksi bug semacam ini di Javascript, daripada mengimplementasikan tipe dalam kompiler yang tidak dapat memenuhi janjinya.

Anda menyebutkan Flow memiliki solusi untuk masalah ini, tetapi ketika membaca tautan Anda, saya melihat beberapa hal yang mengkhawatirkan:

"Alur terkadang juga dapat menyesuaikan jenis properti objek, terutama ketika tidak ada operasi perantara antara pemeriksaan dan penggunaan. Namun, secara umum, aliasing objek membatasi ruang lingkup bentuk penalaran ini , karena pemeriksaan pada properti objek dapat menjadi tidak valid dengan menulis ke properti itu melalui alias, dan sulit bagi analisis statis untuk melacak alias dengan tepat ."

"Nilai yang tidak ditentukan, sama seperti null, dapat menyebabkan masalah juga. Sayangnya, nilai yang tidak ditentukan ada di mana-mana dalam JavaScript dan sulit untuk menghindarinya tanpa mempengaruhi kegunaan bahasa[...] Flow membuat tradeoff dalam kasus ini: itu mendeteksi variabel lokal yang tidak ditentukan dan mengembalikan nilai, tetapi mengabaikan kemungkinan hasil yang tidak ditentukan dari properti objek dan akses elemen array ."

Sekarang undefined dan null bekerja secara berbeda, kesalahan tidak terdefinisi masih dapat muncul di mana-mana, tanda tanya tidak dapat menjamin bahwa nilainya bukan nol, dan bahasa berperilaku lebih berbeda dengan Javascript (yang coba dihindari TS dari apa yang saya lihat ).

ps

foo(thing: whatiknowaboutmyobject) {
    if (thing.hidden) {
        delete thing.description;
    }
}

if (typeof thing.description === "string") {
    //thing.description is non-nullable now, right?
    foo(thing);
    //What is thing.description?
    console.log(thing.description.length);
}

TS sudah rentan terhadap efek aliasing (seperti bahasa apa pun yang memungkinkan nilai yang dapat diubah). Ini akan dikompilasi tanpa kesalahan:

function foo(obj: { bar: string|number }) {
    obj.bar = 5;
}

var baz: { bar: string } = { bar: "5" };

foo(baz);

console.log(baz.bar.charAt(0)); // Runtime error - Number doesn't have a charAt method

Dokumen Flow menyatakan ini hanya untuk kelengkapan.

Sunting: Contoh yang lebih baik.

Tua:

Ya, tetapi saya berpendapat bahwa cara menetapkan sesuatu ke tidak terdefinisi jauh lebih besar daripada cara menetapkan sesuatu ke nilai lain.

Maksudku,

mything.mystring = 5; // is clearly wrong.
delete mything.mystring; //is not clearly wrong - this is not quite the equivalent of setting mystring to >undefined.

Sunting:
Meh, pada titik ini cukup banyak preferensi pribadi. Setelah menggunakan javascript untuk waktu yang lama, saya tidak berpikir saran ini akan membantu bahasa. Saya pikir itu akan membuai orang ke dalam rasa aman yang salah, dan saya pikir itu akan mendorong TypeScript (sebagai bahasa) menjauh dari Javascript.

@Griffork Untuk contoh bagaimana

var mything = {mystring: "5"}; 
delete mything.mystring;
console.log(mything.mystring.charAt(1));

Omong-omong, operator hapus dapat diperlakukan dengan cara yang sama seperti menetapkan nilai bertipe null dan itu akan cukup untuk menutupi kasus Anda juga.

Klaim bahwa bahasa akan berperilaku berbeda dari JavaScript adalah benar, tetapi tidak berarti. TypeScript sudah memiliki perilaku yang berbeda dari JavaScript. Inti dari sistem tipe selalu melarang program yang tidak masuk akal. Pemodelan tipe non-nullable hanya menambahkan beberapa batasan tambahan. Melarang penetapan nilai null atau tidak terdefinisi ke variabel tipe non-nullable persis sama dengan melarang penetapan angka ke variabel tipe string. JS mengizinkan keduanya, TS tidak mengizinkan keduanya

@spion

idenya adalah untuk menghindari cek nol di mana-mana dalam kode Anda

Jika saya mengerti apa yang Anda anjurkan:

A. Jadikan semua tipe non-null secara default.
B. Tandai bidang dan variabel yang dapat dibatalkan.
C. Pastikan pengembang aplikasi/pustaka memeriksa semua titik masuk ke dalam aplikasi.

Tetapi bukankah itu berarti tanggung jawab untuk memastikan kode seseorang bebas dari nol ada pada orang yang menulis kode, dan bukan pada kompiler? Kami secara efektif memberi tahu kompiler "dw, saya tidak membiarkan nol apa pun ke dalam sistem."

Alternatifnya adalah mengatakan, nol ada di mana-mana, jadi jangan repot-repot, tetapi jika ada sesuatu yang tidak dapat dibatalkan maka saya akan memberi tahu Anda.

Faktanya adalah pendekatan yang terakhir rentan terhadap pengecualian referensi nol, tetapi lebih jujur. Berpura-pura bahwa bidang pada objek yang diperoleh melalui kawat (yaitu ajax) tidak nol berarti memiliki iman kepada Tuhan :smiley: .

Saya percaya ada ketidaksepakatan yang kuat tentang masalah ini karena, tergantung pada apa yang sedang dikerjakan, item C atas bisa jadi sepele atau tidak layak.

@jbondc Saya senang Anda menanyakan itu. Memang CallExpression ditandai sebagai tidak terdefinisi atau tidak dapat dibatalkan. Namun, sistem tipe saat ini tidak mengambil keuntungan dari itu - masih memungkinkan semua operasi pada typeArguments seolah-olah tidak null atau undefined.

Namun, saat menggunakan tipe gabungan baru dalam kombinasi dengan tipe yang tidak dapat dibatalkan, tipe tersebut dapat dinyatakan sebagai NodeArray<TypeNode>|null . Kemudian sistem tipe tidak akan mengizinkan operasi apa pun di bidang itu kecuali jika cek nol diterapkan:

if (ce.typeArguments != null) {
  callSomethingOn(ce.typeArguments)
}

// callSomethingOn doesn't need to perform any checks

function callSomethingOn(na:NodeArray<TypeNode>) {
...
}

Dengan bantuan pelindung tipe TS 1.4, di dalam blok if, tipe ekspresi akan dipersempit menjadi NodeArray<TypeNode> yang pada gilirannya akan mengizinkan semua operasi NodeArray pada tipe itu; selain itu, semua fungsi yang dipanggil dalam pemeriksaan itu akan dapat menentukan tipe argumennya menjadi NodeArray<TypeNode> tanpa melakukan pemeriksaan lagi, selamanya.

Tetapi jika Anda mencoba untuk menulis

function someOtherFunction(ce: CallExpression) {
  callSomethingOn(ce.typeArguments)
}

kompiler akan memperingatkan Anda tentang hal itu pada waktu kompilasi, dan bug itu tidak akan terjadi.

Jadi tidak, @NoelAbrahams , ini bukan tentang mengetahui segalanya dengan pasti. Ini tentang kompiler yang membantu Anda memberi tahu nilai apa yang dapat dikandung oleh variabel atau bidang, sama seperti semua tipe lainnya.

Tentu saja, dengan nilai eksternal, seperti biasa, terserah Anda untuk menentukan jenisnya. Anda selalu dapat mengatakan bahwa data eksternal berisi string alih-alih angka, dan kompiler tidak akan mengeluh namun program Anda akan macet ketika mencoba melakukan operasi string pada nomor tersebut.

Tetapi tanpa tipe yang tidak dapat dibatalkan, Anda bahkan tidak memiliki kemampuan untuk mengatakan bahwa suatu nilai tidak boleh nol. Nilai null dapat ditetapkan untuk setiap jenis dan Anda tidak dapat membuat batasan apa pun tentangnya. Variabel dapat dibiarkan tidak diinisialisasi dan Anda tidak akan mendapatkan peringatan apa pun karena undefined adalah nilai yang valid untuk jenis apa pun. Oleh karena itu, kompiler tidak dapat membantu Anda menangkap kesalahan terkait null dan tidak terdefinisi pada waktu kompilasi.

Saya merasa terkejut bahwa ada begitu banyak kesalahpahaman tentang tipe yang tidak dapat dibatalkan. Itu hanya tipe yang tidak dapat dibiarkan tidak diinisialisasi atau tidak dapat diberi nilai null atau undefined . Ini tidak berbeda dengan tidak dapat menetapkan string ke nomor. Ini adalah posting terakhir saya tentang masalah ini, karena saya merasa bahwa saya tidak benar-benar mendapatkan apa-apa. Jika ada yang tertarik untuk mengetahui lebih lanjut, saya sarankan memulai dengan video "Kesalahan miliaran dolar" yang saya sebutkan di atas. Masalah ini sudah diketahui dengan baik dan berhasil ditangani oleh banyak bahasa dan kompiler modern.

@spion , saya sepenuhnya setuju tentang semua manfaat untuk dapat menyatakan apakah suatu tipe dapat atau tidak dapat menjadi nol. Tetapi pertanyaannya adalah apakah Anda ingin tipe menjadi non-null _secara default _?

Berpura-pura bahwa bidang pada objek yang diperoleh melalui kawat (yaitu ajax) tidak nol berarti memiliki iman kepada Tuhan

Jadi jangan berpura-pura. Tandai sebagai nullable dan Anda akan dipaksa untuk mengujinya sebelum menggunakannya sebagai non-nullable.

Tentu. Ini bermuara pada apakah kita ingin menandai bidang sebagai nullable (dalam sistem di mana bidang non-null secara default) atau apakah kita ingin menandai bidang sebagai non-nullable (dalam sistem di mana bidang nullable secara default).

Argumennya adalah bahwa yang pertama adalah perubahan yang melanggar (yang mungkin atau mungkin tidak signifikan) dan juga tidak dapat dipertahankan karena mengharuskan pengembang aplikasi untuk memeriksa dan menjamin semua titik masuk.

@NoelAbrahams Saya tidak mengerti mengapa itu 'dapat dipertahankan' pada dasarnya sebagian besar waktu Anda tidak ingin nol, juga ketika titik masuk dapat mengembalikan nol, Anda harus memeriksanya , pada akhirnya sistem tipe dengan tipe non-null sebagai default akan memungkinkan Anda untuk membuat lebih sedikit pemeriksaan nol karena Anda akan dapat mempercayai beberapa titik api/perpustakaan/entri di aplikasi Anda.

Ketika Anda berpikir sedikit tentang menandai tipe sebagai non null dalam sistem tipe nullable memiliki nilai terbatas, Anda masih dapat menggunakan variabel yang diketik/tipe pengembalian nullable tanpa dipaksa untuk mengujinya.
Ini juga akan memaksa penulis definisi untuk menulis lebih banyak kode, karena sebagian besar waktu perpustakaan yang dirancang dengan baik tidak pernah mengembalikan nilai nol atau tidak terdefinisi.
Akhirnya bahkan konsepnya aneh, dalam sistem tipe dengan tipe yang tidak dapat dibatalkan, tipe yang dapat dibatalkan dapat diekspresikan dengan sempurna sebagai tipe gabungan: ?string setara dengan string | null | undefined . Dalam sistem tipe dengan tipe nullable sebagai default di mana Anda dapat menandai tipe sebagai tidak dapat dibatalkan, bagaimana Anda mengekspresikan !string ? string - null - undefined ?

Pada akhirnya saya tidak begitu mengerti kekhawatiran orang-orang di sini, null bukan string, dengan cara yang sama dari 5 bukan string, kedua nilai tidak akan dapat digunakan di mana string diharapkan, dan membiarkan slip var myString: string = null sama rawannya dengan kesalahan: var myString: string = 5 .
Memiliki nol atau tidak terdefinisi yang dapat ditetapkan untuk jenis apa pun mungkin merupakan konsep yang akrab dengan pengembang, tetapi masih merupakan konsep yang buruk.

Saya rasa saya tidak sepenuhnya benar dalam posting saya sebelumnya: Saya akan menyalahkannya pada saat itu.

Saya baru saja melihat beberapa kode kami untuk melihat bagaimana hal-hal akan bekerja dan itu pasti akan membantu untuk menandai bidang tertentu sebagai nullable, misalnya:

interface Foo {
        name: string;
        address: string|null; /* Nullable */
}

var foo:Foo = new FooClass();
foo.name.toString(); // Okay
foo.address.toString(); // Error: do not use without null check

Tapi yang saya keberatan adalah sebagai berikut:

foo.name = undefined; // Error: non-nullable

Saya merasa ini akan mengganggu cara alami bekerja dengan JavaScript.

Hal yang sama dapat diterapkan dengan nomor :

interface Foo {
        name: string;
        address: string|number; 
}
var foo:Foo = new FooClass();
foo.name.toString(); // Okay
foo.address.slice() // error

foo.name  = 5 // error

Dan itu masih valid di JavaScript

Cukup berapa kali Anda rela menetapkan null ke properti suatu objek ?

Saya pikir sebagian besar hal akan ditandai sebagai nol tetapi Anda akan mengandalkan penjaga tipe untuk menyatakan bahwa bidang tersebut sekarang tidak dapat dibatalkan.

@fdecampredon
Cukup banyak sebenarnya.

@Griffork ,

Saya pikir sebagian besar hal akan ditandai sebagai nol

Itu pemikiran awal saya. Tetapi setelah melalui beberapa bagian dari kode kami, saya menemukan komentar seperti berikut:

interface MyType {

     name: string;

     /** The date the entry was updated from Wikipedia or undefined for user-submitted content. */
     wikiDate: Date; /* Nullable */
}

Gagasan bahwa bidang adalah nullable sering digunakan untuk memberikan informasi. Dan TypeScript akan menangkap kesalahan jika memerlukan penjaga tipe saat mengakses wikiDate .

@fdecampredon

foo.name = 5 // error
Dan itu masih valid di JavaScript

Benar, tetapi itu adalah kesalahan karena TypeScript tahu dengan pasti 100% bahwa itu tidak disengaja.

sedangkan

foo.name = tidak terdefinisi; // Jangan kirim nama ke server

benar-benar disengaja.

Saya pikir implementasi yang paling sesuai dengan persyaratan kami adalah tidak menggunakan tipe serikat pekerja, tetapi ikuti saran aslinya:

 wikiDate: ?Date;

Saya setuju dengan @NoelAbrahams

foo.nama = 5 // kesalahan
Dan itu masih valid di JavaScript
Benar, tetapi itu adalah kesalahan karena TypeScript tahu dengan pasti 100% bahwa itu tidak disengaja.

Kompiler hanya tahu bahwa Anda menandai nama sebagai string dan bukan string | number jika Anda menginginkan nilai yang dapat dibatalkan, Anda cukup menandainya sebagai ?string atau string | null ( yang cukup setara)

Saya pikir implementasi yang paling sesuai dengan persyaratan kami adalah tidak menggunakan tipe serikat pekerja, tetapi ikuti saran aslinya:

wikiTanggal: ?Tanggal;

Jadi kami setuju bahwa tipe adalah non null secara default dan Anda akan menandai nullable dengan ? :) .
Perhatikan bahwa ini akan menjadi tipe gabungan karena ?Date akan setara dengan Date | null | undefined :)

Oh maaf, saya mencoba menyetujui nullable secara default dan bukan null dengan pengetikan khusus (simbolnya membingungkan).

@fdecampredon , sebenarnya yang dimaksud adalah ketika suatu bidang atau variabel ditandai sebagai nullable maka diperlukan pelindung tipe untuk akses:

var wikiDate: ?Date;

wikiDate.toString(); // error
wikiDate && wikiDate.toString(); // okay

Ini bukan perubahan yang melanggar, karena kita harus tetap bisa melakukan ini:

 var name: string;   // okay
 name.toString();  // if you think that's fine then by all means

Mungkin Anda percaya bahwa kami tidak dapat memiliki ini tanpa memasukkan null ke dalam tipe serikat?

Contoh pertama Anda benar sekali ketika Anda melakukannya:

wikiDate && wikiDate.toString(); // okay

Anda menggunakan typeguard dan kompiler tidak boleh memperingatkan apa pun.

Namun contoh kedua Anda tidak baik

var name: string;   // okay
name.toString();  // if you think that's fine then by all means

kompiler seharusnya memiliki kesalahan di sini, algoritme sederhana hanya dapat kesalahan pada baris pertama (variabel unitialisasi tidak ditandai sebagai nullable), yang lebih kompleks dapat mencoba mendeteksi penetapan sebelum penggunaan pertama:

var name: string;   // okay
name.toString();  // error because not initialized
var name: string;
if (something) {
  name = "Hello World";
} else {
  name = "Foo bar";
}
name.toString();  // no error since name will always be initialized.

Saya tidak tahu persis di mana harus meletakkan penghalang tetapi itu pasti membutuhkan semacam penyetelan halus agar tidak menghalangi pengembang.

Ini adalah perubahan yang melanggar dan tidak dapat diperkenalkan sebelum 2.0, kecuali mungkin dengan arahan 'gunakan nonnull' yang diusulkan oleh @metaweta

Mengapa tidak memiliki:

var string1: string; //this works like typescript does currently, doesn't need type-guarding before use, null and undefined can be assigned to it.
string1.length; //works
var string2: !string; //this doesn't work because the string must be assigned to a non-null and non-undefined value, doesn't need type-guarding before use.
var string3: ?string; //this must be type guarded to non-null, non-undefined before use.
var string4: !string|null = null; //This may be null, but should never be undefined (and must be type-guarded before use).
var string5: !string|undefined; //this may never be null, but can be undefined (and must be type-guarded before use).

Dan memiliki flag kompiler (yang hanya berfungsi jika -noimplicitany aktif) yang mengatakan -noinferrednulls, yang menonaktifkan sintaks normal untuk tipe (seperti string, dan int) dan Anda harus menyediakan ? atau ! dengan mereka (null, undefined dan semua jenis menjadi pengecualian).

Dengan cara ini, non-nullables adalah opt-in, dan Anda dapat menggunakan flag compiler untuk memaksa sebuah proyek menjadi nulled secara eksplisit.
Kompiler menandai kesalahan pada penetapan types , bukan after (seperti proposal sebelumnya).

Pikiran?

Sunting: Saya menulis ini karena memaksa gagasan menggunakan non-null menjadi eksplisit dalam setiap tindakan. Siapa pun yang membaca kode yang berasal dari proyek TS lain akan tahu persis apa yang terjadi. Juga flag kompiler menjadi sangat jelas jika aktif (karena blah: string adalah kesalahan, tetapi blah:!string tidak, mirip dengan cara -noimplicitany bekerja).

Sunting2:
DefinatelyTyped kemudian dapat ditingkatkan untuk mendukung noninferrednulls, dan mereka tidak akan mengubah penggunaan perpustakaan jika orang memilih untuk tidak ikut serta ke ? dan ! fitur.

Saya tidak peduli apakah non-null dan non-undefined adalah opt-in, opt-out, with
type modifiers (!?), directive, atau compiler flag; Aku akan melakukan apapun itu
dibutuhkan untuk mendapatkannya _selama mungkin untuk diekspresikan_, yang tidak
saat ini kasusnya.

Pada Mon, 22 Desember 2014 di 02:35, Griffork [email protected] menulis:

Mengapa tidak memiliki:

var string1: string; //ini bekerja seperti yang dilakukan TypeScript saat ini., tidak perlu penjagaan tipe sebelum digunakan, null dan undefined dapat diberikan padanya.
string1.panjang; //worksvar string2: !string; //ini tidak berfungsi karena string harus ditetapkan ke nilai non-null dan non-undefined, tidak perlu penjagaan tipe sebelum digunakan.var string3: ?string; //ini harus bertipe guarded to non-null, non-undefined sebelum use.var string4: !string|null = null; //Ini mungkin null, tetapi tidak boleh tidak terdefinisi (dan harus dijaga tipenya sebelum digunakan).var string5: !string|undefined; //ini mungkin tidak pernah nol, tetapi bisa tidak terdefinisi (dan harus dijaga tipenya sebelum digunakan).

Dan memiliki flag kompiler (yang hanya berfungsi jika -noimplicitany aktif) yang
kata -noinferrednulls, yang menonaktifkan sintaks normal untuk tipe (seperti
string, dan int) dan Anda harus menyediakan ? atau ! dengan mereka (null, undefined
dan setiap pengecualian).

Dengan cara ini, non-nullables adalah opt-in, dan Anda dapat menggunakan compiler
flag untuk memaksa proyek menjadi batal secara eksplisit.
Kompiler menandai kesalahan pada _assignment of types_, bukan setelahnya (seperti
usulan sebelumnya).

Pikiran?

Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -67899445
.

Mike Stay - [email protected]
http://www.cs.auckland.ac.nz/~mike
http://reperiendi.wordpress.com

@Griffork yang akan menjadi pilihan, tapi yang buruk menurut saya, dan saya akan menjelaskan mengapa:

  • Ini akan menyebabkan lebih banyak pekerjaan dalam file definisi, karena sekarang kita harus memeriksa dan membubuhi keterangan setiap jenis untuk memiliki definisi yang benar.
  • Kami akhirnya akan menulis !string (dan kadang-kadang ?string ) di mana-mana pada kode yang akan membuat kode jauh lebih mudah dibaca.
  • !string dalam sistem di mana tipe dapat dibatalkan adalah konsep aneh satu-satunya cara Anda tidak dapat benar-benar menggambarkannya adalah string minus null minus undefined , sebaliknya ?string cukup sederhana untuk dijelaskan dalam sistem tipe di mana tipenya nol secara default string | null | undefined .
  • Saya memperkirakan banyak sakit kepala ( dan perf loss ) untuk menemukan algoritma pemeriksaan tipe di mana kompiler memahami bahwa string | null memerlukan penjaga tipe tetapi string tidak, pada dasarnya Anda memperkenalkan konsep di mana beberapa jenis serikat pekerja harus diperlakukan berbeda dari yang lain.
  • Dan akhirnya bagian terburuk kita benar-benar kehilangan inferensi ketik var myString = "hello" apa myString seharusnya string , ?string atau !string ? jujur ​​sakit kepala besar dalam perspektif di sini.

Jika kita tidak memiliki tipe non null sebagai default, proposal terbaik yang pernah saya lihat di sini adalah direktif 'use non-null' diusulkan oleh @metaweta.
Tentu itu perlu ditentukan dengan baik tetapi setidaknya hanya dengan string use non-null di semua file kami, kami bisa mendapatkan perilaku sederhana yang dapat diprediksi.

@fdecampredon

  1. Mungkin lebih banyak pekerjaan dalam file definisi, tetapi _Anda harus tetap melakukannya_ (untuk memastikan jenisnya benar) dan kali ini kompiler akan mengingatkan Anda tentang apa yang belum Anda edit (jika menggunakan -noimplicitnull ).
  2. Saya terbuka untuk saran lain untuk anotasi. Sejujurnya saya percaya bahwa sistem tipe saat ini memiliki tempatnya, dan tidak boleh _diganti_. Saya tidak berpikir perubahan besar itu sepadan. Sebaliknya saya pikir kita harus menemukan cara yang lebih baik untuk menggambarkan apa yang Anda cari. (Saya benar-benar tidak menyukai gagasan untuk mewakili semua ini dengan simbol, mereka tidak intuitif.)
  3. Apa yang sulit untuk dijelaskan tentangnya? Saya telah melihat diskusi di tempat lain di TypeScript di mana permintaan ini (untuk tipe tertentu) telah diusulkan (tanpa penanda). Melakukan pencarian cepat dan saya tidak dapat menemukan masalahnya, saya akan berburu lebih banyak nanti.

  4. Jika Anda mengacu pada apa yang saya tulis sebagai !string|null , itu akan berfungsi di sistem saat ini jika null diperlakukan _like_ {} (tetapi tidak dapat ditetapkan untuk itu).
    Jika Anda berbicara tentang string|null yang tidak saya miliki dalam daftar saya, maka saya pikir null harus diabaikan dalam kasus ini. Null dan undefined hanya masuk akal dalam serikat pekerja di mana setiap non-null dan non-undefined didahului dengan ! dan any bukan sembarang (ini bisa berupa peringatan/kesalahan kompiler).
  5. Pertanyaan bagus, dan satu yang hanya muncul jika Anda menggunakan opsi -noimplicitnull , saya pikir opsi teraman adalah menetapkannya ke opsi mana yang paling mungkin menyebabkan kesalahan awal (mungkin dapat dibatalkan), tetapi saya mendapatkan merasa ada ide yang lebih baik yang tidak saya pikirkan. Saya ingin tahu apakah orang lain memiliki saran tentang bagaimana ini harus didekati?

Sunting: Ditambahkan ke poin 2.
Sunting: Memperbaiki kesalahan ketik pada poin 4.

Mungkin lebih banyak pekerjaan dalam file definisi, tetapi Anda tetap harus melakukan pekerjaan itu (untuk memastikan jenisnya benar) dan kali ini kompiler akan mengingatkan Anda tentang apa yang belum Anda edit (jika menggunakan -noimplicitnull ).

Ya dan tidak, periksa pustaka yang Anda gunakan dan lihat seberapa banyak sebenarnya mengembalikan nol atau tidak terdefinisi.
ini adalah kasus yang cukup langka, kami hanya dapat menemukan dalam masalah ini sangat sedikit kejadian untuk lib standar dan misalnya perpustakaan Promise tidak pernah melakukannya.
Maksud saya adalah bahwa dalam sistem tipe di mana tipe tidak dapat dibatalkan secara default, sebagian besar file definisi yang ada sudah valid .

Saya terbuka untuk saran lain untuk anotasi. Sejujurnya saya percaya bahwa sistem tipe saat ini memiliki tempatnya, dan tidak boleh diganti. Saya tidak berpikir perubahan besar itu sepadan. Sebaliknya saya pikir kita harus menemukan cara yang lebih baik untuk menggambarkan apa yang Anda cari. (Saya benar-benar tidak menyukai gagasan untuk mewakili semua ini dengan simbol, mereka tidak intuitif.)

Saya rasa tidak ada cara seperti itu tetapi saya harap saya salah, karena itu memiliki nilai yang tak ternilai menurut saya.
Untuk bagian perubahan yang melanggar mengapa Anda begitu menentang direktif 'use non-null' ?
Pengembang yang menginginkan sistem tipe nullable tidak akan terpengaruh sama sekali (kecuali mereka anehnya sudah menambahkan 'use non-null' di bagian atas file mereka tapi jujur ​​itu akan sedikit ... aneh)
Dan pengembang yang menginginkan sistem tipe non-null bisa menggunakan arahan baru.

Apa yang sulit untuk dijelaskan tentangnya? Saya telah melihat diskusi di tempat lain di TypeScript di mana permintaan ini (untuk tipe tertentu) telah diusulkan (tanpa penanda). Melakukan pencarian cepat dan saya tidak dapat menemukan masalahnya, saya akan berburu lebih banyak nanti.
Saya hanya menemukan konsepnya agak 'aneh' dan tidak terlalu jelas, saya biasanya menggunakan komposisi sebagai alat utama untuk pemrograman, bukan _dekomposisi_ tetapi mengapa tidak.

Jika Anda mengacu pada apa yang saya tulis sebagai !string|null, itu akan berfungsi di sistem saat ini jika null diperlakukan seperti {} (tetapi tidak dapat ditetapkan untuk itu). Jika Anda berbicara tentang string|null yang tidak saya miliki dalam daftar saya, maka saya pikir null harus diabaikan dalam kasus ini. Null dan undefined hanya masuk akal dalam serikat pekerja di mana setiap non-null dan non-undefined didahului dengan ! dan any bukan sembarang (ini bisa berupa peringatan/kesalahan kompiler).
Saya mengacu pada fakta bahwa ketika Anda menguraikan 3 jenis:

  • !sting : string - null - undefined
  • string : string | null | undefined
  • ?string : string | null | undefined

2 yang terbaru pada dasarnya tidak memiliki perbedaan, tetapi kompiler harus tahu bahwa untuk string seharusnya tidak memaksa pemeriksaan penjaga tipe dan untuk ?string seharusnya, info itu harus disebarkan di mana-mana, algoritma akan jauh lebih kompleks dari yang sebenarnya, dan saya cukup yakin saya dapat menemukan kasus aneh dengan inferensi tipe.

Pertanyaan bagus, dan satu yang hanya muncul jika Anda menggunakan opsi -noimplicitnull , saya pikir opsi teraman adalah menetapkannya ke opsi mana yang paling mungkin menyebabkan kesalahan awal (mungkin dapat dibatalkan), tetapi saya mendapatkan merasa ada ide yang lebih baik yang tidak saya pikirkan. Saya ingin tahu apakah orang lain memiliki saran tentang bagaimana ini harus didekati?

Tidak akan melakukannya pada dasarnya akan memperkenalkan masalah yang sama dari @RyanCavanaugh berkomentar tentang ketika saya berpikir tentang hanya memperkenalkan sebuah bendera yang akan memungkinkan untuk beralih dari nol sebagai default ke nol sebagai default.
Dalam hal ini proposisi sebelumnya jauh lebih sederhana.

Sekali lagi mengapa Anda menentang arahan 'use non-null' , semakin saya memikirkannya, bagi saya itu adalah solusi yang ideal.

@fdecampredon
Karena "gunakan non-null" seperti yang saat ini diusulkan mengubah cara bahasa _digunakan_ bukan cara bahasa _tertulis_. Artinya fungsi dari satu lokasi saat dipindahkan ke lokasi lain dapat bekerja secara berbeda saat _digunakan_. Anda akan mendapatkan kesalahan waktu kompilasi yang berpotensi berjarak 1-3 file karena ada sesuatu yang salah diketik.

Perbedaan antara:
string
dan
?string
Apakah itu ? dan ! simbol meminta pemeriksaan tipe yang ketat untuk variabel ini (seperti arahan "gunakan nonnull" Anda, tetapi berdasarkan per-variabel). Jelaskan seperti itu dan saya pikir Anda tidak akan memiliki banyak masalah dengan orang-orang yang memahaminya.

Perbedaannya tentu saja:
Berkas 1:

//...187 lines of code down...
string myfoo(checker: boolean) {
    if(checker){
        return null;
    }
    else {
        return "hello";
    }
}

berkas 2:

"use nonnull"
//...2,748 lines of code down...
string myfoo(checker: boolean) {
    if(checker){
        return null; //Error!
    }
    else {
        return "hello";
    }
}

Pengembang sekarang harus menyimpan peta mental tentang file mana yang nonnull dan file mana yang tidak. Sejujurnya saya percaya bahwa ini adalah ide yang buruk (bahkan jika sebagian besar kode _your_ adalah 'gunakan nonnull').

Sunting: Juga ketika Anda mulai mengetik suatu fungsi dan Anda mendapatkan jendela kecil yang memberi tahu Anda apa definisi fungsinya, bagaimana Anda tahu jika string dalam definisi itu nullable atau nonnullable?

@Griffork

  • sekali lagi itu sudah terjadi dengan 'use strict' .
  • Setidaknya idenya sederhana. dan memperkenalkan konsep sederhana dalam sistem tipe.
  • Jika pengembang memindahkan fungsi dalam file lain tampaknya merupakan kasus yang cukup baik bagi saya, dan karena kesalahan yang akan dimintanya adalah tentang pemeriksaan nol, dia akan dengan cepat dapat memahami apa yang terjadi ...

Sekali lagi Ini tidak sempurna, tetapi saya tidak melihat alternatif yang lebih baik.

Untuk memperjelas, satu-satunya masalah Anda dengan apa yang saya usulkan (setelah argumen tandingan) yang dapat saya lihat adalah:

  • Kami tidak tahu seperti apa perilaku var mystring = "string" nantinya
  • Anda tidak ingin harus mengetik simbol di mana-mana.

Dan kekhawatiran saya dengan arahan tersebut adalah:

  • Non-nulls tidak akan eksplisit (tetapi dapat terjadi dalam proyek yang sama dengan nullables). edit : memperbaiki kata-kata agar lebih masuk akal.
  • Akan lebih sulit bagi pengembang untuk melacak apakah yang mereka tulis dapat dibatalkan atau tidak dapat dibatalkan.
  • Definisi fungsi yang Anda lihat saat menjalankan fungsi ( edit : pop-up yang disediakan oleh Visual Studio saat Anda mulai mengetik fungsi) mungkin dapat dibatalkan atau tidak dan Anda tidak akan dapat mengetahuinya.
  • Setelah suatu fungsi dibungkus melalui 2 atau 3 lapisan, Anda tidak tahu apakah definisinya masih benar (karena Anda tidak dapat menggunakan inferensi tipe melalui file yang tidak memiliki "gunakan nonnull").

Sejujurnya penerapan "use strict" bukanlah sesuatu yang harus dicita-citakan . Itu dirancang untuk JavaScript (bukan TypeScript), dan dalam JavaScript ada beberapa alternatif yang berharga. Kami menggunakan TypeScript, jadi kami memiliki opsi untuk melakukan sesuatu dengan lebih baik.
Dan saya tidak mengerti mengapa arahan itu lebih baik daripada memaksa pengembang untuk mengungkapkan niat mereka secara eksplisit (bagaimanapun juga, itulah alasan TypeScript dibuat, bukan?).

Sunting: Mengklarifikasi suatu poin.
Sunting: Masukkan string dalam tanda kutip

Sejujurnya, ringkasan kekhawatiran saya agak kecil, dan tidak mencerminkan apa yang saya tulis, proposisi Anda menambah kompleksitas pada sistem tipe, membuat algoritma tipe-cek sakit kepala, cukup sulit untuk ditentukan dengan semua kasus tepi yang akan terjadi buat khusus untuk inferensi tipe, dan buat kode ultra-verbose tanpa biaya. Pada dasarnya bukan alat yang tepat untuk pekerjaan itu.

Saya ingin semua non-nulls menjadi eksplisit (karena mereka dapat terjadi dalam proyek yang sama dengan nullables).

Saya ingin fakta bahwa string sebenarnya string | null | undefined tanpa memaksa Anda untuk memeriksanya, eksplisit.

Akan lebih sulit bagi pengembang untuk melacak apakah yang mereka tulis dapat dibatalkan atau tidak dapat dibatalkan.

Saya ragu akan ada satu file dalam proyek tanpa 'gunakan non-null' jika proyek menggunakan nonnullity, dan menggulir di bagian atas pada file Anda tidak terlalu sulit (setidaknya ketika Anda menulis file dengan kurang dari 500 baris kode yang merupakan sebagian besar kasus ...)

Definisi fungsi yang Anda lihat saat menjalankan suatu fungsi mungkin dapat dibatalkan atau tidak dan Anda tidak akan dapat mengetahuinya.

Ya, Anda akan melakukannya jika itu berasal dari modul yang memiliki arahan 'gunakan non-null' akan diketik sesuai, jika Anda tidak hanya menganggap semuanya sebagai nullable ...

Setelah suatu fungsi dibungkus melalui 2 atau 3 lapisan, Anda tidak tahu apakah definisinya masih benar (karena Anda tidak dapat menggunakan inferensi tipe melalui file yang tidak memiliki "gunakan nonnull").

Saya tidak mengerti maksud Anda di sini.

Sejujurnya penerapan "use strict" bukanlah sesuatu yang harus dicita-citakan. Itu dirancang untuk JavaScript (bukan TypeScript), dan dalam JavaScript ada beberapa alternatif yang berharga. Kami menggunakan TypeScript, jadi kami memiliki opsi untuk melakukan sesuatu dengan lebih baik.
Dan saya tidak mengerti mengapa arahan itu lebih baik daripada memaksa pengembang untuk mengungkapkan niat mereka secara eksplisit (bagaimanapun juga, itulah alasan TypeScript dibuat, bukan?).

TypeScript adalah superset dari javascript, memiliki akar dalam JavaScript dan tujuannya adalah untuk memungkinkan Anda menulis javascript dengan cara yang lebih aman, jadi menggunakan kembali konsep JavaScript tampaknya tidak masuk akal.

Dan saya tidak mengerti mengapa arahan lebih baik daripada memaksa pengembang untuk secara eksplisit tentang niat mereka

Karena Anda tidak akan dapat mencapai hasil yang sama, untuk semua alasan yang saya kutip memiliki tipe non-nullable dalam sistem tipe nullable hanyalah ide yang buruk.

Dari sudut pandang saya, ada 3 solusi yang layak di sini:

  • Melanggar perubahan untuk 2.0 di mana tipe menjadi tidak dapat dibatalkan
  • Bendera kompiler yang beralih ke tipe non-nullable secara default seperti saya mengusulkan beberapa lusin komentar, tetapi @RyanCavanaugh punya poin bagus tentang itu. (acara jika saya benar-benar berpikir bahwa itu layak)
  • direktif 'gunakan non-null'

Omong-omong, bendera itu akan sangat berharga bagi saya.

function fn(x: string): number;
function fn(x: number|null): string;

function foo() {
    return fn(null);
}

var x = foo(); // x: number or x: string?

Jika fungsi adalah satu-satunya perhatian, pengecualian dapat dibuat dalam kasus di mana kelebihan berisi argumen null eksplisit - itu akan selalu diutamakan daripada null implisit (agar harmonis dengan --noImplicitNull ) . Ini juga akan menjadi interpretasi yang masuk akal bagi pengguna (yaitu "apa yang saya katakan secara eksplisit harus mengesampingkan apa yang dikatakan secara implisit"). Meskipun saya bertanya-tanya apakah ada masalah serupa lainnya dengan flag yang tidak dapat diselesaikan dengan cara ini. Dan tentu saja ini menambahkan beberapa kerumitan peretasan pada spesifikasi dan implementasinya :|

  1. string|null|undefined secara eksplisit dalam proposal saya menggunakan flag, itu sebabnya saya mengabaikannya.
  2. Lalu mengapa harus per file? Saya menyarankan saran saya karena _tidak merusak kompatibilitas_ yang penting bagi saya, dan mungkin banyak pengembang lain. Dan itu bisa dipaksakan untuk seluruh proyek.
  3. Saya menggunakan banyak file yang tidak saya buat; bagaimana saya tahu bahwa file tertentu memiliki "gunakan nonnull"? Jika saya memiliki 100 file yang dibuat oleh orang lain, saya harus mengingat mana dari 100 file tersebut yang nonnull? (atau, _setiap kali_ saya menggunakan variabel/fungsi dari file lain, saya harus membuka file itu dan memeriksanya?)
  4. Mari kita lihat apakah saya dapat membuatnya lebih jelas: contoh di akhir posting.
  5. Di mana Anda berhenti? Di mana Anda menyebutnya buruk? Satu string atau kata kunci di awal file tidak boleh mengubah cara file berperilaku. "gunakan ketat" ditambahkan ke javascript karena

    1. Tidak ada superset besar Javascript (misalnya TypeScript) yang ada pada saat itu yang dapat melakukan apa yang diinginkannya.

    2. Itu adalah upaya untuk mempercepat pemrosesan JavaScript (yang di mata saya adalah satu-satunya alasan itu bisa dimaafkan).

      "gunakan ketat" diadopsi _bukan karena itu adalah hal yang benar untuk dilakukan_ tetapi karena itu satu -

  6. Javascript bukan "sistem tipe non-nullable", TypeScript bukan "sistem tipe non-nullable". Memperkenalkan kemampuan untuk mendeklarasikan tipe non-nullable tidak berarti bahwa seluruh sistem "non-nullable". Itu bukan inti dari TypeScript.
File 1:
"use notnull"
export string foo() {
    return "mygeneratedstring";
}
File 2:
export string foo() {
    return file1.foo()
}
File 3:
"use notnull"
file2.foo(); //???

Anda sebenarnya memiliki kemampuan untuk kehilangan informasi kontekstual saat masih menggunakan sintaks yang sama! Itu bukan sesuatu yang saya nantikan dalam bahasa apa pun yang saya gunakan.


Kami seperti sedang berdebat berputar-putar. Jika saya tidak masuk akal bagi Anda maka saya minta maaf, tetapi Anda tampaknya berulang kali mengangkat masalah yang sama, dan saya berulang kali membalasnya.

@spion
Dalam contoh string itu bukan null implisit (saya yakin ini mengasumsikan bahwa flag --noImplicitNull aktif)

string|null|undefined secara eksplisit dalam proposal saya menggunakan bendera, itu sebabnya saya meninggalkannya

Maksud saya adalah bahwa dalam sistem aktual var myString: string secara implisit var myString: (string | null | undefined); dan di atas itu kompiler tidak memaksa Anda untuk menggunakan typeguard kecuali semua jenis serikat lainnya.

Lalu mengapa harus per file? Saya menyarankan saran saya karena itu tidak merusak kompatibilitas mundur yang penting bagi saya, dan mungkin banyak pengembang lain. Dan itu bisa dipaksakan untuk seluruh proyek.
Arahan tidak merusak kompatibilitas ke belakang (kecuali jika Anda menggunakan string literal 'use not-null' dalam posisi arahan untuk alasan yang cukup aneh, yang saya yakin tidak ada yang melakukannya), Juga pada titik tertentu TypeScript harus memecah kompatibilitas ke belakang, itu sebabnya semver mendefinisikan versi utama, tapi itu cerita lain.

Saya menggunakan banyak file yang tidak saya buat; bagaimana saya tahu bahwa file tertentu memiliki "gunakan nonnull"? Jika saya memiliki 100 file yang dibuat oleh orang lain, saya harus mengingat mana dari 100 file tersebut yang nonnull? (atau, setiap kali saya menggunakan variabel/fungsi dari file lain, saya harus membuka file itu dan memeriksanya?)

Jika Anda berada dalam proyek dengan pedoman dll, dll, seperti setiap proyek diatur secara normal, Anda tahu ... dalam kasus terburuk, Anda hanya perlu menggulir sedikit ... sepertinya tidak terlalu sulit ...

Di mana Anda berhenti? Di mana Anda menyebutnya buruk? Satu string atau kata kunci di awal file tidak boleh mengubah cara file berperilaku. "gunakan ketat" ditambahkan ke javascript karena
Tidak ada superset besar Javascript (misalnya TypeScript) yang ada pada saat itu yang dapat melakukan apa yang diinginkannya.
Itu adalah upaya untuk mempercepat pemrosesan JavaScript (yang di mata saya adalah satu-satunya alasan itu bisa dimaafkan). "gunakan ketat" diadopsi bukan karena itu adalah hal yang benar untuk dilakukan tetapi karena itu satu-satunya cara agar browser dapat memenuhi tuntutan pengembang. Saya tidak suka melihat TypeScript menambahkan 1 (lalu 2, lalu 3 lalu 4) arahan lain yang secara mendasar mengubah cara kerja bahasa sebagai string yang dideklarasikan dalam beberapa lingkup arbitrer, dan memengaruhi beberapa lingkup arbitrer lainnya. Ini benar-benar desain bahasa yang buruk. Saya akan senang jika "gunakan ketat" tidak ada di TypeScript, dan sebagai gantinya adalah flag compiler (dan TypeScript memancarkannya ke setiap file/lingkup yang membutuhkannya).

'gunakan ketat' diperkenalkan untuk mengubah perilaku bahasa dan mempertahankan kompatibilitas retro, masalah yang sama persis yang kita hadapi sekarang, dan akan sedikit banyak menghilang dengan modul es6 (dan penggunaan not null bisa hilang dengan versi utama TypeScript berikutnya) .

Javascript bukan "sistem tipe non-nullable", TypeScript bukan "sistem tipe non-nullable". Memperkenalkan kemampuan untuk mendeklarasikan tipe non-nullable tidak berarti bahwa seluruh sistem "non-nullable". Itu bukan inti dari TypeScript.

Kalimat itu tidak masuk akal bagi saya, JavaScript tidak memiliki sistem tipe statis, jadi seperti yang saya katakan, fakta bahwa Anda dapat menetapkan null ke variabel yang sebelumnya string dapat dengan mudah dibandingkan fakta bahwa Anda dapat menetapkan 5 ke variabel yang merupakan string sebelumnya.
Satu-satunya perbedaan adalah TypeScript, pemeriksa tipe yang dibuat untuk JavaScript, dengan pendapat menganggap null sebagai dapat ditetapkan untuk semuanya dan bukan 5 .

Sebagai contoh Anda, ya, kami kehilangan beberapa informasi di antara file, ini adalah pertukaran yang dapat diterima bagi saya, tetapi saya dapat memahami kekhawatiran Anda.

Kami seperti sedang berdebat berputar-putar. Jika saya tidak masuk akal bagi Anda maka saya minta maaf, tetapi Anda tampaknya berulang kali mengangkat masalah yang sama, dan saya berulang kali membalasnya.

Ya saya setuju, saya ragu akan ada konsensus tentang itu, sejujurnya saya berpikir bahwa saya hanya akan berakhir dengan garpu kompiler dan menambahkan tipe non null untuk diri saya sendiri berharap suatu hari itu akan berakhir di kompiler utama, atau aliran itu cukup matang untuk dapat digunakan.

@Griffork menurut saya ide @spion berhasil, idenya adalah untuk mengatakan bahwa tanpa flag string menjadi nullable implisit number | null akan selalu menjadi prevalensi pada kelebihan pertama, jadi di keduanya kasus foo(null) akan menjadi string .

@RyanCavanaugh menurut Anda itu bisa berhasil?

@fdecampredon

  1. "gunakan ketat" diperkenalkan untuk mengoptimalkan javascript. Itu harus dikonsumsi oleh browser , oleh karena itu harus dalam kode. Dan itu harus kompatibel ke belakang, jadi itu harus menjadi pernyataan non-fungsional.
    Jenis yang tidak dapat dibatalkan tidak harus dikonsumsi oleh browser, sehingga tidak harus memiliki arahan dalam kode.
  2. Maaf, saya mengacu pada sesuatu yang saya lupa posting, ini dia:

Menurut Facebook : "Dalam JavaScript, null secara implisit dikonversi ke semua tipe primitif; itu juga merupakan penghuni yang valid dari semua tipe objek."
Alasan saya ingin non-nullable menjadi eksplisit (dalam setiap tindakan yang dilakukan pengembang) adalah karena dengan menggunakan non-nullable, pengembang mengakui bahwa mereka menyimpang dari cara kerja javascript, dan bahwa suatu tipe tidak dapat dibatalkan tidak dijamin (karena ada banyak hal yang dapat menyebabkan kesalahan ini).
Properti yang tidak dapat dibatalkan adalah properti yang tidak akan

Saya telah menggunakan TypeScript untuk waktu yang cukup lama. Sulit bagi saya untuk awalnya meyakinkan bos saya untuk mengubah dari Javascript ke Typescript, dan cukup sulit bagi saya untuk meyakinkan dia untuk mengizinkan kami meningkatkan setiap kali ada perubahan yang melanggar (bahkan jika tidak, itu tidak mudah). Ketika ES:Harmony didukung oleh browser, kami mungkin ingin menggunakannya. Jika perubahan yang melanggar terjadi di TypeScript antara sekarang dan ketika TypeScript mendukung ES:Harmony (terutama yang lazim seperti non-nullable) saya ragu saya akan memenangkan argumen itu. Belum lagi jumlah waktu dan uang yang harus kami keluarkan untuk melatih semua pengembang kami (saya adalah "orang TypeScript" kami, tugas saya adalah melatih pengembang TypeScript kami).
Jadi, saya benar-benar menentang memperkenalkan perubahan yang melanggar.
Saya tidak keberatan bisa menggunakan non-nullables sendiri, jika ada cara saya bisa memasukkannya ke dalam kode masa depan tanpa merusak kode lama (inilah alasan proposal saya). Idealnya pada akhirnya semua kode kita akan dikonversi, tetapi akan menghabiskan waktu dan uang yang jauh lebih sedikit dalam melatih orang dan menggunakan perpustakaan pihak ke-3.

Saya suka ide menggunakan simbol (sesuai saran saya) alih-alih arahan per file, karena mereka memiliki kemampuan untuk menyebar tanpa kehilangan kontekstual.

@Griffork Ok seperti yang saya katakan, saya mengerti kekhawatiran Anda, saya harap Anda mengerti kekhawatiran saya.
Sekarang saya tidak memiliki pendapat yang sama, perubahan teknologi, tidak ada yang stabil (seluruh drama di sekitar sudut 2 telah membuktikannya), dan saya lebih suka merusak barang-barang dan memiliki alat yang lebih baik daripada tetap menggunakan apa yang saya miliki, saya pikir dalam jangka panjang itu membuat saya menang kali dan uang.
Sekarang seperti yang kita berdua simpulkan sebelumnya, saya ragu kita akan memiliki konsensus di sini, saya lebih suka arahan, Anda lebih suka proposal Anda.
Pokoknya seperti yang saya katakan untuk bagian saya, saya hanya akan mencoba untuk garpu kompiler dan menambahkan tipe non-null, karena saya pikir itu bukan perubahan besar dalam kompiler.
Terima kasih diskusinya menarik

@fdecampredon

Terima kasih atas diskusinya

Menganggap Anda berarti mencerahkan (karya menarik juga :D), saya juga menikmati perdebatan :).
Semoga berhasil dengan garpu Anda, dan saya harap kami mendapatkan orang TS di sini untuk setidaknya mengabaikan beberapa saran kami (jadi kami tahu apa yang pasti tidak ingin mereka terapkan).

Jadi untuk menguraikan lebih lanjut tentang solusi yang saya usulkan untuk masalah yang dijelaskan @RyanCavanaugh , setelah menambahkan tipe null dan flag --noImplicitNull , typechecker ketika dijalankan tanpa flag akan memiliki perilaku yang dimodifikasi:

Setiap kali kelebihan tipe berisi |null (seperti contoh yang diberikan):

function fn(x: string): number;
function fn(x: number|null): string;

typechecker akan mengambil setiap kelebihan dan membuat varian baru dengan (implisit) null pada posisi argumen itu, ditambahkan di akhir daftar:

function fn(x: string): number;
function fn(x: number|null): string;
// implicit signature added at the end, caused by the first overload
function fn(x: null): number;

Ini menghasilkan interpretasi yang sama dengan versi --noImplicitNull ketat: ketika melewati nol, kelebihan pertama yang cocok adalah number|null eksplisit. Secara efektif itu membuat null eksplisit lebih kuat daripada yang implisit.

(Ya, saya tahu bahwa jumlah definisi implisit tumbuh secara eksponensial dengan jumlah argumen yang memiliki kelebihan |null - Saya berharap ada implementasi yang lebih cepat yang "mengizinkan" nol pada pass kedua jika tidak ada tipe lain cocok, tapi saya tidak berpikir itu akan menjadi masalah karena saya berharap situasi ini jarang terjadi)

Diformulasikan seperti ini, saya pikir perubahannya diharapkan akan sepenuhnya kompatibel ke belakang dan ikut serta.

Berikut adalah beberapa ide tentang sisa perilaku:

Menetapkan nilai nol atau membiarkan nilai tidak diinisialisasi juga akan diizinkan untuk nilai jenis apa pun saat bendera tidak diteruskan.

Saat tanda diaktifkan, membiarkan nilai tidak diinisialisasi (tidak ditetapkan) atau nol akan diizinkan hingga waktu diteruskan ke fungsi atau operator yang mengharapkannya tidak nol. Tidak yakin tentang anggota kelas: mereka juga akan

  • perlu diinisialisasi pada akhir konstruktor, atau
  • inisialisasi dapat dilacak dengan memeriksa apakah metode anggota yang sepenuhnya menginisialisasi mereka dipanggil.

Serikat nol eksplisit akan berperilaku serupa dengan semua serikat lain di kedua mode. Mereka akan membutuhkan penjaga untuk mempersempit jenis serikat pekerja.

Saya baru saja membaca _all_ komentar di atas dan itu banyak. Sangat mengecewakan bahwa setelah begitu banyak komentar dan 5 bulan, kami masih memperdebatkan poin yang sama: sintaks dan flag...

Saya pikir banyak orang telah membuat argumen kuat untuk nilai pemeriksaan nol statis oleh kompiler dalam basis kode besar. Saya tidak akan mengatakan apa-apa lagi (tetapi +1).

Sejauh ini perdebatan utama adalah tentang sintaks dan masalah kompatibilitas dengan banyak kode TS yang ada dan definisi perpustakaan TS. Saya dapat melihat mengapa memperkenalkan tipe null dan sintaks Flow ?string tampaknya paling bersih dan paling alami. Tapi ini mengubah arti dari string dan karenanya merupakan _perubahan besar_ yang melanggar untuk _semua_ kode yang ada.

Solusi yang diusulkan di atas untuk perubahan yang melanggar ini adalah dengan memperkenalkan flag compiler ( @RyanCavanaugh telah membuat poin yang sangat bagus mengapa ini adalah ide yang buruk) atau direktif per-file. Ini sepertinya tindakan stop-gap yang sangat hackish. Selain itu, saya tidak suka membaca kode di tengah ulasan yang berbeda atau file besar dan tidak dapat segera mengetahui _pasti_ arti kode yang saya lihat. 'use strict'; dikutip sebagai preseden. Bukan karena TS mewarisi kesalahan masa lalu JS sehingga kita harus mengulanginya.

Itu sebabnya saya pikir anotasi "bukan nol" ( string! , !string , ...) adalah satu-satunya cara untuk pergi dan saya akan mencoba memperkenalkan argumen baru dalam diskusi: mungkin itu tidak begitu buruk.

Tidak ada yang ingin memiliki poni ( ! ) di seluruh kode. Tapi kita mungkin tidak perlu melakukannya. TS menyimpulkan banyak tentang mengetik.

1. Variabel lokal
Saya tidak berharap untuk membubuhi keterangan variabel lokal saya dengan poni. Saya tidak peduli jika saya menetapkan null ke variabel lokal, selama saya menggunakannya dengan cara yang aman, yang dapat ditegaskan oleh TS.

var x: string;  // Nullable
x.toString();  // Error: x can be undefined or null
x = "jods";
x.toUpperCase();  // Fine.
notNull(x);  // Fine

Cukup sering saya menggunakan penginisialisasi alih-alih tipe.

var x = "jods";  // x: string (nullable)
notNull(x);  // Fine
x = null;  // Fine
notNull(x);  // Error

2. Fungsi mengembalikan nilai
Sangat sering saya tidak menentukan nilai pengembalian. Sama seperti TS menyimpulkan jenisnya, ia dapat menyimpulkan apakah nullable atau tidak.

function() /* : string! */ {
  return "jods";
}

EDIT: ide buruk karena pewarisan dan variabel fungsi, lihat komentar oleh @Griffork di bawah
Acara jika saya memberikan tipe pengembalian, TS sangat mampu menambahkan anotasi "bukan nol" untuk saya.

Jika Anda ingin mengganti nullability yang disimpulkan (misalnya karena kelas turunan dapat mengembalikan null meskipun metode Anda tidak), Anda menentukan jenisnya secara eksplisit:

function f() : string {  // f is nullable although this implementation never returns null.
  return "abc";
}

3. Parameter fungsi
Sama seperti Anda harus secara eksplisit menentukan jenis, di situlah Anda _harus_ menunjukkan jika Anda tidak menerima parameter nulls:

function (x: string!) { return x.toUpperCase(); } // OK
function (x: string) { return x.toUpperCase(); } // Error

Untuk menghindari kesalahan kompilasi di basis kode lama, kita memerlukan tanda baru "Dereferensi nol adalah kesalahan", sama seperti kita memiliki "Tidak ada yang tersirat". _Tanda ini tidak mengubah analisis kompiler, hanya apa yang dilaporkan sebagai kesalahan!_
Apakah itu banyak poni untuk ditambahkan? Sulit dikatakan tanpa melakukan statistik pada basis kode yang nyata dan besar. Jangan lupa bahwa parameter opsional adalah hal biasa di JS dan semuanya dapat dibatalkan, jadi Anda memerlukan anotasi tersebut jika 'bukan nol' adalah defaultnya.
Poni itu juga merupakan pengingat yang bagus bahwa null tidak diterima untuk penelepon, konsisten dengan apa yang mereka ketahui tentang string hari ini.

4. Bidang kelas
Ini mirip dengan 3. Jika Anda perlu membaca bidang tanpa dapat menyimpulkan isinya, Anda harus menandainya sebagai non-nullable saat deklarasi.

class C {  x: string!; }

function(c: C!) // : string! is inferred
{ return c.x; } // OK, but annotations are required

Kita bisa membayangkan singkatan untuk penginisialisasi, mungkin:

class C {
  x! = "jods"; // Note the bang: x is inferred as !string rather than just string.
}

Singkatan serupa mungkin diinginkan untuk properti nullable jika Anda mengatakan not-null adalah default dengan penginisialisasi.
Sekali lagi sulit untuk mengatakan apakah sebagian besar bidang dalam kode yang ada dapat dibatalkan atau tidak, tetapi anotasi ini sangat sesuai dengan harapan yang dimiliki pengembang saat ini.

5. Deklarasi, .d.ts
Manfaat besar adalah bahwa semua perpustakaan yang ada berfungsi. Mereka menerima nilai null dan non-null sebagai input dan mereka diasumsikan mengembalikan nilai yang mungkin nol.
Pada awalnya Anda perlu secara eksplisit memberikan beberapa nilai pengembalian ke "bukan nol", tetapi situasinya akan membaik karena definisi diperbarui secara perlahan untuk menunjukkan kapan mereka tidak pernah mengembalikan nol. Memberi anotasi pada parameter lebih aman tetapi tidak diperlukan agar kompilasi berhasil.

Saya pikir menyatakan status "bukan-null" dari suatu nilai di mana kompiler tidak dapat menyimpulkannya dapat berguna (misalnya ketika memanipulasi kode yang tidak diketik, atau ketika dev dapat membuat beberapa pernyataan mengenai keadaan global program) dan singkatan mungkin bagus:

var a: { s: string } = something(); // Notice s is nullable.
notNull(a.s); // Error
notNull(<!>a.s);  // OK, this is a shorthand "not-null" cast, because the dev knows about something().

Saya pikir kemampuan untuk mengisyaratkan kompiler - dengan cara yang sederhana - bahwa sesuatu yang tidak nol diinginkan. Ini juga berlaku untuk not-null secara default, meskipun lebih sulit untuk membuat sintaks logis yang sederhana.

Maaf, ini adalah posting yang sangat panjang. Saya mencoba menunjukkan gagasan bahwa bahkan dengan "null secara default", kita tidak perlu membubuhi keterangan semua kode kita: TS dapat menyimpulkan kebenaran sebagian besar kode tanpa bantuan kita. Dua pengecualian adalah bidang dan parameter. Saya pikir ini tidak terlalu berisik dan membuat dokumentasi yang baik. Poin kuat yang mendukung pendekatan ini adalah bahwa pendekatan ini kompatibel ke belakang.

Saya setuju dengan sebagian besar dari apa yang Anda katakan @ jods4 tetapi saya memiliki beberapa pertanyaan dan kekhawatiran:

2) jika kompiler dapat secara otomatis mengonversi nullable ke non nullable pada tipe pengembalian, bagaimana Anda bisa menimpanya (misalnya untuk pewarisan atau fungsi yang diganti)?

3 & 5) Jika kompiler dapat mengonversi nullable menjadi non nullable maka tanda tangan fungsi dalam file kode sekarang dapat berperilaku berbeda dengan tanda tangan fungsi yang sama dalam file d.ts.

6) Bagaimana cara kerja/tampilan bang saat menggunakan tipe serikat pekerja?

2) Hasil tangkapan yang bagus. Saya tidak berpikir itu akan bekerja dengan baik dengan warisan (atau 'delegasi') memang.
Di tempat saya bekerja, pengalaman kami dengan TS adalah bahwa kami hampir tidak pernah secara eksplisit mengetik nilai pengembalian dari fungsi. TS mengetahuinya dan saya pikir TS dapat mengetahui bagian nol/bukan nol.

Satu-satunya kasus di mana kami secara eksplisit menentukannya adalah ketika kami ingin mengembalikan antarmuka atau kelas dasar, misalnya untuk ekstensibilitas (termasuk pewarisan). Nullability tampaknya cocok dengan deskripsi ini.

Mari kita ubah proposal saya di atas: nilai pengembalian yang diketik secara eksplisit adalah _not_ disimpulkan tidak dapat dibatalkan jika tidak dinyatakan demikian. Ini bekerja dengan baik untuk kasus yang Anda sarankan. Ini menyediakan cara untuk mengganti kompiler yang menyimpulkan "bukan nol" pada kontrak yang mungkin nol di kelas anak.

3 & 5) Dengan perubahan di atas, bukan itu lagi, kan? Tanda tangan fungsi dalam kode dan definisi sekarang berperilaku dengan cara yang persis sama.

6) Pertanyaan yang menarik!
Yang ini memang tidak bagus, terutama jika Anda menambahkan obat generik ke dalam campuran. Satu-satunya jalan keluar yang dapat saya pikirkan adalah bahwa semua bagian harus non-null. Anda mungkin memerlukan tipe literal nol karena obat generik, lihat contoh terakhir saya.

var x: number | string!; // compiler error
var x: number | string; // x can be null
var x: number! | string!; // x cannot be null
function f<T>() : number | T; // f can be null
function f<T>() : number! | T; // f is nullable if T is nullable
function f<T, G>(): T | G; // f is nullable if T or G is nullable
function f<T>(): T | null; // f is nullable even if T is not nullable.
function f<T>(): T!; // Whatever T is, f never returns null.

// Generics constraint option 1
function f<T!>(x: T!, y: T): T!; // T: not nullable type, x: not-null, y: null, f: not-null
function f<T!>(x: T!, y: T): T;  // T: not nullable type, x: not-null, y: null, f: null

// Generics constraint option 2
function f<T!>(x: T, y: T | null): T; // same as option 1.1
function f<T!>(x: T, y: T | null): T | null;  // same as option 1.2

Demi diskusi, perhatikan bahwa jika Anda menggunakan tipe non-null secara default, Anda juga perlu menemukan sintaks baru: bagaimana cara menyatakan bahwa tipe generik tidak boleh mengizinkan nilai nullable?
Dan sebaliknya, bagaimana Anda menentukan bahwa meskipun tipe generik T berisi nol, suatu fungsi tidak pernah mengembalikan T tetapi tidak pernah nol?

Ketik literal ok-ish tetapi juga tidak terlalu memuaskan: var x: { name: string }! , terutama jika mereka menjangkau lebih dari satu baris :(

Contoh lainnya:
Array angka bukan nol number![]
Bukan array angka nol: number[]!
Tidak ada yang bisa nol: number![]!
Umum: Array<number!>[]

2, 3 & 5) Saya setuju dengan perubahan proposal, yang sepertinya akan berhasil.
6)

function f<T>() : number | T; // f can be null
function f<T>() : number! | T; // f is nullable if T is nullable

Saya berasumsi bahwa di sini maksud Anda f dapat mengembalikan nol, bukan berarti f dapat ditetapkan ke nol.

Untuk serikat pekerja, apa pendapat Anda tentang sintaks berikut sebagai opsi lain yang tersedia? (terlihat agak konyol, tetapi mungkin lebih ringkas/lebih mudah diikuti dalam beberapa kasus).

var x = ! & number | string;

Untuk menandakan bahwa keduanya tidak dapat dibatalkan.


Untuk tipe literal saya akan mengatakan bahwa Anda dapat meletakkan bang di awal ekspresi literal serta di akhir, karena var x: !{name: string} , menurut saya akan lebih mudah untuk dikerjakan daripada memilikinya di akhir.

@ jods4 apakah Anda membaca tentang perbaikan yang diusulkan "null eksplisit memiliki prioritas di atas null implisit"? Itu menangani berbagai masalah semantik, saya pikir.

@Griffork
Ya tentu saja, "dapat dibatalkan" adalah kata-kata yang buruk untuk "dapat mengembalikan nol".

Yang membuat saya berpikir bahwa function f() {} sebenarnya adalah sebuah deklarasi dari f , yang dapat disetel nanti... apakah kita ingin menyatakan bahwa f tidak akan pernah disetel ke nol ? Suka function f!() {} ? Sepertinya itu terlalu jauh menurut saya. Saya pikir pemeriksa tipe harus menganggap ini selalu terjadi. Ada kasus Edge lain yang tidak biasa yang tidak dapat ditanganinya.

Mengenai opsi baru, saya setuju bahwa itu terlihat agak konyol dan memperkenalkan satu simbol tambahan dalam deklarasi tipe: & , dengan satu tujuan... Itu bukan ide yang bagus untuk saya. Dan dalam beberapa bahasa & AND mengikat dengan prioritas lebih tinggi dari | OR yang tidak terjadi di sini.

Menempatkan bang di depan tipe adalah alternatif yang mungkin, saya kira kita harus mencoba menulis ulang semua contoh untuk melihat apa yang mungkin terlihat lebih baik. Saya pikir pintasan bidang ini yang saya sarankan akan menjadi masalah:

class C {
  name! = "jods";  // Field inferred string, but marked not nullable.
  // Maybe we could do that instead, which works with "!T" convention:
  name : ! = "jods";
  // That kind of make sense with the proposed <!> cast.
}

@spion
Ya saya melihatnya.

Pertama, saya tidak yakin bahwa itu benar-benar menyelesaikan masalah yang dihadapi. Jadi dengan definisi baru Anda yang kelebihan beban mengikat fn(null) ? Yang 'dihasilkan' yang mengembalikan number ? Bukankah itu tidak konsisten dengan fakta bahwa kelebihan kedua juga menerima null tetapi mengembalikan string ? Atau apakah ini kesalahan kompiler karena Anda tidak dapat melakukan resolusi kelebihan beban?

Kedua, saya cukup yakin kita bisa menemukan banyak masalah lain. Saya pikir mengubah arti global dari kode sumber di flip switch tidak bisa bekerja. Tidak hanya di .d.ts , tetapi orang suka menggunakan kembali kode (penggunaan kembali perpustakaan atau bahkan menyalin-menempel). Saran Anda adalah bahwa makna semantik dari kode sumber saya bergantung pada flag kompiler dan itu tampaknya merupakan proposisi yang sangat cacat bagi saya.

Tetapi jika Anda dapat membuktikan bahwa kode akan dikompilasi dan dijalankan dengan benar dalam semua kasus, saya setuju!

@jods4

Dalam kedua kasus, kelebihan beban ke-2 akan digunakan

function fn(x: string): number;
function fn(x: number|null): string; 

karena null eksplisit ke-2 akan didahulukan daripada implisit (yang pertama), tidak peduli flag mana yang digunakan. Oleh karena itu artinya tidak berubah berdasarkan bendera.

Karena tipe null tidak ada sekarang, mereka akan diperkenalkan bersama dan perubahan akan sepenuhnya kompatibel ke belakang. Definisi tipe lama akan terus bekerja secara normal.

@spion
Jadi ada apa dengan implisit function fn(x: null): number; ? Jika kelebihan beban ke-2 selalu diutamakan, tidak peduli flag mana yang digunakan, seluruh "perbaikan" ini tidak berpengaruh sama sekali?

Pertanyaan lain: hari ini semua definisi lib memiliki perilaku "tidak diizinkan". Jadi sampai mereka _all_ diperbarui, saya harus menggunakan flag "implisit null". Sekarang dengan flag ini aktif, bagaimana saya bisa mendeklarasikan fungsi yang menggunakan parameter non-null di proyek saya?

// null by default flag turned on because of 3rd party libs.
function (x: string)   // <- how do I declare this not null?
{ return x.toUpperCase(); }

Perbaikan itu pasti berpengaruh. Itu membuat perilaku dengan dan tanpa bendera konsisten, yang memperbaiki masalah semantik yang disebutkan di atas.

Kedua, Anda tidak perlu menggunakan tanda "null secara default" untuk lib pihak ke-3. Hal terpenting tentang fitur ini adalah Anda tidak perlu melakukan _apa pun_ untuk mendapatkan manfaat di sebagian besar kasus.

Katakanlah ada perpustakaan yang menghitung jumlah kata untuk sebuah string. Deklarasinya adalah

declare function wordCount(s: string): number;

Katakanlah kode Anda menggunakan fungsi ini dengan cara ini:

function sumWordcounts(s1:string, s2:string) {
  return wordCount(s1) + wordCount(s2);
}

Program ini lewat di bawah kedua kompiler: bahkan jika null implisit dinonaktifkan. Kode ini benar-benar kompatibel ke belakang.

Mengapa? Karena kompiler seperti sekarang ini sudah memiliki keyakinan bahwa nilai tidak nol di mana pun Anda mencoba menggunakannya (meskipun secara teoritis bisa nol).

Kompiler baru juga akan mengasumsikan bahwa nilainya tidak nol ketika Anda mencoba menggunakannya. Tidak ada alasan untuk percaya sebaliknya, karena Anda belum menentukan bahwa nilainya mungkin nol, sehingga bagian dari perilaku tetap sama. Perubahan perilaku hanya berlaku saat menetapkan nol (atau membiarkan variabel tidak diinisialisasi) dan kemudian mencoba meneruskan nilai tersebut ke fungsi seperti di atas. Mereka tidak memerlukan perubahan apa pun di bagian lain dari kode yang menggunakan nilai secara normal.

Sekarang mari kita lihat implementasi dari wordCount

function wordCount(s) {
  if (s == '') return null;
  return s.split(' ').length
}

Ups, tipe ini tidak menceritakan keseluruhan cerita. Mungkin saja fungsi itu mengembalikan nilai nol.

Masalahnya justru itu. Mustahil untuk menceritakan keseluruhan cerita di kompiler saat ini, bahkan jika kita mau. Tentu, kami mengatakan bahwa nilai kembalian adalah angka, yang secara implisit menyatakan itu bisa nol: tetapi kompiler tidak pernah memperingatkan tentang itu ketika kami mencoba mengakses nilai yang berpotensi nol itu secara tidak benar. Itu selalu dengan senang hati menganggapnya sebagai nomor yang valid.

Setelah noImplicitNull berubah, kita akan mendapatkan hasil yang sama persis di sini jika kita menggunakan definisi tipe yang sama. Kode akan dikompilasi tanpa keluhan. Kompiler masih akan dengan senang hati berpikir bahwa wordCount selalu mengembalikan angka. Program masih bisa gagal jika kita melewatkan string kosong, sama seperti sebelumnya (kompiler lama tidak akan memperingatkan kita bahwa nomor yang dikembalikan mungkin nol, dan yang baru juga tidak akan mempercayai definisi tipe). Dan jika kita menginginkan perilaku yang sama, kita dapat menyimpannya tanpa mengubah apa pun dalam kode kita. (1)

Namun sekarang kita akan _dapat_ melakukan yang lebih baik: kita akan dapat menulis definisi tipe yang ditingkatkan untuk wordCount:

declare function wordCount(s:string):string|null;

dan dapatkan peringatan kompiler setiap kali kami mencoba menggunakan wordCount tanpa memeriksa nilai balik nol.

Bagian terbaiknya adalah peningkatan ini sepenuhnya opsional. Kami dapat menyimpan semua jenis deklarasi sebagaimana adanya dan mendapatkan perilaku yang hampir sama persis seperti sekarang. Deklarasi tipe tidak akan menjadi lebih buruk (2) - mereka hanya dapat ditingkatkan agar lebih tepat dalam kasus di mana kami merasa perlu.


(1): sudah ada peningkatan di sini bahkan tanpa membuat definisi tipe menjadi lebih baik. dengan definisi tipe baru, Anda tidak akan dapat secara tidak sengaja meneruskan null ke sumWordcounts dan mendapatkan pengecualian pointer nol ketika fungsi wordCount mencoba ke .split() null itu.

sumWordcounts(null, 'a'); // error after the change

(2): oke, itu bohong. Deklarasi tipe akan menjadi lebih buruk untuk sebagian kecil fungsi: yang menggunakan argumen nullable yang bukan argumen opsional.

Semuanya akan tetap baik-baik saja untuk argumen opsional:

declare function f(a: string, b?:string); 

tetapi tidak untuk argumen yang tidak opsional dan dapat berupa null

declare function f(a: string, b:string); // a can actually be null

Saya berpendapat bahwa fungsi-fungsi itu cukup langka, dan perbaikan yang diperlukan akan minimal.

@spion

Perbaikan itu pasti berpengaruh. Itu membuat perilaku dengan dan tanpa bendera konsisten.

Dengan cara apa, dapatkah Anda memberikan contoh lengkapnya? Anda juga mengatakan:

Dalam kedua kasus, kelebihan beban ke-2 akan digunakan

Yang, bagi saya, menyiratkan bahwa perbaikan tidak berpengaruh?

Program ini lewat di bawah kedua kompiler: bahkan jika null implisit dinonaktifkan. Kode ini benar-benar kompatibel ke belakang.

Ya itu mengkompilasi tanpa kesalahan dalam kedua kasus, tetapi tidak untuk hasil yang sama. sumWordCounts() akan diketik sebagai number! dalam satu kasus dan number? dalam kasus kedua. Mengubah semantik kode dengan sakelar sangat _tidak_ diinginkan. Seperti yang ditunjukkan sebelumnya, perubahan ini kemudian dapat memiliki efek global, misalnya pada resolusi kelebihan beban.

Kompiler baru juga akan mengasumsikan bahwa nilainya tidak nol ketika Anda mencoba menggunakannya. Tidak ada alasan untuk percaya sebaliknya

Tidak! Inilah intinya: Saya ingin kompiler memberikan kesalahan pada saya ketika saya menggunakan nilai yang berpotensi nol. Jika "berasumsi" bukan nol seperti yang saya lakukan ketika saya membuat kode, maka fitur ini tidak berguna!

Perubahan perilaku hanya berlaku saat menetapkan nol (atau membiarkan variabel tidak diinisialisasi) dan kemudian mencoba meneruskan nilai tersebut ke fungsi seperti di atas.

Tidak yakin saya mengerti, karena "fungsi seperti di atas" sebenarnya menerima nol ...

Membaca akhir komentar terakhir Anda, saya mendapat kesan bahwa kami tidak akan mendapatkan analisis nol statis nyata sampai kami mengaktifkan sakelar, yang tidak dapat Anda lakukan sampai _semua_ definisi perpustakaan Anda diperbaiki. :(

Secara umum sulit untuk sepenuhnya memahami semua kasus dan bagaimana semuanya bekerja hanya dengan kata-kata. Beberapa contoh praktis akan sangat membantu.

Berikut adalah contoh dan bagaimana saya ingin kompiler berperilaku:

Asumsikan perpustakaan dengan fungsi yes(): string! yang tidak pernah mengembalikan null dan no(): string? yang dapat mengembalikan null.

Definisi .d.ts adalah:

declare function yes(): string;
declare function no(): string;

Saya ingin dapat membuat proyek besar di TS yang mendapat manfaat dari nol yang diperiksa secara statis dan menggunakan definisi perpustakaan yang ada. Selain itu, saya ingin dapat melakukan transisi _secara progresif_ ke situasi di mana semua perpustakaan diperbarui dengan informasi nullness yang benar.

Menggunakan pengubah bukan nol yang diusulkan ! atas, saya dapat melakukannya:

function x(s: string!) {  // inferred : string, could be explicit if we want to
  return s.length === 0 ? null : s;  // no error here as s is declared not null
}

x(no());  // error: x called with a possibly null parameter
y(<!>yes());  // no error because of not null cast. When .d.ts is updated the cast can be dropped.

Bagaimana itu akan berhasil dengan ide Anda?

Utas ini adalah diskusi yang terlalu panjang (130 komentar!), yang membuat sangat sulit untuk mengikuti ide, saran, dan masalah yang disebutkan oleh semua orang.

Saya menyarankan agar kita membuat intisari eksternal dengan proposal kita, termasuk sintaks yang disarankan, apa yang diterima oleh TS, apa yang salah dan sebagainya.

@spion karena ide Anda melibatkan flag kompiler, untuk setiap bagian kode Anda harus mendokumentasikan perilaku TS dengan flag yang disetel dan tidak disetel.

Inilah inti dari penanda !T not-null:
https://Gist.github.com/jods4/cb31547f972f8c6bbc8b

Hampir sama dengan komentar saya di atas, dengan perbedaan sebagai berikut:

  • Saya perhatikan bahwa aspek parameter fungsi 'bukan-null' dapat disimpulkan dengan aman oleh kompiler (terima kasih kepada @spion untuk wawasan itu).
  • Saya menyertakan komentar @Griffork , terutama tentang menyimpulkan nilai pengembalian dan saya mencoba !T alih-alih T! .
  • Saya menambahkan beberapa bagian, misalnya tentang perilaku konstruktor.

Jangan ragu untuk berkomentar, bercabang, dan membuat proposal alternatif ( ?T ).
Mari kita coba untuk memajukan ini.

@jods4

Dalam kedua kasus, kelebihan beban ke-2 akan digunakan

Yang, bagi saya, menyiratkan bahwa perbaikan tidak berpengaruh?

  1. Ini menyiratkan bahwa resolusi kelebihan diubah untuk membuat semantik bahasa konsisten untuk kedua flag.

Ya itu mengkompilasi tanpa kesalahan dalam kedua kasus, tetapi tidak untuk hasil yang sama. sumWordCounts() akan diketik sebagai angka! dalam satu kasus dan nomor? di kedua. Mengubah semantik kode dengan sakelar sangat tidak diinginkan. Seperti yang ditunjukkan sebelumnya, perubahan ini kemudian dapat memiliki efek global, misalnya pada resolusi kelebihan beban.

  1. Tidak ada number! dalam proposal saya. Jenisnya hanya number dalam kedua kasus. Yang berarti resolusi kelebihan terus bekerja seperti biasa, kecuali dengan nilai nol, dalam hal ini perilaku baru untuk null eksplisit lebih diutamakan daripada implisit menormalkan semantik dengan cara yang kompatibel ke belakang.

Tidak! Inilah intinya: Saya ingin kompiler memberikan kesalahan pada saya ketika saya menggunakan nilai yang berpotensi nol. Jika "berasumsi" bukan nol seperti yang saya lakukan ketika saya membuat kode, maka fitur ini tidak berguna!

  1. Poin yang saya coba ungkapkan adalah bahwa ada sedikit ketidakcocokan ke belakang dari fitur tersebut (dalam hal deklarasi tipe). Jika Anda tidak menggunakannya, Anda mendapatkan perilaku yang sama persis seperti sebelumnya. Jika Anda ingin menggunakannya untuk menyatakan kemungkinan bahwa nilai tipe null dapat dikembalikan, sekarang Anda bisa.

Seolah-olah kompiler menggunakan tipe string untuk nilai tipe object dan string sebelumnya, mengizinkan semua metode string pada semua objek dan tidak pernah memeriksanya. Sekarang ia akan memiliki tipe terpisah untuk Object , dan Anda dapat mulai menggunakan tipe itu sebagai gantinya untuk menunjukkan bahwa metode string tidak selalu tersedia.

Tidak yakin saya mengerti, karena "fungsi seperti di atas" sebenarnya menerima nol ...

Mari kita lihat fungsi ini:

function sumWordcounts(s1:string, s2:string) {
  return wordCount(s1) + wordCount(s2);
}

Di bawah flag kompiler baru Anda tidak akan dapat memanggilnya dengan nilai null misalnya sumWordCounts(null, null); dan itulah satu-satunya perbedaan. Fungsi itu sendiri akan dikompilasi karena definisi wordCounts mengatakan dibutuhkan string dan mengembalikan angka.

Membaca akhir komentar terakhir Anda, saya mendapat kesan bahwa kami tidak akan mendapatkan analisis nol statis nyata sampai kami mengaktifkan sakelar, yang tidak dapat Anda lakukan sampai semua definisi perpustakaan Anda diperbaiki. :(

Sebagian besar kode tidak benar-benar berurusan dengan nol atau nilai yang tidak ditentukan, selain memeriksa apakah mereka nol/tidak terdefinisi dan melemparkan kesalahan untuk mencegah penyebaran nilai itu di seluruh basis kode ke tempat yang sama sekali tidak terkait, membuatnya sulit untuk men-debug. Ada beberapa fungsi di sana-sini menggunakan argumen opsional, yang dapat dikenali dan mungkin dapat dimodelkan sesuai dengan sakelar baru. Tidak sering fungsi mengembalikan nilai nol seperti contoh saya (yang saya gunakan hanya untuk menjelaskan tentang fitur tersebut, bukan sebagai kasus representatif)

Apa yang saya katakan adalah bahwa sebagian besar definisi tipe akan tetap benar, dan untuk beberapa kasus yang tersisa di mana kita perlu melakukan perbaikan, kita dapat memilih untuk tidak memperbaikinya jika kita menganggap kasus nol tidak penting, atau untuk mengubah jenis yang sesuai, jika kita peduli. Yang sepenuhnya sejalan dengan tujuan TypeScript untuk secara progresif "memutar dial up" untuk mendapatkan jaminan yang lebih kuat kapan pun kita membutuhkannya.


Oke, jadi definisi tipe yang ada adalah

declare function yes(): string;
declare function no(): string;

Dan katakanlah kode Anda ditulis sebelum fitur ditambahkan juga:

function x(s: string) {
  return s.length === 0 ? null : s;
}

Di bawah kompiler baru, perilakunya akan persis sama dengan yang lama

x(no());  // no error
x(yes());  // no error

Kecuali jika Anda mencoba sesuatu yang diketahui oleh kompiler mungkin nol, seperti hasil x()

x(x(no())) // error, x(something) may be null

Anda melihat no() dan Anda dapat memutuskan bahwa kasus yang mengembalikan null jarang terjadi sehingga Anda tidak akan memodelkannya, atau Anda dapat memperbaiki definisi tipe.

Sekarang mari kita lihat apa yang terjadi dengan proposal Anda: setiap baris kode yang bahkan menyentuh pustaka eksternal akan rusak. Fungsi yang memiliki definisi tipe yang benar-benar valid seperti di atas juga rusak. Anda harus memperbarui setiap anotasi argumen di mana-mana dan menambahkan ! agar kompiler berhenti mengeluh, atau menambahkan cek nol di mana-mana.

Intinya adalah bahwa sistem tipe di TypeScript saat ini salah. Ini memiliki interpretasi ganda dari nilai nol:

Ketika kami mencoba untuk menetapkan null ke beberapa variabel bertipe T , atau meneruskan null sebagai argumen di mana tipe T diperlukan, ia bertindak seolah-olah tipenya adalah T|null .

Ketika kami mencoba menggunakan nilai bertipe T , ia bertindak seolah-olah tipenya adalah T dan tidak ada kemungkinan null.

Proposal Anda menyarankan untuk memperlakukan semua tipe normal T sebagai T|null selalu; saya menyarankan untuk memperlakukan mereka hanya sebagai T . Keduanya mengubah semantik. Keduanya membuat kompiler berperilaku "benar"

Argumen saya adalah bahwa varian "hanya T " jauh lebih tidak menyakitkan daripada yang terlihat dan selaras dengan sebagian besar kode.

sunting: Saya baru menyadari bahwa proposal Anda mungkin tetap memperlakukan tipe tanpa "!" atau "?" cara yang sama seperti sebelumnya. Itu kompatibel ke belakang, ya, tetapi banyak pekerjaan untuk mendapatkan manfaat apa pun (karena sebagian besar kode tidak berurusan dengan nilai nol selain memeriksa/melempar.

@spion

1.Ini menyiratkan bahwa resolusi yang berlebihan diubah untuk membuat semantik bahasa konsisten untuk kedua flag.

Anda hanya menyatakan fakta yang sama tetapi masih belum jelas bagi saya kasus mana yang ditangani dan dengan cara apa.
Makanya saya minta contoh konkrit.

1.Tidak ada number! dalam proposal saya. Jenisnya hanya number dalam kedua kasus.

Itu salah, saya tahu sintaksnya berbeda tetapi konsep dasarnya sama. Ketika saya mengatakan number! Saya menekankan jenis angka non-null, yang dalam proposal Anda akan menjadi number . Dan kasus kedua tidak akan menjadi number dalam kasus Anda, tetapi number | null .

Sebagian besar kode tidak benar-benar berurusan dengan nol atau nilai yang tidak ditentukan, selain memeriksa apakah mereka nol/tidak terdefinisi dan melemparkan kesalahan untuk mencegah penyebaran nilai itu di seluruh basis kode ke tempat yang sama sekali tidak terkait, membuatnya sulit untuk men-debug. Ada beberapa fungsi di sana-sini menggunakan argumen opsional, yang dapat dikenali dan mungkin dapat dimodelkan sesuai dengan sakelar baru. Tidak sering fungsi mengembalikan nilai nol seperti contoh saya (yang saya gunakan hanya untuk menjelaskan tentang fitur tersebut, bukan sebagai kasus representatif)

Saya pikir deskripsi seperti ini membuat _banyak_ asumsi dan jalan pintas. Itu sebabnya saya berpikir bahwa untuk membuat kemajuan kita perlu beralih ke contoh yang lebih konkret dengan kode, sintaks dan penjelasan tentang cara kerja sistem. Kamu bilang:

Sebagian besar kode tidak benar-benar berurusan dengan nol atau nilai yang tidak ditentukan, selain memeriksa apakah mereka nol/tidak terdefinisi dan menimbulkan kesalahan

Sangat bisa diperdebatkan.

Ada beberapa fungsi di sana-sini menggunakan argumen opsional.

Bahkan lebih diperdebatkan. Pustaka JS penuh dengan contoh seperti itu.

, yang dapat dikenali dan mungkin dapat dimodelkan sesuai dengan sakelar baru

Buat proposisi yang lebih konkrit karena ini adalah jalan pintas yang besar. Saya pikir saya dapat melihat ke mana arah ini menuju kode JS yang sebenarnya, tetapi bagaimana Anda "mengenali" argumen opsional di dalam declare function(x: {}); atau di dalam interface ?

Tidak sering fungsi mengembalikan nilai nol seperti contoh saya.

Sekali lagi sangat diperdebatkan. Banyak fungsi mengembalikan nilai null atau undefined : find (bila item tidak ditemukan), getError (bila tidak ada kesalahan) dan seterusnya ... Jika Anda ingin lebih banyak contoh, lihat saja API browser standar, Anda akan menemukan _plenty_.

Apa yang saya katakan adalah bahwa sebagian besar definisi tipe akan tetap benar.

Seperti yang Anda tahu dari komentar saya sebelumnya, saya tidak yakin akan hal itu.

dan untuk beberapa kasus yang tersisa di mana kami perlu melakukan perbaikan, kami dapat memilih untuk tidak memperbaikinya jika kami menganggap kasus nol tidak penting, atau untuk mengubah jenis yang sesuai, jika kami peduli. Yang sepenuhnya sejalan dengan tujuan TypeScript untuk secara progresif "memutar dial up" untuk mendapatkan jaminan yang lebih kuat kapan pun kita membutuhkannya.

Pada titik ini pernyataan ini tampaknya tidak sepele bagi saya. Bisakah Anda memberikan contoh konkret tentang cara kerjanya? Terutama bagian _progresif_.

Sekarang mari kita lihat apa yang terjadi dengan proposal Anda: setiap baris kode yang bahkan menyentuh pustaka eksternal akan rusak. Fungsi yang memiliki definisi tipe yang benar-benar valid seperti di atas juga rusak. Anda harus memperbarui setiap anotasi argumen di mana-mana dan menambahkan ! agar kompiler berhenti mengeluh, atau menambahkan cek nol di mana-mana.

Ini hampir sepenuhnya salah. Silakan baca inti yang saya tulis dengan cermat. Anda akan melihat bahwa sebenarnya beberapa anotasi bukan nol diperlukan. Tapi ada _satu_ perubahan yang melanggar, ya.

Asumsikan Anda mengambil basis kode yang ada yang menggunakan definisi pustaka dan mencoba mengompilasinya dengan proposal saya tanpa mengubah kode apa pun:

  • Anda akan mendapatkan banyak manfaat analisis nol, bahkan tanpa anotasi (hampir sama dengan cara Anda mendapatkannya di kompiler Flow).
  • Satu hal akan rusak: menggunakan nilai kembalian fungsi perpustakaan yang Anda tahu tidak mengembalikan nol.
declare function f(): string; // existing declaration, but f will never return null.
var x = f();
x.toUpperCase();  // error, possibly null reference.

Perbaikan jangka panjang adalah memperbarui definisi perpustakaan agar lebih akurat: declare function f(): !string;
Perbaikan jangka pendek adalah menambahkan pemeran bukan-null: var x = <!>f(); .
Dan untuk membantu proyek besar dengan banyak dependensi ditingkatkan dengan lebih mudah, saya sarankan menambahkan flag kompiler yang mirip dengan "tidak ada yang tersirat": "perlakukan kemungkinan referensi nol sebagai peringatan". Ini berarti Anda dapat menggunakan kompiler baru dan menunggu hingga perpustakaan memperbarui definisi. Catatan: tidak seperti proposisi Anda, flag ini tidak mengubah semantik kompiler. Itu hanya mengubah apa yang dilaporkan sebagai kesalahan.

Seperti yang saya katakan, saya tidak yakin kita membuat kemajuan dalam debat ini. Saya sarankan Anda melihat inti saya dan membuatnya dengan ide, contoh kode, dan perilaku Anda sendiri di bawah kedua status bendera Anda. Karena kita semua adalah pembuat kode, saya pikir ini akan lebih jelas. Juga ada banyak kasus tepi untuk dipertimbangkan. Setelah melakukan itu, saya memiliki banyak pertanyaan untuk diajukan untuk situasi khusus, tetapi tidak ada gunanya mendiskusikan konsep dan ide tanpa definisi yang tepat.

Ada terlalu banyak diskusi tentang berbagai hal yang terjadi di 130+ masalah komentar ini.

Saya sarankan kita melanjutkan diskusi _general_ di sini.

Untuk diskusi proposal konkret yang mungkin menerapkan tipe not-null di TS, saya sarankan kita membuka masalah baru, masing-masing tentang proposal desain tunggal. Saya membuat #1800 untuk membahas sintaks !T .

@spion Saya sarankan Anda membuat masalah untuk desain Anda juga.

Banyak orang menginginkan tipe bukan-null. Sangat mudah dalam bahasa baru (misalnya Rust) tetapi sangat sulit untuk menyesuaikan kembali dalam bahasa yang ada (orang-orang telah meminta referensi bukan-null di .net untuk waktu yang lama -- dan saya rasa kita tidak akan pernah mendapatkannya mereka). Melihat komentar di sini menunjukkan bahwa itu bukan masalah sepele.
Flow telah meyakinkan saya ini bisa dilakukan untuk TS, mari kita coba mewujudkannya!

@ jods4 Maaf untuk mengatakannya, tetapi proposal Anda tidak ada hubungannya dengan tipe non-null, ini lebih terkait dengan semacam analisis aliran kontrol, hampir tidak dapat diprediksi, dan itu benar-benar merusak retrokompatibilitas (sebenarnya sebagian besar kode yang valid 1,4 ts akan gagal di bawah aturan yang dijelaskan dalam Intisari Anda).
Saya setuju dengan fakta bahwa diskusi tidak mengarah ke mana-mana, dan 130 komentar+ mungkin terlalu berlebihan. Tapi mungkin karena ada 2 kelompok orang yang berdiskusi disini :

  • mereka yang berpikir bahwa tipe non-null harus default dan ingin menemukan cara untuk mewujudkannya (melalui flag atau mekanisme lainnya)
  • mereka yang tidak ingin tipe non-null dan hanya mencoba menghindari pengenalan mereka, atau mendorong mereka ke dalam sintaks baru.

2 kelompok orang itu sudah lama berhenti mendengarkan argumen satu sama lain, dan pada akhirnya yang kita butuhkan adalah sudut pandang tim TS, sampai saat itu saya pribadi akan berhenti mencoba membahas lebih lanjut tentang masalah ini.

@fdecampredon
Mengenai bagian tentang inti saya, saya menyalin-menempelkan argumen Anda di #1800 dan jika Anda benar-benar tertarik, kita dapat mendiskusikan mengapa itu adalah desain yang buruk di sana. Saya tidak begitu mengerti sudut pandang Anda, tetapi saya tidak ingin mulai membahasnya di sini, karena itulah saya membuat #1800 sejak awal.

Mengenai utas ini, saya setuju dengan Anda bahwa diskusi tidak akan kemana-mana ...

Seperti yang Anda tahu dari komentar saya sebelumnya, saya tidak yakin akan hal itu.

Yah saya benar-benar tidak tahu bagaimana menjelaskannya dengan cara lain - saya sudah menjelaskannya beberapa kali. Biarkan saya mencoba sekali lagi.

Saat ini kamu tidak bisa mengungkapkannya

declare function err():string;

mengembalikan nol. Tidak mungkin, karena TypeScript akan selalu dengan senang hati membiarkan Anda melakukan err().split(' ') . Anda tidak dapat mengatakan "itu mungkin tidak valid".

Setelah perubahan ini, Anda akan dapat, dengan mengubah string menjadi ?string atau string|null

Tetapi jika tidak, _Anda tidak kehilangan apa pun_ yang Anda miliki sebelum perubahan:

Anda tidak memiliki pemeriksaan nol sebelum perubahan, dan Anda tidak memiliki setelahnya (jika Anda tidak melakukan apa pun). Saya berpendapat bahwa ini sebagian besar kompatibel ke belakang. Masih harus didemonstrasikan pada basis kode yang lebih besar, tentu saja.

Perbedaannya adalah: Anda tidak dapat memaksa pemeriksaan nol sebelum perubahan, tetapi Anda _dapat_ setelahnya (secara progresif, pertama pada definisi yang paling Anda pedulikan, lalu di tempat lain)

@fdecampredon Saya masih membahas topik ini karena saya merasa ada banyak kesalahpahaman tentang masalah ini dan tentang seberapa "sulit" dan "ketat" untuk memanfaatkan fitur ini. Saya menduga bahwa banyak potensi masalah yang sangat berlebihan. Kita mungkin harus mencoba dan mengimplementasikan varian dasar --noImplicitNull di fork - Saya tidak yakin saya akan dapat menemukan waktu di periode berikutnya, tetapi jika saya melakukannya, saya akan mencobanya , Kedengarannya seperti proyek yang menyenangkan.

@spion , saya

Dan @fdecampredon satu-satunya permintaan saya adalah untuk tidak melakukan perubahan besar, terutama di mana kesalahan ditampilkan di tempat yang salah.

Saya menduga @spion dalam contoh Anda saat ini, Anda dapat melanjutkan pengkodean TypeScript dengan cara yang sama seperti yang kita lakukan sekarang, jika Anda berasumsi bahwa variabel lokal jika tidak ditetapkan dikonversi ke parameter nullable dan fungsi dengan ? juga bisa nol. Yah, kecuali untuk contoh yang Anda miliki sebelumnya dengan parameter fungsi awal yang dapat dibatalkan.

@spion
Apa yang saya pahami dari komentar terakhir Anda sangat berbeda dari apa yang saya pahami sebelumnya ...

Jadi : string adalah "tipe string lama Anda yang tidak akan pernah diperiksa untuk nullness, seperti <any> tidak pernah diperiksa untuk kebenaran tipe".

Sintaks baru : string | null kompatibel ke belakang karena tidak ada sebelumnya dan mengakses jenis ini tanpa pemeriksaan nol merupakan kesalahan.

Itu menarik dan sulit untuk memahami semua implikasinya. Aku harus memikirkannya untuk beberapa waktu.

Pertanyaan pertama yang muncul di benak Anda adalah bagaimana Anda memberikan batasan pada nilai input (misalnya parameter fungsi):

declare f(x: string): void;
f(null);

Apakah ini baik-baik saja atau kesalahan? Jika tidak apa-apa, bagaimana saya bisa membuat kesalahan.

_Saya masih berpikir ada ide dalam diskusi ini yang hilang, tidakkah Anda ingin membuka masalah baru dengan ide Anda ini? Kita bisa mendiskusikannya disana._

@jods itu akan menjadi kesalahan:

declare f(x: string): void; //string must not be null.
declare f(x: string|null): void; //string may be null (not sure about undefined here).
declare f(x?: string): void; //I assume x may be null or undefined.

@ jods4 Saya tidak berpikir kita perlu membuat banyak masalah pada satu topik, maka segalanya akan menjadi lebih sulit untuk dilacak karena Anda harus pergi ke 10 proposal berbeda hanya untuk melihat apakah ada yang terjadi / berlangganan 10 yang berbeda . Dan mereka semua harus memiliki tautan ke proposal lain dalam perkenalan mereka sehingga setiap orang yang mencari non-nullability tidak hanya melihat & memilih satu.

@fdecampredon Saya tahu ada banyak posting dan banyak hal yang belum terselesaikan, tetapi itu tidak berarti kita harus berhenti mencoba mencari solusi yang disukai semua orang. Jika Anda tidak menyukai kenyataan bahwa kami masih senang membicarakan hal ini dan menjelaskan diri kami sendiri, maka Anda dipersilakan untuk berhenti berlangganan topik ini.

@Griffork
Tapi hanya setelah kamu menyalakan bendera ajaib, kan?
Jadi dengan flag off Anda memiliki kode yang tidak dicentang, kecuali tipe nullables baru yang tidak ada sebelumnya; lalu ketika Anda menyalakan bendera, semua jenis tidak dapat dibatalkan dan dicentang?

Saya pikir hasil akhirnya mungkin adalah solusi terbaik. _Tapi itu melanggar hampir semua kode yang ada saat ini._ Bayangkan bahwa saya memiliki 300 ribu baris kode... Saya harus menambahkan anotasi nullable di mana pun jenis sebenarnya nullable sebelum saya dapat mengaktifkan flag. Itu masalah besar.

Jika saya mengerti dengan benar, ini bagus untuk proyek baru (setelah .d.ts diperbarui) tetapi sangat menyakitkan untuk kode yang ada.

@Griffork masalah saya dengan satu masalah ini dan utas komentarnya yang panjang adalah sangat sulit untuk melakukan diskusi terfokus pada satu proposal. Berlangganan 3 atau 4 masalah bukanlah masalah besar, dan Anda memiliki konteks yang baik di setiap masalah.

Proposal asli @spion tidak memiliki bendera ajaib. Itu adalah perubahan inti pada cara kerja TypeScript.

Ya, tetapi seseorang yang masuk ke dalam diskusi ini harus membaca semua yang ada sebelumnya. Jika Anda berpikir bahwa orang tidak boleh membaca semua yang datang sebelumnya (dan karena itu berpotensi mengusulkan proposal yang sama) _maka_ kita dapat membaginya; tapi saya tidak setuju. Intinya berada di satu tempat adalah bahwa meskipun kami berbicara tentang detail yang berbeda atau kemungkinan implementasi yang berbeda, kami semua membicarakan hal yang sama .
Topik yang sama, bukan topik yang berbeda.
Semua percakapan terpusat, dan tidak ada yang terkubur dengan tidak ditanggapi.

Anda juga tidak dapat benar-benar memecah percakapan ini dengan detail implementasi, karena banyak proposal akan sering menyentuh banyak detail berbeda yang saat ini sedang dibahas.

Memisahkan proposal satu sama lain berarti bahwa jika seseorang menemukan kekurangan, mereka harus pergi ke beberapa utas berbeda dan menulisnya. Orang-orang tidak belajar dari kesalahan satu sama lain saat itu, dan orang-orang memiliki kemampuan (yang mereka inginkan) untuk mengasingkan diri ke satu diskusi dan mengulangi kesalahan yang diuraikan dalam diskusi lain.

Intinya adalah: Memisahkan percakapan akan menyebabkan orang harus mengulang banyak posting karena pemirsa yang terlalu malas untuk membaca semua yang ada di semua posting tertaut lainnya (dengan asumsi bahwa semua posting yang relevan menautkan semua posting lainnya ).

Juga @ jods4 itu hanya perubahan besar ketika Anda menetapkan nol untuk sesuatu.

Imo jauh lebih sedikit dari perubahan yang melanggar daripada proposal Anda, yang akan memiliki setiap definisi tunggal yang seharusnya tidak memiliki nol dapat ditetapkan untuk itu perlu diperiksa/disentuh sebelum Anda dapat menggunakannya dan mendapatkan manfaat dari jenis yang tidak dapat dibatalkan.

Berikut adalah contoh seperti apa proses "peningkatan" pada proposal @jods4 's dan @spion ' (dengan asumsi semua tanda yang diperlukan diaktifkan):

function a(arg1: string): string;
a("mystr").toLowerCase(); //errors in <strong i="11">@jods4</strong>'s proposal because a may return null.
a("mystr").toLowerCase(); //fine in <strong i="12">@spion</strong>'s proposal.

a(null).toLowerCase(); //fine in <strong i="13">@jods4</strong>'s proposal because a may accept null.
a(null).toLowerCase(); //errors in <strong i="14">@spion</strong>'s proposal since a doesn't accept null.

Pada akhirnya mereka berdua melanggar perubahan. Keduanya sama menyakitkannya untuk di-debug, dengan kesalahan yang terjadi pada penggunaan variabel alih-alih deklarasi variabel. Perbedaannya adalah bahwa satu kesalahan ketika null ditetapkan ke sesuatu (@spion), dan kesalahan lainnya ketika nol tidak ditetapkan ke sesuatu (@jods4). Secara pribadi saya menetapkan nol untuk hal-hal jauh lebih jarang daripada ketika saya tidak menetapkan nol untuk hal-hal, jadi @spion kurang dari perubahan bagi saya untuk bekerja dengan.

Hal lain yang saya suka:

  • Jenis variabel tidak berubah pada saya secara dinamis. Jika saya menginginkannya, saya akan menggunakan flow:
var s: string;
s.toUpperCase(); // error
var t = (s || "test").toUpperCase(); // ok. Inferred t: string
yes(t);  // ok
t = no();
yes(t);  // error

Secara pribadi saya ingin kesalahan jika saya memanggil no(), bukan perubahan tipe implisit.

  • Nullables yang berisi kata null, daripada beberapa simbol, lebih mudah dibaca dan kurang diingat. Betulkah ! harus digunakan untuk "tidak" bukan "tidak-null". Saya benci harus mengingat apa yang dilakukan simbol, sama seperti saya membenci akronim, saya tidak pernah dapat mengingatnya dan mereka secara signifikan memperlambat kecepatan kerja saya.

@jods4 Saya tahu bahwa percakapan saat ini sulit untuk

@Griffork jujur ​​saya membaca semuanya, tapi saya ragu banyak orang akan melakukannya. Itu masuk ke banyak sub-diskusi yang berbeda dan tidak terkait dan saya kehilangan jejak lebih dari sekali. Tapi jangan membuat utas ini lebih berat dari yang sudah ada dan berhenti di situ.

Ini adalah perubahan yang melanggar setiap kali Anda memiliki sesuatu yang dapat dibatalkan (yang menyiratkan itu akan diberikan nol di beberapa titik, jika tidak, itu tidak akan benar-benar dapat dibatalkan). Kesalahan akan dilaporkan di mana null ditetapkan / diteruskan ke suatu fungsi, tetapi perbaikannya ada pada deklarasi. Ini termasuk deklarasi fungsi, deklarasi variabel, deklarasi bidang di kelas... Banyak hal yang berpotensi ditinjau dan dimodifikasi.

@jods4 sub-diskusi yang tidak

@spion Saya pikir saya memiliki solusi yang lebih sederhana untuk masalah @RyanCavanaugh dengan flag '-nonImplicitNull', idenya cukup sederhana, kita hanya harus tidak mengizinkan ?type type|null atau type|undefined tanpa bendera ini.

@fdecampredon maka Anda memerlukan dua versi dari setiap perpustakaan yang tersedia dan terus diperbarui.
Itu juga akan mematikan proses kami di mana kami memiliki satu proyek yang tidak ketat untuk mencoba kode dan menjalankan tes yang bergantung pada kode proyek yang lebih ketat.

@jods4

declare f(x: string): void;
f(null);

Saat menggunakan flag --noImplicitNull diusulkan, ya, itu akan menjadi kesalahan.

Saya belum merasa nyaman membuka proposal resmi, saya ingin setidaknya melakukan beberapa eksperimen pemikiran lagi atau mungkin mencoba menerapkan versi ide yang lebih sederhana dalam fork.

Saya perhatikan bahwa proposal !T sebenarnya akan memecahkan banyak kode juga. Bukan karena semantik dari tipe yang ada berubah tetapi karena analisis baru akan menimbulkan kesalahan di banyak tempat.

Jadi saya menutupnya. Jika kita memecahkan banyak kode, saya lebih suka pendekatan lain, tampaknya lebih bersih.

Saya sekarang yakin bahwa tidak ada cara untuk memperkenalkan fitur ini tanpa melanggar banyak kode yang ada. Ini masih bagus untuk semua kode yang belum ditulis dan mungkin kode lama dapat terus dikompilasi tanpa manfaat pemeriksaan nol oleh kompiler -- persis seperti sekarang ini.

Saya ingin tahu apa sikap tim TS resmi tentang aspek melanggar ini vs manfaatnya.

Sangat tidak mungkin kami akan mengambil jeda sebesar "membuat semuanya tidak dapat dibatalkan secara default". Jika ada beberapa proposal yang _only_ mengeluarkan kesalahan baru dalam kasus yang jelas-jelas merupakan bug, itu mungkin ada di atas meja, tetapi proposal seperti itu sulit didapat :wink:

Jika cakupan dampak lebih kecil -- perubahan dalam resolusi kelebihan saat meneruskan null sebagai argumen, misalnya, di mana perilaku yang tepat belum tentu segera terlihat, itu bisa menjadi cerita yang berbeda.

:+1: orang waras terdeteksi: @RyanCavanaugh

Bagaimana kalau tidak ada perubahan pada cara kerja yang ada, tetapi pengenalan tipe nol untuk serikat pekerja, yang memaksa Anda untuk menjaga variabel sebelum menggunakannya (atau menetapkannya ke variabel yang diketik non-null)? Perilaku default masih memungkinkan nol untuk ditugaskan ke variabel yang diketik secara normal serta variabel yang diketik dengan nol, namun fungsi yang dapat mengembalikan nol jika ditandai dengan benar akan memaksa pemeran.

Dengan cara ini tidak ada perubahan yang melanggar, namun dengan praktik yang baik Anda masih dapat memiliki banyak bug yang diambil oleh sistem pengetikan dengan pengetikan yang disimpulkan dan praktik pengkodean yang baik.

Kemudian (mungkin nanti?) Anda dapat menambahkan flag --noImplicitNullCast yang mencegah null untuk ditetapkan ke variabel yang tidak memiliki null sebagai bagian dari pengetikannya (ini kurang lebih berfungsi seperti saran @spion dengan flag diaktifkan).

Saya membayangkan ini tidak akan terlalu berbeda dengan semua pengetikan normal dan penambahan flag --noImplicitAny.

@Griffork
Paruh pertama paket (tambahkan tipe null dan melarang dereference tanpa pelindung) tidak 100% kompatibel. Anda akan memecahkan lebih banyak kode daripada yang Anda pikirkan. Pertimbangkan ini:

Dengan kemampuan baru ini, banyak lib def akan diperbarui, termasuk yang sudah ada di dalamnya. Katakanlah mereka mengubah beberapa tanda tangan untuk secara eksplisit memasukkan null sebagai kemungkinan nilai kembalian. Beberapa contoh:

interface Array<T> {
  find(predicate: (T) => bool) : T | null;
  pop() : T | null;
}
interface Storage {
  getItem(key: string) : any | null;
}

Ada banyak api seperti itu: find mengembalikan undefined jika tidak ada elemen array yang cocok dengan predikat, pop mengembalikan undefined jika array kosong, localStorage.getItem atau sessionStorage.getItem keduanya mengembalikan null jika kunci tidak ditemukan.

Kode berikut adalah sah dan dikompilasi dengan sempurna sebelumnya. Sekarang akan rusak dengan kesalahan:

var xs: string[];
if (xs.length > 0) return xs.pop().trim();  // error xs.pop() may be undefined (false positive)

var items : { id: number }[];
var selectedId : number;
// Assume we are sure selectedId is amongst items
var selectedItem = items.find(x => x.id === selectedId);
selectedItem.toString(); // error selectedItem may be undefined (false positive)

Ide yang sama jika Anda mengambil sesuatu dari localStorage yang Anda tahu ada di sana. Ada banyak kode seperti itu di sekitar dan sekarang memerlukan pemeran (atau sintaks baru) untuk mengkompilasi dan memberi tahu kompiler: anggap ini bukan nol, saya tahu apa yang saya lakukan.

Itu akan mencakup beberapa bug yang sebenarnya, tentu saja. Tapi itu juga akan mencakup banyak positif palsu dan ini melanggar perubahan. Dalam 100K+ LOC itu bukan peningkatan kompiler yang sepele.

Ini mungkin berarti bahwa satu-satunya pendekatan yang akan berhasil adalah: kode baru yang ditulis dari awal atau kode lama yang dimigrasikan memanfaatkan sepenuhnya pemeriksaan nol; kode warisan tidak memiliki manfaat sama sekali. Ini didorong oleh opsi kompiler (--enableNullAnalysis) dan tanpa jalur migrasi "transisi" atau "progresif".

Baiklah. Tetapi Anda selalu dapat membekukan lib.d.ts untuk suatu proyek atau mengambil yang lama.
Pengetikan baru akan merusak kode lama, itu akan tetap terjadi. Hampir semua pengetikan baru saat diperkenalkan mengharuskan kode lama diperbarui dalam beberapa cara atau lainnya agar berfungsi dengan benar (mis. serikat pekerja, obat generik).
Perubahan ini berarti bahwa mereka yang ingin mengabaikan pembaruan atau membekukan definisi perpustakaan/kode mereka tidak akan menderita.
Secara teoritis sebagian besar kode yang menggunakan fungsi-fungsi itu harus tetap dijaga (dan biasanya dalam basis kode besar) - karena ada bug yang mengintai dalam kode Anda jika tidak. Dengan demikian perubahan ini akan lebih mungkin menangkap kesalahan daripada menyebabkan perubahan putus yang meluas.

Sunting
@jods4 mengapa Anda menggunakan kode yang tidak aman sebagai contoh padahal itu adalah jenis kode yang dapat menyebabkan kesalahan yang kami coba deteksi dengan membuat perubahan ini?

Sunting 2
Jika Anda tidak melanggar _some_ (salah / tidak aman) kode, maka tidak ada gunanya untuk membuat perubahan yang harus dilakukan dengan topik ini pernah.

Tapi sekali lagi, karena ini hanya masalah sintaks, semuanya akan tetap dikompilasi dengan benar, itu hanya akan error di semua tempat.

_Pengetikan baru akan merusak kode lama, itu akan tetap terjadi._

Ini bukan kasusnya. Kecuali untuk keadaan luar biasa, kami tidak akan menambahkan fitur baru yang merusak kode yang ada. Generik dan serikat pekerja tidak ditambahkan ke lib.d.ts sedemikian rupa sehingga mengharuskan konsumen lib.d.ts untuk memperbarui basis kode mereka untuk menggunakan versi baru dari kompiler. Ya, orang dapat memilih untuk menggunakan versi perpustakaan yang lebih lama, tetapi kami tidak akan mengambil perubahan bahasa yang merusak banyak kode yang ada (seperti halnya untuk semua bahasa utama). Akan ada pengecualian sesekali untuk ini (https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes) tetapi mereka akan sedikit dan jarang, paling sering jika kami yakin kode yang kami langgar hanya bisa bug.

Tetapi Anda selalu dapat membekukan lib.d.ts untuk sebuah proyek atau mengambil yang lama

Membekukan lib.d.ts (dan .d.ts lainnya yang mungkin saya gunakan BTW) memiliki implikasi yang sangat kuat. Itu berarti saya tidak bisa mendapatkan pembaruan apa pun pada lib yang saya gunakan atau apis HTML baru. Ini bukan sesuatu yang bisa dianggap enteng.

Secara teoritis sebagian besar kode yang menggunakan fungsi-fungsi itu harus tetap dijaga (dan biasanya dalam basis kode besar) - karena ada bug yang mengintai dalam kode Anda jika tidak.

JS memiliki tradisi mengembalikan undefined (atau terkadang null ) dalam banyak kasus yang akan menimbulkan kesalahan dalam bahasa lain. Contohnya adalah Array.prototype.pop . Dalam C# muncul dari tumpukan kosong akan melempar. Jadi bisa dibilang selalu mengembalikan nilai yang valid. Dalam Javascript muncul array kosong mengembalikan undefined . Jika sistem tipe Anda ketat tentang itu, Anda harus menjaganya entah bagaimana.

Saya tahu Anda akan menjawabnya, itulah sebabnya saya menulis contoh yang sah, kode yang berfungsi. Dalam basis kode besar, Anda akan menemukan banyak contoh di mana api dapat mengembalikan null dalam beberapa situasi, tetapi Anda tahu bahwa itu aman dalam kasus spesifik Anda (jadi Anda mengabaikan pemeriksaan keamanan apa pun).

Saya sedang melihat bagian tugas mikro dari beberapa perpustakaan hanya satu jam yang lalu. Bagian utama pada dasarnya bermuara pada ini:

class MicroTasks {
  queue: Array<() => void>;

  flushQueue() {
    while (queue.length > 0) {
      let task = queue.pop();
      task();  // error possible null dereference (not!)
     }
  }
}

Ada banyak kasus seperti ini.

Mengenai Edit 2: ya, semoga perubahan ini _will_ menangkap bug dan menunjukkan kode yang tidak valid. Saya yakin itu berguna dan itulah mengapa saya ingin itu terjadi entah bagaimana. Namun tim TS akan mempertimbangkan perubahan yang melanggar pada kode _valid_. Ada trade-off yang perlu diputuskan di sini.

@danquirk Fair, maka saya kira fitur ini tidak akan pernah bisa diimplementasikan.
@ jods4 Saya mengerti apa yang Anda katakan, tetapi nullable tidak akan pernah dapat diimplementasikan tanpa melanggar kode itu.

Sedih, haruskah saya akhirnya menutup masalah itu?

Mungkin.

Saya pikir jika kita menghindari argumen nullable dan non-nullable...

Masalahnya dapat diatasi (atau dikurangi) dengan cara yang tidak mengganggu dengan serangkaian fitur baru:

  • Perkenalkan simbol ?<type> null-typeguard ?<type>
    Jika suatu tipe dianotasi dengan simbol ini, maka merupakan kesalahan untuk mengaksesnya secara langsung

``` TypeScript
var foo: ?string;

foo.indexOf('s'); // Kesalahan
foo && foo.indexOf('s'); // Oke
```

  • Perkenalkan bendera --nouseofunassignedlocalvar

``` TypeScript
var foo: string;

foo.indexOf('s'); // kesalahan

foo = 'batang';

foo.indexOf('s'); // Oke
```

Catatan sejarah yang menarik : Saat mencoba menemukan masalah terkait untuk ini, saya menemukan masalah lama pada codeplex yang menyebutkan opsi kompiler --cflowu pada Februari-2013. @RyanCavanaugh , saya ingin tahu apa yang terjadi dengan bendera itu?

  • Perkenalkan operator navigasi aman #16

TypeScript var x = { y: { z: null, q: undefined } }; console.log(x?.y?.z?.foo); // Should print 'null'

Dalam kombinasi, fitur-fitur ini akan membantu menjebak lebih banyak kesalahan seputar penggunaan null tanpa benar-benar menyebabkan gangguan saraf bagi semua orang.

@NoelAbrahams :
Proposal pertama Anda pada dasarnya sama persis dengan proposal terakhir saya, Anda hanya menggunakan ? alih-alih |null (baca posting @jods4 tentang masalah dengan memperbarui lib.d.ts dan melanggar perubahan).

Proposal kedua Anda memiliki masalah yang sebelumnya ditangani oleh @RyanCavanaugh (di atas):

Bendera yang mengubah semantik suatu bahasa adalah hal yang berbahaya. Satu masalah adalah bahwa efeknya berpotensi sangat non-lokal:
...[menggunting]...
Satu-satunya hal yang aman untuk dilakukan adalah menjaga semantik penugasan tetap sama dan mengubah apa yang salah vs apa yang tidak bergantung pada bendera, seperti cara kerja noImplicitAny hari ini.

Saya cukup yakin sebuah bendera yang tidak berbeda diusulkan sebelumnya, dan kemudian ditinggalkan karena komentar @RyanCavanaugh .

Proposal ketiga Anda tidak ada hubungannya dengan topik saat ini (ini bukan lagi tentang kesalahan mengetik dan waktu kompilasi, tetapi menangkap kesalahan waktu proses). Alasan saya mengatakan ini adalah karena alasan topik ini dibuat adalah untuk membantu mengurangi kebutuhan untuk melakukan pemeriksaan tidak terdefinisi atau nol pada variabel yang dikenal "aman" (dengan cara mudah untuk melacaknya), bukan menambahkan nol baru atau cek tidak terdefinisi di mana-mana.

Mungkinkah mengimplementasikan salah satu proposal yang disarankan, dan tidak memperbarui lib.d.ts untuk menggunakan nullable (dan lib lainnya)? Kemudian komunitas dapat mempertahankan/menggunakan versi mereka sendiri dengan nullable jika mereka mau?

Sunting:
Secara khusus, semua lib berisi informasi terbaru, tetapi pengetikannya tidak diperbarui untuk memerlukan typeguards.

@RyanCavanaugh Saya akan kembali dan mendiskusikannya jika/ketika saya memiliki tambalan yang menunjukkan bahwa itu bukan perubahan yang besar sama sekali (terutama jika file .d.ts tidak diperbarui).

Definisi tipe

Bagaimanapun, tampaknya masalah ini sia-sia.

Omong-omong, ada kompiler yang dimodifikasi oleh MSR yang melakukan ini antara lain - apakah ada makalah yang tersedia dengan temuan mereka? Sunting: menemukannya: http://research.microsoft.com/apps/pubs/?id=224900 tapi sayangnya, saya tidak yakin itu terkait.

@Griffork , ya, saya yakin hampir semuanya telah dibahas dalam satu atau lain bentuk - mengingat panjangnya diskusi. Ringkasan saya adalah ringkasan praktis tentang mengurangi masalah seputar null dengan memperkenalkan sejumlah fitur terkait.

tidak menambahkan cek nol atau tidak terdefinisi baru di mana-mana

Perangkap variabel lokal yang tidak ditetapkan mengurangi ini dan operator navigasi yang aman adalah fitur ES yang diusulkan, sehingga akan mendarat di TS di beberapa titik.

Saya juga berpikir bahwa kemungkinan ini masuk ke TS rendah ... :(

Satu-satunya solusi yang dapat saya pikirkan adalah menjadikan null safety sebagai flag compiler opt-in. Misalnya, perkenalkan sintaks baru T | null atau ?T tetapi jangan memunculkan kesalahan _any_ kecuali jika proyek ikut serta. Jadi proyek baru mendapat manfaat dan begitu juga proyek lama yang memilih untuk membuat kode mereka kompatibel dengan fitur baru. Basis kode besar yang terlalu besar untuk diadaptasi dengan mudah tidak mendapatkan fitur ini.

Meski begitu, masih ada beberapa masalah yang tersisa untuk membuat fitur ini terbang...

@NoelAbrahams maaf, maksud saya bukan itu saran yang buruk, hanya saja bukan jawaban yang diinginkan dari hasutan diskusi ini.
Saya pasti akan menggunakan fitur itu ketika (/ jika) menjadi asli, kedengarannya sangat bagus.

@jods4 yang memiliki masalah yang sama dengan komentar @RyanCavanaugh yang saya kutip di atas (kesalahan penugasan karena masalah di tempat lain ketika bendera diubah).

Sekali lagi, cara termudah adalah dengan menerapkan salah satu proposal lain (mungkin @spion 's) dan tidak menambahkan tipe baru ke .d.ts'.

@Griffork
Belum tentu, meskipun ini adalah salah satu dari "beberapa masalah" yang tersisa. Fitur tersebut harus dirancang sedemikian rupa sehingga mengaktifkan flag tidak _tidak_ mengubah semantik program apa pun. Seharusnya hanya meningkatkan kesalahan.

Misalnya jika kita menggunakan desain 'not null' !T , saya rasa kita tidak memiliki masalah ini. Tapi ini jauh dari menyelesaikan masalah.

Saya juga harus menunjukkan bahwa sementara T | null adalah perubahan yang melanggar, ?T tidak.

@ jods4 Saya mendapat kesan bahwa "mengubah semantik" termasuk "mengubah kemampuan penugasan", bagaimanapun juga kekhawatiran saya (dan mungkin @RyanCavanaugh ) tentang efek non-lokal masih ada.

@NoelAbrahams bagaimana?
T | null membutuhkan typeguard, ?T membutuhkan typeguard, mereka adalah hal yang sama dengan nama/simbol yang berbeda.
Ya, Anda dapat mengaktifkan keduanya dengan bendera.
Saya gagal melihat perbedaannya?

@Griffork , cara saya melihatnya ?T berarti "jangan mengakses tanpa memeriksa nol". Seseorang bebas menambahkan anotasi ini ke kode baru untuk menegakkan pemeriksaan. Saya memikirkan ini sebagai semacam operator daripada mengetikkan anotasi.

Anotasi T|null memecah kode yang ada karena membuat pernyataan tentang apakah tipe dapat dibatalkan atau tidak secara default.

@jbondc
Menarik... Saya belum melihat terlalu dalam ke proposal Anda, tetapi saya pikir saya akan melakukannya.

Saat ini kekekalan yang didukung oleh kompiler tidak semenarik di JS seperti dalam bahasa lain karena model utasnya melarang data bersama. Tapi pasti ada yang perlu diingat.

@NoelAbrahams
Semua proposal yang memberlakukan nol melanggar perubahan. Bahkan ?T yang Anda jelaskan. Saya menunjukkan beberapa contoh mengapa beberapa komentar di atas.

Itu sebabnya saya pikir satu-satunya jalan keluar jika kita menginginkan ini adalah menjadikannya opsi kompiler, dan memikirkan ini dengan cukup baik sehingga mengaktifkan opsi mengubah kesalahan yang dilaporkan tetapi bukan perilaku program. Tidak yakin apakah itu bisa dilakukan atau tidak.

Aku sebenarnya baik-baik saja dengan ini. Dengan TS 1.5 saya bisa mendapatkan apa yang saya inginkan:

function isVoid(item:any): item is void { return item == null; }
declare externalUnsafeFunction(...):string|void

function test() {
  var res = externalUnsafeFunction(...);
  var words = res.split(' '); // error
  if (!isVoid(res)) {
    var words = res.split(' '); // ok
  }
}

sekarang pemeriksaan isVoid dipaksakan pada nilai pengembalian untuk externalUnsafeFunction atau fungsi atau definisi fungsi lainnya di mana saya menambahkan |void ke tipe pengembalian.

Saya masih tidak dapat mendeklarasikan fungsi yang tidak menerima null/undefined, tetapi mungkin untuk cukup rajin mengingat untuk menginisialisasi variabel lokal dan anggota kelas.

Karena @NoelAbrahams kita sudah membahas bahwa bahkan dengan tipe null, null masih harus diizinkan untuk secara implisit dilemparkan ke tipe lain kecuali jika flag compiler masa depan mengubahnya.

Ini juga berarti bahwa di masa mendatang kita dapat membawa flag compiler yang memungkinkan kita memberi anotasi di mana dan kapan suatu fungsi dapat menerima null.

Dan secara pribadi saya benci ide menggunakan simbol untuk mewakili tipe ketika kita bisa menggunakan kata-kata sebagai gantinya.

@spion itu poin yang bagus sebenarnya. Jika itu berfungsi di TS saat ini maka bisa dibilang semua file d.ts bawaan sudah "salah" , menambahkan tipe nol seperti yang Anda usulkan dengan modifikasi yang saya sarankan tidak akan mengubah apa pun.

Sebenarnya karena itu sudah dapat dicapai, saya akan mengusulkan agar kita mulai menggunakannya minggu ini kepada bos saya.

Saya akan mengingatkan bahwa kita sama sekali tidak melihat T|void sebagai sintaks yang kita takutkan akan rusak di masa mendatang. Ini hampir tidak masuk akal.

@RyanCavanaugh Ini sama omong kosongnya dengan void === undefined (nilai yang dapat ditetapkan).

Mendesah.

Kode kita terlalu besar untuk memulai tergantung pada T|void jika akan segera rusak dan tidak diganti. Dan saya tidak akan bisa meyakinkan pemrogram lain untuk menggunakannya jika setiap minggu bisa rusak.

Oke, @spion jika Anda pernah membuat tambalan, beri tahu saya dan saya akan menjalankannya terhadap basis kode pekerjaan saya. Setidaknya saya bisa memberikan statistik tentang berapa banyak kesalahan yang ditimbulkannya.

@RyanCavanaugh omong kosong, benarkah? Dengan cara apa? Dan apa yang Anda sarankan untuk mengekspresikan tipe nullable yang harus dilarang untuk konsumsi apa pun sebelum pemeriksaan nol/tidak terdefinisi?

Saya benar-benar memiliki banyak perpustakaan yang akan mendapat manfaat dari itu.

@Griffork tidak akan mungkin untuk menghapus void dari serikat sebelum 1.5, bukan tanpa penjaga tipe yang ditentukan pengguna, jadi menggunakan ini hanya mungkin dilakukan setelah 1.5 (jika memungkinkan)

Omong kosong seperti apakah Anda pernah menulis kode ini?

var foo: void;
var bar: void = doStuff();

Tentu saja tidak. Jadi apa artinya menambahkan void ke gabungan jenis yang mungkin? Perhatikan bagian spesifikasi bahasa ini yang telah ada cukup lama di bagian yang menjelaskan The Void Type (3.2.4):

_CATATAN: Kami mungkin mempertimbangkan untuk melarang mendeklarasikan variabel bertipe Void karena tidak memiliki tujuan yang berguna. Namun, karena Void diizinkan sebagai argumen tipe ke tipe atau fungsi generik, tidak mungkin untuk melarang properti atau parameter Void._

Pertanyaan ini:

_Dan apa yang akan Anda sarankan untuk mengekspresikan tipe nullable yang harus dilarang untuk segala jenis konsumsi sebelum pemeriksaan null/undefined?_

adalah apa seluruh utas adalah tentang. Ryan hanya menunjukkan bahwa string|void adalah omong kosong menurut semantik bahasa saat ini dan karenanya tidak masuk akal untuk bergantung pada semantik omong kosong.

Kode yang saya tulis di atas adalah TS 1.5 yang benar-benar valid kan?

Saya akan memberikan contoh yang tentunya bukan omong kosong. Kami memiliki fungsi perpustakaan basis data (nodejs) yang kami sebut get() mirip dengan Linq's Single() yang sayangnya mengembalikan Promise<null> alih-alih melemparkan kesalahan (janji yang ditolak) ketika item tidak ditemukan. Ini digunakan di seluruh basis kode kami di ribuan tempat dan kemungkinan besar tidak akan segera diganti atau hilang. Saya ingin menulis definisi tipe yang memaksa saya dan pengembang lain untuk menggunakan pelindung tipe sebelum menggunakan nilainya, karena kami memiliki lusinan bug yang sulit dilacak yang berasal dari nilai nol yang bergerak jauh ke basis kode sebelum dikonsumsi secara tidak benar.

interface Legacy { 
  get<T>(...):Promise<T|void>
}

function isVoid(val: any): val is void { return val == null; } // also captures undefined

legacy.get(...).then(val => {
  // val.consumeNormally() is a type error here
  if (!isVoid(val)) { 
    val.consumeNormally(); // OK
  }
  else { handle null case }
});

Ini terlihat sangat masuk akal bagi saya. Menambahkan void ke serikat menyebabkan semua operasi pada val menjadi tidak valid, sebagaimana mestinya. Typeguard mempersempit tipe dengan menghapus |void dan meninggalkan bagian T .

Mungkin spesifikasi tidak memperhitungkan implikasi serikat pekerja dengan kekosongan?

Jangan larang variabel kosong! Mereka bekerja sebagai nilai tipe unit dan dapat digunakan
dalam ekspresi (tidak seperti void di C# yang membutuhkan 2 set semuanya: satu
untuk tindakan dan satu fungsi). Silakan tinggalkan kekosongan saja, oke?
Pada 27 Januari 2015 20:54, "Gorgi Kosev" [email protected] menulis:

Kode yang saya tulis di atas adalah TS 1.5 yang benar-benar valid kan?

Saya akan memberikan contoh yang tentunya bukan omong kosong. Kita punya sebuah
fungsi perpustakaan database mirip dengan Linq's Single() yang sayangnya
kembali Janjialih-alih melempar kesalahan (janji yang ditolak) ketika
barang tidak ditemukan. Ini digunakan di seluruh basis kode kami dalam ribuan
tempat dan kemungkinan besar tidak akan diganti atau hilang dalam waktu dekat. Saya ingin menulis
definisi tipe yang memaksa saya dan pengembang lain untuk menggunakan penjaga tipe
sebelum mengkonsumsi nilainya, karena kami memiliki lusinan bug yang sulit dilacak
berasal dari nilai nol yang bergerak jauh ke dalam basis kode sebelum menjadi
dikonsumsi secara tidak benar.

antarmuka Warisan {
Dapatkan(...):Janji
}
function isVoid(val: any): val is void { return val == null; } // juga menangkap undefined

warisan.get(...).lalu(val => {
// val.consumeNormally() adalah kesalahan
if (!isVoid(val)) {
val.consumeBiasanya(); // OKE
}
else { menangani kasus nol }
});

Ini terlihat sangat masuk akal bagi saya. Bisakah Anda menunjukkan bagian mana?
omong kosong?

Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -71767358
.

Apa yang saya katakan adalah bahwa T|void pada dasarnya tidak berarti saat ini karena kita telah memiliki aturan bahwa penyatuan suatu tipe dan subtipenya setara dengan supertipe, misalnya Cat|Animal setara dengan Animal . Memperlakukan void sebagai pengganti untuk nilai null / undefined tidak koheren karena null dan undefined _sudah ada di domain T _ untuk semua T , yang menyiratkan bahwa mereka harus menjadi subtipe dari T

Dengan kata lain, T|null|undefined , jika Anda bisa menulisnya, sudah bisa diciutkan menjadi T . Memperlakukan void sebagai mantra untuk null|undefined adalah salah karena jika memang demikian, kompilator akan menciutkan T|void menjadi T .

Saya pribadi akan membaca ini http://www.typescriptlang.org/Content/TypeScript%20Language%20Specification.pdf

Satu-satunya nilai yang mungkin untuk tipe Void adalah null dan tidak terdefinisi. Tipe Void adalah subtipe dari tipe Any dan supertipe dari tipe Null dan Undefined, tetapi sebaliknya Void tidak terkait dengan semua tipe lainnya.

Sebagai; di T|void , T secara khusus _bukan_ supertipe void karena satu-satunya supertipe (menurut spesifikasi) adalah any .

Sunting: melarangnya adalah masalah yang berbeda.

Sunting2: Baca kembali apa yang Anda katakan, dan itu masuk akal secara harfiah (dalam cara dokumentasi memiliki kata-kata batal), tetapi tidak masuk akal dalam apa yang dianggap sebagai interpretasi umum (atau benar) dari dokumentasi.

@RyanCavanaugh Saya akan tetap menggunakan fitur ini sampai rusak, karena sementara itu kemungkinan akan membantu saya menemukan banyak bug.

Dan saya tidak bisa tidak menunjukkan ironi mengatakan bahwa mengecilkan T|null|undefined menjadi T "masuk akal" sementara keberadaan T|void tidak. (Saya tahu, ini tentang spesifikasinya, tapi tetap saja ...)

@jbondc

Tidak ada tipe struktural statis dalam ECMAScript, sehingga argumen tersebut tidak valid. Dalam ECMAScript, semua nilai bertipe any oleh karena itu apa pun dapat ditetapkan ke apa pun atau diteruskan ke apa pun. Fakta itu seharusnya tidak menyiratkan apa pun tentang sistem tipe statis TypeScript, yang ada untuk memperbaiki yang tidak ada di JS

Agak menyedihkan bahwa saya harus menjelaskan ironi, tapi begini:

  1. anda memiliki nilai yang tidak mendukung metode dan tidak ada properti, null
  2. Anda membuat sistem tipe di mana nilai ini dapat ditetapkan ke variabel jenis apa pun, sebagian besar mendukung berbagai metode dan properti (mis. nomor string atau objek kompleks var s:string = null )
  3. anda membuat tipe void yang menerima nilai itu dengan benar dan tidak mengizinkan metode atau properti, dan sebagai akibatnya T|void tidak mengizinkan metode dan properti.

Klaimnya adalah bahwa (3) adalah omong kosong, sedangkan (2) tidak. Apakah Anda melihat bagian yang ironis di sini? Jika tidak, saya akan mencoba contoh lain:

  1. anda memiliki nilai yang hanya mendukung beberapa metode dan properti {}
  2. Anda membuat sistem tipe di mana nilai ini dapat ditetapkan ke variabel tipe apa pun (mis. ke string angka atau objek kompleks var s:string = {} ) yang memiliki kumpulan metode dan properti yang jauh lebih besar
  3. anda memiliki tipe {} yang menerima nilai {} dan tidak mengizinkan metode atau properti selain beberapa metode bawaan, dan T|{} secara alami tidak mengizinkan metode atau properti selain yang built-in dari {}

Sekarang mana yang tidak masuk akal, (2) atau (3) ?

Apa satu-satunya perbedaan antara dua contoh? Beberapa metode bawaan.

Akhirnya, antarmuka hanyalah deskripsi definisi tipe. Ini bukan antarmuka yang diimplementasikan di mana saja. Ini adalah antarmuka yang disediakan oleh perpustakaan, yang tidak pernah mengembalikan apa pun selain janji, tetapi dapat mengembalikan janji untuk nol. Oleh karena itu keamanannya pasti sangat nyata.

@spion itulah yang saya pikirkan.

Di sini, saya pikir ini akan sedikit memperjelas:
Menurut Spek :


Apa itu kekosongan?

Tipe Void, direferensikan oleh kata kunci void, mewakili ketiadaan nilai dan digunakan sebagai tipe kembalian fungsi tanpa nilai kembalian.

Jadi, void bukan null|undefined.

Satu-satunya nilai yang mungkin untuk tipe Void adalah null dan tidak terdefinisi

Jadi, null dan undefined dapat ditetapkan ke void (karena dapat dialihkan ke hampir semua hal lain).


Null adalah sebuah tipe.

3.2.5 Tipe Null
Tipe Null sesuai dengan tipe primitif JavaScript yang bernama sama dan merupakan tipe dari null
harfiah.
Referensi literal nol satu-satunya nilai dari tipe Null. Tidak mungkin untuk secara langsung mereferensikan tipe Null itu sendiri.


Tidak terdefinisi adalah sebuah tipe.

3.2.6 Jenis Tidak Terdefinisi
Tipe Undefined sesuai dengan tipe primitif JavaScript yang bernama sama dan merupakan tipe literal yang tidak terdefinisi.


Void hanyalah Supertype dari _types_ Null dan Undefined

. Tipe Void adalah subtipe dari tipe Any dan supertipe dari tipe Null dan Undefined, tetapi sebaliknya Void tidak terkait dengan semua tipe lainnya.


Jadi @jbondc menurut Spesifikasi Bahasa TypeScript, null adalah nilai, Null adalah tipe, undefined adalah nilai, Undefined adalah tipe, dan Void (huruf kecil ketika dalam kode) adalah tipe yang merupakan Supertype dari Null dan Tidak terdefinisi tetapi _tidak secara implisit mentransmisikan_ ke tipe lain (kecuali any ). Faktanya, Void secara khusus ditulis untuk memiliki perilaku yang berbeda dengan tipe Null dan Undefined:

Ruang kosong:

Tipe Void adalah subtipe dari tipe Any dan supertipe dari tipe Null dan Undefined, tetapi sebaliknya Void tidak terkait dengan semua tipe lainnya.

Batal:

Tipe Null adalah subtipe dari semua tipe, kecuali tipe Undefined. Ini berarti bahwa null dianggap sebagai nilai yang valid untuk semua tipe primitif, tipe objek, tipe gabungan, dan parameter tipe, termasuk bahkan tipe primitif Number dan Boolean.

Tidak terdefinisi:

Tipe yang tidak terdefinisi adalah subtipe dari semua tipe. Ini berarti bahwa undefined dianggap sebagai nilai yang valid untuk semua tipe primitif, tipe objek, tipe gabungan, dan parameter tipe.

Lebih jelas sekarang?

Apa yang kami minta adalah tipe yang memaksa penjaga untuk nol, dan tipe yang memaksa penjaga untuk tidak terdefinisi. Saya tidak meminta nol atau tidak terdefinisi untuk tidak dapat dialihkan ke jenis lain (itu terlalu melanggar), hanya kemampuan untuk ikut serta dalam mark-up tambahan. Heck, mereka bahkan tidak harus disebut Null atau Undefined (tipe).

@jbondc ini adalah ironi: Tidak peduli apa yang dikatakan kompiler TS, nilai nol dan tidak terdefinisi di JS tidak akan pernah mendukung metode atau properti apa pun (selain melempar pengecualian). Oleh karena itu, membuat mereka dapat ditetapkan ke jenis apa pun adalah omong kosong, hampir tidak masuk akal seperti mengizinkan nilai {} untuk ditetapkan ke string. Tipe void di TS tidak mengandung nilai, _but_ null atau undefined dapat ditetapkan untuk semua orang, dan oleh karena itu dapat ditetapkan ke variabel bertipe void, jadi ada sedikit omong kosong di sana. Dan itulah ironi - bahwa (melalui penjaga tipe dan serikat pekerja) keduanya saat ini bergabung menjadi sesuatu yang benar-benar masuk akal :)

Sesuatu yang serumit kompiler TS ditulis tanpa pelindung, itu sesuatu yang perlu dipikirkan.

Ada aplikasi yang cukup besar yang ditulis dalam PHP atau JAVA, yang tidak membuat bahasa lebih baik atau lebih buruk.
Piramida Mesir telah dibangun tanpa mesin modern, apakah itu berarti bahwa semua yang kami temukan dalam 4500 tahun terakhir adalah omong kosong?

@jbondc Lihat komentar saya di atas dengan daftar masalah dalam kompiler TypeScript yang disebabkan oleh nilai nol dan tidak terdefinisi (minus yang pertama dalam daftar)

@jbondc itu tidak seharusnya menyelesaikan masalah dunia, itu seharusnya menjadi alat lain yang tersedia untuk pengembang.
Itu akan memiliki dampak dan utilitas yang sama dengan kelebihan fungsi, serikat pekerja, dan alias tipe.
Ini adalah sintaks keikutsertaan lainnya yang memungkinkan pengembang mengetikkan nilai dengan lebih akurat.

Salah satu masalah besar yang disebutkan untuk membuat tipe non-nullable adalah apa yang harus dilakukan tentang kode TypeScript yang ada. Ada dua masalah di sini: 1) Membuat kode yang ada berfungsi dengan kompiler baru yang mendukung tipe yang tidak dapat dibatalkan dan 2) Berinteroperasi dengan kode yang belum diperbarui - menghindari rawa gaya Python 2/3

Seharusnya dimungkinkan untuk menyediakan alat yang melakukan penulisan ulang otomatis kode TS yang ada untuk membuatnya dibangun dan untuk kasus terbatas ini, sesuatu yang bekerja jauh lebih baik daripada mengatakan 2to3 lakukan untuk Python.

Mungkin migrasi bertahap bisa terlihat seperti ini:

  1. Perkenalkan dukungan untuk sintaks '?T' untuk menunjukkan kemungkinan tipe null. Awalnya ini tidak berpengaruh pada pengecekan tipe, itu hanya ada untuk dokumentasi.
  2. Sediakan alat yang secara otomatis menulis ulang kode yang ada untuk menggunakan '?T'. Implementasi terbodoh akan mengubah semua penggunaan 'T' menjadi '?T'. Implementasi yang lebih cerdas akan memeriksa apakah kode yang menggunakan tipe tersebut secara implisit menganggapnya bukan nol dan menggunakan 'T' dalam kasus itu. Untuk kode yang mencoba menetapkan nilai tipe '?T' ke parameter atau properti yang mengharapkan 'T', ini dapat membungkus nilai dalam placeholder yang menegaskan (pada waktu kompilasi) bahwa nilainya bukan nol - misalnya let foo: T = expect(possiblyNullFoo) atau let foo:T = possiblyNullFoo!
  3. Terapkan alat ini ke semua definisi tipe yang disediakan dan yang ada di repositori PastiTyped
  4. Perkenalkan peringatan kompiler saat mencoba menetapkan '?T' ke slot yang mengharapkan 'T'.
  5. Biarkan peringatan muncul di alam liar dan verifikasi bahwa porting dapat dikelola dan lihat bagaimana perubahan diterima
  6. Buat penetapan '?T' ke slot yang mengharapkan 'T' sebagai kesalahan, dalam rilis baru utama kompiler.

Mungkin ada baiknya juga memperkenalkan sesuatu yang akan menonaktifkan peringatan untuk modul tertentu untuk memudahkan penggunaan kode yang belum diterapkan alat tersebut.

Dalam komentar sebelumnya, saya berasumsi bahwa tidak akan ada mode untuk memilih nullability dari 'T' biasa untuk menghindari pemisahan bahasa dan bahwa langkah 1-3 akan berguna dengan sendirinya bahkan jika langkah 6 tidak pernah terbukti praktis.

Pikiran acak saya:

  1. Bagian expect() mungkin rumit, perlu dipikirkan dengan matang. Tidak hanya penugasan, tetapi juga antarmuka, generik, atau tipe parameter...
  2. Saya _think_ TS tidak memiliki peringatan apa pun, hanya kesalahan. Jika saya benar, saya tidak yakin mereka akan memperkenalkan peringatan hanya untuk fitur itu.
  3. Bahkan jika banyak orang mengupgrade kode mereka, perusahaan mungkin sangat lambat untuk melakukannya, atau bahkan mungkin tidak pernah ingin melakukannya dalam kasus basis kode besar yang ada. Ini membuat rilis besar itu menjadi perubahan besar, sesuatu yang tidak diinginkan oleh tim TS.

Yang mengatakan, saya masih berpikir bahwa tanda "keikutsertaan" untuk melaporkan kesalahan nol adalah solusi yang dapat diterima. Masalahnya adalah bahwa kode/definisi yang sama harus memiliki perilaku yang sama (dikurangi kesalahan) ketika bendera dimatikan, sesuatu yang saya belum punya waktu untuk memikirkannya.

@jods4 - Untuk bagian expect() , pemikiran saya adalah bahwa di mana pun Anda memiliki ?T kompiler melihat T | undefined sehingga Anda dapat menggunakan kembali aturan untuk menangani jenis serikat sejauh mungkin.

Saya setuju Saya tidak yakin seberapa realistis 'hari bendera' untuk TypeScript. Selain flag 'opt-in', itu juga bisa menjadi flag opt-in yang akhirnya menjadi flag opt-out. Yang penting adalah memiliki arah yang jelas tentang apa itu gaya idiomatik.

Hal lain yang relevan dengan diskusi ini - https://github.com/rwaldron/tc39-notes/blob/master/es6/2015-01/JSExperimentalDirections.pdf membahas penyelidikan oleh tim Chrome untuk menggunakan info jenis (ditentukan melalui gaya TypeScript anotasi) di kompiler untuk mengoptimalkan JS. Ada pertanyaan terbuka di sana tentang nullability - mereka ingin tipe non-nullable tetapi juga tidak yakin tentang kelayakannya.

Hanya akan berpadu kembali di sini. Saya terkejut kami belum benar-benar menyelesaikan yang satu ini.

Saya katakan ada nilai tambah di sini. Salah satu yang hanya berlaku pada waktu kompilasi.
Alih-alih harus menulis lebih banyak kode untuk memeriksa nilai nol, mendeklarasikan nilai sebagai tidak dapat dibatalkan dapat menghemat waktu dan pengkodean. Jika saya menggambarkan suatu tipe sebagai 'non-nullable' maka itu harus diinisialisasi dan mungkin dinyatakan sebagai non-null sebelum diteruskan ke fungsi lain yang mengharapkan non-null.

Seperti yang saya katakan di atas, mungkin ini bisa diselesaikan dengan implementasi kontrak kode.

Mengapa Anda tidak bisa berhenti menggunakan literal nol dan menjaga semua tempat di mana
nulls dapat bocor ke kode Anda? Dengan cara ini Anda akan aman dari null
referensi pengecualian tanpa harus melakukan pemeriksaan nol di mana-mana.
Pada 20 Februari 2015 13:41, "electricessence" [email protected] menulis:

Hanya akan berpadu kembali di sini. Saya terkejut kami belum benar-benar menyelesaikannya
yang ini.

Saya katakan ada nilai tambah di sini. Salah satu yang hanya berlaku di kompilasi
waktu.
Alih-alih harus menulis lebih banyak kode ke nol, periksa nilai, karena dapat
mendeklarasikan nilai sebagai tidak dapat dibatalkan dapat menghemat waktu dan pengkodean. Jika saya menggambarkan
ketik sebagai 'non-nullable' maka itu harus diinisialisasi dan mungkin ditegaskan
sebagai non-null sebelum diteruskan ke fungsi lain yang mengharapkan
bukan nol.

Seperti yang saya katakan di atas, mungkin ini bisa diselesaikan dengan kontrak kode
penerapan.

Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75295204
.

Pemeriksaan null secara signifikan lebih cepat daripada pemeriksaan yang tidak ditentukan.
Pada 21/02/2015 11:54, "Aleksey Bykov" [email protected] menulis:

Mengapa Anda tidak bisa berhenti menggunakan literal nol dan menjaga semua tempat di mana
nulls dapat bocor ke kode Anda? Dengan cara ini Anda akan aman dari null
referensi pengecualian tanpa harus melakukan pemeriksaan nol di mana-mana.
Pada 20 Februari 2015 13:41, notifikasi "electricessence"@github.com
menulis:

Hanya akan berpadu kembali di sini. Saya terkejut kami belum benar-benar menyelesaikannya
yang ini.

Saya katakan ada nilai tambah di sini. Salah satu yang hanya berlaku di kompilasi
waktu.
Alih-alih harus menulis lebih banyak kode ke nol, periksa nilai, karena dapat
mendeklarasikan nilai sebagai tidak dapat dibatalkan dapat menghemat waktu dan pengkodean. Jika saya menggambarkan
ketik sebagai 'non-nullable' maka harus diinisialisasi dan mungkin
menegaskan
sebagai non-null sebelum diteruskan ke fungsi lain yang mengharapkan
bukan nol.

Seperti yang saya katakan di atas, mungkin ini bisa diselesaikan dengan kontrak kode
penerapan.

Balas email ini secara langsung atau lihat di GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-75295204>
.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75346198
.

Maksud saya bahwa pemeriksaan itu tidak perlu (jika kata kunci nol dilarang
dan semua tempat lain di mana ia bisa masuk dijaga).

Mengapa? 1. Kode tanpa pemeriksaan sama sekali berjalan jauh lebih cepat daripada kode
dengan cek untuk nol. 2. Ini berisi lebih sedikit jika dan karena itu lebih mudah dibaca
dan dapat dipertahankan.
Pada 20 Februari 2015 19:57, "Griffork" [email protected] menulis:

Pemeriksaan null secara signifikan lebih cepat daripada pemeriksaan yang tidak ditentukan.
Pada 21/02/2015 11:54, "Aleksey Bykov" [email protected] menulis:

Mengapa Anda tidak bisa berhenti menggunakan literal nol dan menjaga semua tempat di mana
nulls dapat bocor ke kode Anda? Dengan cara ini Anda akan aman dari null
referensi pengecualian tanpa harus melakukan pemeriksaan nol di mana-mana.
Pada 20 Februari 2015 13:41, notifikasi "electricessence"@github.com
menulis:

Hanya akan berpadu kembali di sini. Saya terkejut kami belum benar-benar menyelesaikannya
yang ini.

Saya katakan ada nilai tambah di sini. Salah satu yang hanya berlaku di kompilasi
waktu.
Alih-alih harus menulis lebih banyak kode ke nol, periksa nilai, bisa
ke
mendeklarasikan nilai sebagai tidak dapat dibatalkan dapat menghemat waktu dan pengkodean. Jika saya
menggambarkan sebuah
ketik sebagai 'non-nullable' maka harus diinisialisasi dan mungkin
menegaskan
sebagai non-null sebelum diteruskan ke fungsi lain yang mengharapkan
bukan nol.

Seperti yang saya katakan di atas, mungkin ini bisa diselesaikan dengan kontrak kode
penerapan.

Balas email ini secara langsung atau lihat di GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75295204

.

Balas email ini secara langsung atau lihat di GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-75346198>
.

Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75346390
.

Anda melewatkan intinya, kami menggunakan nol untuk menghindari pemeriksaan yang tidak ditentukan.
Dan karena aplikasi kami tidak memiliki satu status yang konsisten, kami melakukannya, dan
sering kali harus membuat sesuatu menjadi nol/tidak terdefinisi alih-alih objek untuk
mewakili itu.
Pada 21/02/2015 12:20, "Aleksey Bykov" [email protected] menulis:

Maksud saya bahwa pemeriksaan itu tidak perlu (jika kata kunci nol dilarang
dan semua tempat lain di mana ia bisa masuk dijaga).

Mengapa? 1. Kode tanpa pemeriksaan sama sekali berjalan jauh lebih cepat daripada kode
dengan cek untuk nol. 2. Ini berisi lebih sedikit jika dan karena itu lebih mudah dibaca
dan dapat dipertahankan.
Pada 20 Februari 2015 19:57, "Griffork" [email protected] menulis:

Pemeriksaan null secara signifikan lebih cepat daripada pemeriksaan yang tidak ditentukan.
Pada 21/02/2015 11:54, "Aleksey Bykov" [email protected]
menulis:

Mengapa Anda tidak bisa berhenti menggunakan literal nol dan menjaga semua tempat
di mana
nulls dapat bocor ke kode Anda? Dengan cara ini Anda akan aman dari null
referensi pengecualian tanpa harus melakukan pemeriksaan nol di mana-mana.
Pada 20 Februari 2015 13:41, notifikasi "electricessence"@github.com
menulis:

Hanya akan berpadu kembali di sini. Saya terkejut kita belum benar-benar
terselesaikan
yang ini.

Saya katakan ada nilai tambah di sini. Yang hanya berlaku di
menyusun
waktu.
Alih-alih harus menulis lebih banyak kode ke nol, periksa nilai, menjadi
sanggup
ke
mendeklarasikan nilai sebagai tidak dapat dibatalkan dapat menghemat waktu dan pengkodean. Jika saya
menggambarkan sebuah
ketik sebagai 'non-nullable' maka harus diinisialisasi dan mungkin
menegaskan
sebagai non-null sebelum diteruskan ke fungsi lain yang mengharapkan
bukan nol.

Seperti yang saya katakan di atas, mungkin ini bisa diselesaikan dengan kode
kontrak
penerapan.

Balas email ini secara langsung atau lihat di GitHub
<

https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75295204

.

Balas email ini secara langsung atau lihat di GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75346198

.

Balas email ini secara langsung atau lihat di GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-75346390>
.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75347832
.

Anda kehilangan poin saya juga. Untuk menjaga status Anda tetap konsisten dan dapat mewakili nilai yang hilang, Anda tidak perlu menggunakan nulls atau undefined. Ada cara lain, terutama sekarang dengan dukungan jenis serikat pekerja, pertimbangkan:

class Nothing { public 'i am nothing': Nothing; }
class Something { 
    constructor(
    public name: string,
    public value: number
   ) { }
}
var nothing = new Nothing();
var whoami = Math.random() > 0.5 ? new Something('meh', 66) : nothing;

if (whoami instanceof Nothing) {
    console.log('i am a null killer');
} else if (whoami instanceof Something) {
    console.log(whoami.name + ': ' + whoami.value);
}

Jadi kami hanya menyandikan nilai yang hilang tanpa menggunakan nol atau tidak terdefinisi. Perhatikan juga bahwa kami melakukannya dengan cara yang 100% eksplisit . Terima kasih kepada penjaga tipe, tidak mungkin seseorang bisa melewatkan cek sebelum membaca nilai.

Betapa kerennya itu?

Apakah menurut Anda instance cek lebih cepat daripada cek nol, untuk
aplikasi yang harus melakukan ratusan ini per detik?
Biarkan saya begini: kami harus membuat alias untuk fungsi karena
lebih cepat untuk tidak menggunakan '.' pengakses.
Pada 21/02/2015 12:58, "Aleksey Bykov" [email protected] menulis:

Anda kehilangan poin saya juga. Untuk menjaga negara Anda tetap konsisten dan
dapat mewakili nilai yang hilang, Anda tidak perlu menggunakan nol atau
tidak terdefinisi. Ada cara lain, terutama sekarang dengan dukungan tipe serikat pekerja
mempertimbangkan:

class Nothing { publik 'saya bukan apa-apa': Tidak ada; }
kelas Sesuatu {
konstruktor (
nama umum: string,
nilai publik: angka
) {}
}
var tidak ada = tidak ada baru();
var whoami = Math.random() > 0.5 ? sesuatu yang baru('meh', 66) : tidak ada;

if (whoami instanceof Nothing) {
// whoami bukan apa-apa
console.log('saya seorang pembunuh nol');
} else if (whoami instanceof Sesuatu) {
// whoami adalah turunan dari Sesuatu
console.log(whoami.name + ': ' + whoami.value);
}

Jadi baru saja dikodekan nilai yang hilang tanpa menggunakan nol atau tidak terdefinisi.
Perhatikan juga bahwa kami melakukannya dengan _100% cara eksplisit_. Terima kasih untuk mengetik
penjaga, tidak mungkin seseorang bisa melewatkan cek sebelum membaca nilai.

Betapa kerennya itu?


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75349967
.

@aleksey-bykov Perhatikan bahwa saya sudah membahas poin itu: kebutuhan untuk memodelkan definisi tipe untuk perpustakaan yang ada yang sudah menggunakan nilai nol tidak terpecahkan. Dalam hal ini, Anda tidak bisa begitu saja "berhenti menggunakan" nilai nol.

Juga, itu bahkan tidak menyentuh masalah dengan variabel yang tidak diinisialisasi yang akan menjadi undefined . Di Haskell, masalahnya bahkan tidak ada karena Anda tidak dapat meninggalkan nilai "tidak diinisialisasi".

@Griffork , saya tidak berpikir, saya menjalankan tes, tes mengatakan itu tergantung pada browser.
http://jsperf.com/nullable-vs-null-vs-undefined-vs-instanceof
Apa yang saya pikirkan adalah bahwa Anda perlu menemukan keseimbangan antara keamanan dan kinerja. Maka Anda perlu mengukur kinerja Anda dengan cermat sebelum mencoba mengoptimalkannya. Kemungkinan Anda memperbaiki sesuatu yang tidak rusak dan pemeriksaan nol Anda yang sangat Anda khawatirkan mungkin merupakan kurang dari 2% dari keseluruhan kinerja, jika demikian jika Anda membuatnya berlipat ganda lebih cepat, Anda hanya akan mendapatkan 1%.

@spion , semua hal dianggap sebagai cara yang jauh lebih baik daripada tetap menggunakan nulls dalam kode Anda, mengingat situasi saat ini dan masalah yang tidak dapat dibatalkan yang dibawa bersama mereka

Basis kode kami adalah lebih dari 800 file TypeScript dan lebih dari 120000 baris kode
dan kami tidak pernah membutuhkan nol atau tidak terdefinisi dalam hal bisnis modeling
entitas domain. Dan meskipun kami harus menggunakan nol untuk manipulasi DOM,
semua tempat seperti itu diisolasi dengan hati-hati, sehingga nol tidak dapat bocor
masuk Saya tidak membeli argumen Anda bahwa nol mungkin diperlukan, ada
bahasa siap produksi tanpa nol sama sekali (Haskell) atau dengan nol
dilarang (F#).
Pada 22 Februari 2015 09:31, "Jon" [email protected] menulis:

@aleksey-bykov https://github.com/aleksey-bykov Dari praktik
perspektif, Anda tidak dapat mengabaikan null. Inilah beberapa latar belakang mengapa Anda melakukannya
perlu tipe nullable:
https://www.google.com/patents/US7627594?ei=P4XoVPCaEIzjsATm4YGoBQ&ved=0CFsQ6AEwCTge

Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75437927
.

Ya, dan hal terpenting yang dapat Anda pahami di sini adalah bahwa kami BUKAN memodelkan entitas domain bisnis, kami SANGAT sensitif terhadap waktu, dan basis kode kami tidak terlalu kecil dari milik Anda.

Untuk tujuan Anda, Anda tidak memerlukan nol, yang baik-baik saja, Anda dapat pergi dan terus menggunakan TypeScript tanpa mereka.
Untuk tujuan kami, kami membutuhkan nol, dan kami tidak mampu untuk tidak melakukannya.

Oh, baru saja melihat komentar Anda yang lain, maaf.
Jika Anda membaca sedikit lebih awal di utas ini, Anda akan melihat bahwa saya telah mengatakan (pada dasarnya) bahwa kami selalu menggunakan nol, dan kami jarang memiliki masalah dengan mereka yang merembes ke dalam bit kode lainnya.

Jika Anda juga membaca di atas, proposal yang tidak dapat dibatalkan juga mencakup yang tidak ditentukan.

Variabel nol dan tidak terdefinisi untuk kecepatannya terlihat serupa, tetapi segera setelah mereka ada pada suatu objek (terutama yang memiliki rantai prototipe) ceritanya sangat berbeda, jadi kami menggunakan nol sebagai gantinya.

Sekali lagi, mungkin tidak perlu dikhawatirkan, kecuali (seperti kami), Anda melakukan 10.000 pemeriksaan dalam <10 md.

(Dan ya, kami benar-benar berubah karena melacak kinerja aplikasi kami dan menemukan hambatan)

Saya ingin mendorong beberapa diskusi tentang ini. Baru kemarin di video Build saya melihat penganalisis diimplementasikan dalam C#. Saya membuka masalah #3003 untuk mengimplementasikan yang mirip dengan TypeScript.

Pada dasarnya analyzer adalah library yang dapat melakukan pemeriksaan sintaks tambahan, tag line sebagai kesalahan/peringatan, dan diinterpretasikan oleh layanan bahasa. Ini akan secara efektif memungkinkan untuk menegakkan pemeriksaan nol dengan perpustakaan eksternal.

Sebenarnya saya melihat penganalisis di TypeScript akan sangat bagus untuk hal-hal lain juga. Ada perpustakaan yang benar-benar aneh di luar sana, lebih dari C#.

Untuk siapa pun yang mengikuti masalah ini: Saya memelihara perpustakaan bernama monapt yang memungkinkan Anda mendeklarasikan nullability suatu variabel. Ini termasuk definisi TypeScript, jadi semuanya diperiksa waktu kompilasi.

Umpan balik selamat datang!

Saya baru saja membaca seluruh utas.

Saya kira proposal harus menggunakan istilah non-voidable dan voidable , karena void dalam spesifikasi TS adalah tipe untuk nilai null dan undefined . (Saya berasumsi proposal juga mencakup nilai yang tidak ditentukan :senyum: )

Saya juga setuju akses tidak terdefinisi atau pengecualian nol adalah akar dari semua kejahatan dalam pemrograman menambahkan fitur ini akan menghemat banyak waktu bagi orang-orang.

Tolong tambahkan ini! Dan akan lebih baik jika ini menjadi fitur saya bisa melewati bendera untuk menjadikannya default. Itu akan menghemat banyak debug neraka bagi banyak orang.

Meskipun saya setuju bahwa ADT dapat digunakan sebagai pengganti tipe yang dapat dibatalkan; bagaimana kita bisa yakin bahwa ADT yang kita lewati bukan null itu sendiri? Semua jenis TS bisa null, termasuk ADT, kan? Saya melihatnya sebagai kesalahan besar dalam memikirkan ADT sebagai solusi untuk masalah tipe yang tidak dapat dibatalkan (tidak dapat dibatalkan). ADT hanya berfungsi sebaik yang mereka lakukan dalam bahasa yang mengizinkan (atau melarang) jenis yang tidak dapat dibatalkan.

Masalah utama lainnya adalah definisi perpustakaan. Saya mungkin memiliki perpustakaan yang mengharapkan string sebagai argumen, tetapi kompiler TS dapat mengetikkan cek dengan meneruskan null ke fungsi! Itu tidak benar...

Saya melihatnya sebagai kesalahan besar dalam memikirkan ADT sebagai solusi untuk masalah tersebut...

  1. dapatkan ADT tersedia melalui pola desain (karena kurangnya asli
    dukungan dari TypeScript)
  2. melarang kata kunci null
  3. jaga semua tempat di mana null dapat bocor ke kode Anda dari luar
  4. nikmati masalah null hilang selamanya

Pada proyek kami, kami berhasil melakukan langkah 1, 2 dan 3. Coba tebak apa yang kami lakukan
sekarang.

@aleksey-bykov

Bagaimana Anda melakukannya (2) dengan undefined ? Itu dapat muncul dalam berbagai situasi yang tidak melibatkan kata kunci undefined : anggota kelas yang tidak diinisialisasi, bidang opsional, pernyataan pengembalian yang hilang di cabang bersyarat...

Bandingkan naskah dengan flowtype . Setidaknya flow menangani sebagian besar kasus.

Juga, jika Anda ingin mengatakan "Lupakan idiom umum dalam bahasa tersebut. Saya tidak peduli, saya akan mengisolasi diri saya dari seluruh dunia", maka Anda mungkin lebih baik menggunakan purescript .

Itu dapat muncul dalam berbagai situasi yang tidak melibatkan kata kunci yang tidak ditentukan ...

  • anggota kelas yang tidak diinisialisasi - kelas dilarang
  • bidang opsional - bidang/parameter opsional dilarang
  • pernyataan pengembalian yang hilang - aturan tslint khusus yang memastikan bahwa semua jalur eksekusi kembali

lebih baik menggunakan purescript

  • saya berharap tetapi kode yang dihasilkannya paling tidak menyangkut kinerja

@aleksey-bykov lucu bahwa Anda menyebutkan kinerja, karena mengganti tipe nullable dengan ADT memaksakan cpu dan memori overhead yang signifikan dibandingkan dengan hanya menegakkan pemeriksaan nol (seperti flowtype). Hal yang sama berlaku untuk tidak menggunakan kelas (bisa sangat mahal jika Anda membuat banyak objek dengan banyak metode di dalamnya)

Anda menyebutkan menyerahkan bidang opsional tetapi mereka digunakan oleh begitu banyak perpustakaan JS. Apa yang Anda lakukan untuk menangani perpustakaan-perpustakaan itu?

Anda juga menyebutkan tidak menggunakan kelas. Saya kira Anda juga menghindari perpustakaan yang mengandalkan (atau akan bergantung) pada kelas (seperti misalnya Bereaksi)?

Bagaimanapun, saya tidak melihat alasan untuk menyerah pada begitu banyak fitur ketika solusi yang sangat masuk akal (yang sebenarnya sesuai dengan bahasa yang tidak diketik) kemungkinan besar dapat diterapkan.

lucu bahwa Anda berpikir bahwa satu-satunya tujuan ADT adalah untuk mewakili nilai yang hilang yang seharusnya dikodekan oleh nol

saya lebih suka mengatakan bahwa menghilangkan masalah "null" (dan kelas juga) adalah produk sampingan dari semua hal baik yang dibawa ADT kepada kita

mengatasi rasa ingin tahu Anda, untuk bagian penting kinerja seperti loop ketat dan struktur data besar, kami menggunakan apa yang disebut abstraksi Nullable<a> dengan membodohi TypeScript dengan berpikir bahwa itu berhubungan dengan nilai yang dibungkus, tetapi sebenarnya (saat runtime) nilai seperti itu hanya primitif yang diizinkan mengambil nol, ada serangkaian operasi khusus pada Nullable yang mencegah kebocoran nol itu

beri tahu saya jika Anda ingin mengetahui detailnya

interface Nullable<a> {
    'a nullable': Nullable<a>;
    'uses a': a;
}
/*  the following `toNullable` function is just for illustration, we don't use it in our code,
    because there are no values capable of holding naked null roaming around,
    instead we just alter the definition of all unsafe external interfaces:
    // before
    interface Array<T> {
       find(callback: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T;
    }
    // after
    interface Array<a> {
       find(callback: (value: a, index: number, array: a[]) => boolean, thisArg: any): Nullable<a>;
    }
*/
function toNullable<a>(value: a) : Nullable<a> {
    return <any>value;
}
function toValueOrDefault<a>(value: Nullable<a>, defaultValue: a)  : a {
    return value != null ? <any>value : defaultValue;
}

Mengapa menggunakan struktur tipe rekursif? 'a nullable': a seharusnya cukup, bukan?

Saya kira Anda juga memiliki callFunctionWithNullableResult<T,U>(f: (T) => U, arg:T):Nullable<U> (termasuk semua kelebihan lain untuk fungsi dengan arity berbeda) untuk menangani semua fungsi eksternal yang mengembalikan nol?

Dan jika kata kunci null dilarang oleh linter, bagaimana Anda mendapatkan " Nothing " disimpan dalam nullable? apakah anda kasus khusus toNullable(null) ?

Saya tahu apa itu tipe data aljabar. Bisakah Anda memberi saya sebuah contoh (selain pemeriksaan kelengkapan dan kecanggungan) di mana ADT (atau lebih tepatnya, tipe penjumlahan) + pencocokan pola dapat melakukan sesuatu yang tidak dapat dilakukan oleh penyatuan TypeScript + penjaga tipe yang ditentukan pengguna?

Mengapa menggunakan struktur tipe rekursif? 'a nullable': a seharusnya cukup, bukan?

baik, dalam contoh ini sudah cukup, tetapi melakukannya seperti ini membebani tujuan bidang tersebut, yang melayani 2 kasus berbeda:

saya pribadi benci kelebihan beban yang berarti inilah mengapa saya menggunakan 2 bidang semu yang terpisah di mana masing-masing memecahkan masalah yang terpisah

Saya kira Anda juga memiliki callFunctionWithNullableResult(f: T -> U, arg:T) => Tidak dapat dibatalkan

tidak, seperti yang saya katakan, kami mengubah file definisi (*.d.ts) dengan mengganti apa pun yang kami punya bukti dapat menjadi null dalam kode eksternal dengan Nullable "wrapper", lihat contoh di komentar di pesan saya sebelumnya

bagaimana Anda mendapatkan "Tidak Ada" yang disimpan dalam nullable

kami tidak menyimpan nulls dalam nullables, nullables berasal dari server (yang kami kendalikan terbatas) atau dari API eksternal, segera setelah itu dalam kode kami dibungkus dengan Nullable, tetapi kode kami tidak mampu menghasilkan sebuah nullable sesuka hati, kita hanya dapat mengubah nullable yang diberikan menjadi nullable lain, tetapi tidak untuk membuat satu dari udara tipis

serikat TypeScript + penjaga tipe yang ditentukan pengguna tidak dapat melakukannya

Ha! saya sama sekali bukan penggemar tipe serikat pekerja dan dapat berbicara berjam-jam tentang apa yang membuat mereka menjadi ide yang buruk

kembali ke pertanyaan Anda, beri saya setara yang dapat digunakan kembali dari Either a b menggunakan serikat pekerja dan ketik penjaga
(petunjuk: https://github.com/Microsoft/TypeScript/issues/2264)

Jenis dalam contoh Anda tidak lebih nominal hanya karena mereka rekursif - mereka tetap "nominal semu".

Untuk menjawab pertanyaan Anda: Anda tidak menerapkan Either generik, justru karena tipenya tidak nominal. Anda cukup menggunakan

type MyType = A | B

dan kemudian gunakan penjaga jenis untuk A dan B ... Sebagai bonus ini juga berfungsi dengan setiap perpustakaan JS yang ada: misalnya cara idiomatis untuk memeriksa Thenable adalah jika ada adalah metode then yang dilampirkan ke objek. Akankah tipe penjaga bekerja untuk itu? Hampir dipastikan.

Ha! Itu tangkapanmu yang bagus. Nah, pasti ada sesuatu yang berubah di versi terbaru dari kompiler. Berikut adalah nominal antipeluru:

enum GottaBeNonimalBrand {}
interface GottaBeNonimal {
     branded: GottaBeNonimalBrand;
}
function toGottaBeNominal() : GottaBeNominal {
   return <any> {};
}

Tunggu sebentar! Anda tertipu ! Cara lama yang baik masih berfungsi seperti pesona.

Untuk menjawab pertanyaan Anda: Anda tidak menerapkan Either generik, justru karena tipenya tidak nominal. Anda cukup menggunakan...

itu bagus apa yang Anda katakan, bagaimana itu Either ? bahwa Either yang otentik adalah GENERIC dan ada sekitar 30 fungsi kenyamanan standar di sekitarnya, sekarang Anda mengatakan saya harus menyelesaikan (nominal) dan memiliki sebanyak 30 x number_of_combinations_of_any_2_types_in_my_app dari fungsi-fungsi itu untuk semua kemungkinan pasangan yang saya mungkin bertemu?

Semoga thread ini masih dipantau oleh orang-orang dari tim TS. Saya ingin mencoba me-rebootnya!
Tampaknya bagi saya bahwa tipe non-null macet ketika menjadi jelas bahwa tidak mungkin untuk memperkenalkan ini dengan cara yang tidak putus-putus. Tim C# sedang memikirkan jenis referensi yang tidak dapat dibatalkan dan tentu saja dihadapkan pada masalah yang sama, terutama kompatibilitas. Saya suka ide-ide yang mereka jelajahi saat ini dan saya ingin tahu apakah itu akan berlaku untuk TS.

C# vnext brainstorming (singkat)

Pada dasarnya, di C# Anda memiliki tipe nilai non-null, seperti int . Kemudian di C# 2.0 kami mendapatkan tipe nilai nullable, seperti int? . Referensi selalu dapat dibatalkan, tetapi mereka berpikir untuk mengubah ini dan menerapkan sintaks yang sama: string is never null, string? mungkin.

Tentu saja, masalah besarnya adalah ini mengubah arti dari string dan merusak kode. Di mana tidak apa-apa untuk menetapkan/mengembalikan null ke string , sekarang kesalahan. Tim C# sedang memikirkan untuk "menonaktifkan" kesalahan tersebut kecuali Anda ikut serta. Mencoret ke string? selalu merupakan kesalahan, karena sintaksnya baru dan tidak diizinkan sebelumnya (karenanya ini bukan perubahan yang melanggar).
Masalah lainnya adalah dengan perpustakaan lain, dll. Untuk mengatasinya, mereka ingin membuat flag keikutsertaan per file atau rakitan. Jadi kode eksternal yang memilih sistem tipe yang lebih ketat mendapat manfaat, dan kode lama dan pustaka terus bekerja.

Bagaimana itu bisa berubah menjadi TS?

  1. Kami memperkenalkan tanda keikutsertaan jenis non-null. Itu bisa global (diteruskan ke kompiler) atau per-file. Itu harus selalu per-file untuk .d.ts .
  2. Tipe dasar tidak dapat dibatalkan, misalnya number . Ada tipe null .
  3. number? hanyalah jalan pintas untuk number | null .
  4. Fitur ini memperkenalkan kesalahan baru untuk tipe nullable, seperti (x: string?) => x.length tanpa tipe guard.
  5. Fitur ini memperkenalkan kesalahan baru untuk jenis yang tidak dapat dibatalkan, seperti menetapkan let x: string = null;
  6. Saat memanipulasi jenis non-null yang dideklarasikan di luar cakupan keikutsertaan, kesalahan yang dilaporkan di bawah 5. akan diabaikan. _Ini sedikit berbeda dengan mengatakan string masih dianggap sebagai string? ._

Apa bagusnya ini?

Ketika saya menulis kode baru, saya dapat ikut serta dan mendapatkan pemeriksaan nol statis lengkap dalam kode saya dan dalam definisi perpustakaan yang diperbarui. Saya dapat terus menggunakan definisi perpustakaan lama tanpa kesalahan.

Ketika saya menggunakan kode lama, saya dapat ikut serta untuk file baru. Saya tetap dapat mendeklarasikan string? dan mendapatkan kesalahan saat mengakses anggota tanpa pemeriksaan nol yang tepat. Saya juga mendapatkan manfaat dan pemeriksaan ketat untuk perpustakaan yang definisinya telah diperbarui. Setelah .d.ts , meneruskan null ke fungsi yang didefinisikan sebagai length(x: string): number menjadi kesalahan.
_(Catatan: Melewati string? juga merupakan kesalahan, meskipun melewatkan string dari kode penyisihan tidak.)_

Selama saya tidak ikut serta, tidak ada perubahan yang melanggar.

Bagaimana menurut anda?

Tentu saja, ada banyak hal yang perlu dipertimbangkan mengenai bagaimana fitur tersebut akan bekerja dalam praktiknya. Tetapi apakah skema keikutsertaan semacam itu merupakan sesuatu yang mungkin dipertimbangkan oleh tim TS?

sungguh menakjubkan bagaimana orang menginginkan kuda yang lebih cepat daripada mobil..

Tidak perlu merendahkan. Saya telah membaca komentar Anda tentang ADT dan menurut saya bukan itu yang saya butuhkan. Kami dapat mendiskusikannya jika Anda mau, tetapi dalam edisi baru tentang "Dukungan ADT di TS". Anda dapat menulis di sana mengapa mereka hebat, mengapa kami membutuhkannya dan bagaimana mereka mengurangi kebutuhan akan tipe yang tidak dapat dibatalkan. Masalah ini tentang tipe yang tidak dapat dibatalkan dan Anda benar-benar membajak topik.

apa yang Anda bicarakan disebut propagasi konstan dan, jika diterapkan, tidak boleh dibatasi hanya oleh 2 konstanta acak yang kebetulan null dan undefined . Hal yang sama dapat dilakukan pada string, angka, dan apa pun dan cara ini masuk akal, jika tidak, hanya untuk memelihara beberapa kebiasaan lama, beberapa orang tidak mau melakukannya.

@aleksey-bykov dan tentu saja kemudian untuk mendapatkan batasan tipe yang lebih baik, Anda menerapkan tipe yang lebih tinggi, dan untuk mendapatkan turunan turunan, Anda mengimplementasikan kelas tipe dan seterusnya dan seterusnya. Bagaimana itu cocok dengan tujuan desain "selaraskan dengan ES6+" dari TypeScript?

Dan itu semua masih tidak berguna dalam memecahkan masalah asli dari masalah ini. Mudah untuk mengimplementasikan Maybe (dan Either) di TS menggunakan kelas generik, bahkan hari ini (tanpa tipe serikat pekerja). Ini akan sama bergunanya dengan menambahkan Optional ke Java, yaitu: nyaris. Karena seseorang hanya akan menetapkan null suatu tempat atau memerlukan properti opsional, dan bahasa itu sendiri tidak akan mengeluh tetapi banyak hal akan meledak saat runtime.

Tidak jelas bagi saya mengapa Anda bersikeras menyalahgunakan bahasa untuk membuatnya melengkung dengan cara yang tidak pernah dirancang. Jika Anda menginginkan bahasa fungsional dengan tipe data aljabar dan tanpa nilai nol, gunakan saja PureScript. Ini adalah bahasa yang sangat baik, dirancang dengan baik dengan fitur-fitur yang cocok secara koheren, diuji pertempuran selama bertahun-tahun di Haskell tanpa akumulasi kutil Haskell (hierarki kelas tipe berprinsip, catatan nyata dengan polimorfisme baris ...). Menurut saya jauh lebih mudah untuk memulai dengan bahasa yang mematuhi prinsip dan kebutuhan Anda kemudian mengubah kinerja, daripada memulai dengan sesuatu yang tidak (dan tidak pernah dimaksudkan untuk) kemudian memutar, memutar, dan membatasi untuk mendapatkannya bekerja dengan cara Anda.

Apa masalah kinerja Anda dengan PureScript? Apakah ini penutupan yang dihasilkan oleh kari yang meresap?

@jods4 apakah itu berarti bahwa semua file .d.ts yang ada saat ini perlu diperbarui dengan tanda tidak berfungsi, atau bahwa pengaturan seluruh proyek tidak memengaruhi .d.ts (dan .d.ts tanpa bendera selalu dianggap sebagai cara 'lama'.
Seberapa bisa dimengerti ini (apakah ini akan menyebabkan masalah bagi orang yang mengerjakan banyak proyek atau kebingungan saat membaca file .d.ts)?
Seperti apa tampilan antarmuka antara kode (perpustakaan) baru dan lama. Apakah itu akan dipenuhi dengan banyak (dalam beberapa kasus) pemeriksaan dan gips yang tidak perlu (mungkin bukan hal yang buruk tetapi mungkin mematikan bahasa pemula)?
Sebagian besar saya (sekarang) menyukai gagasan jenis yang tidak dapat dibatalkan, dan saran oleh @jods4 dan ! Saran adalah satu-satunya cara saya melihat jenis yang tidak dapat dibatalkan berfungsi (mengabaikan iklan).

Mungkin hilang di utas ini, tetapi masalah apa yang dipecahkan ini? Itu adalah sesuatu yang menyinggung saya. Argumen yang saya lihat baru-baru ini di utas ini adalah "bahasa lain memilikinya". Saya juga mengalami kesulitan melihat apa yang dipecahkan ini yang berguna yang tidak memerlukan semacam intrik pada TypeScript. Meskipun saya dapat memahami bahwa nilai null dan undefined dapat tidak diinginkan, saya cenderung melihat ini sebagai logika tingkat tinggi yang ingin saya masukkan ke dalam kode saya versus mengharapkan bahasa yang mendasarinya untuk menanganinya .

Nit kecil lainnya, adalah bahwa setiap orang memperlakukan undefined sebagai konstanta atau kata kunci. Secara teknis juga tidak. Dalam konteks global, ada variabel/properti undefined yang merupakan tipe primitif undefined . null adalah literal (yang juga merupakan tipe primitif null , meskipun ini merupakan bug lama yang diimplementasikan untuk mengembalikan typeof "object" ). Dalam ECMAScript, ini adalah dua hal yang sangat berbeda.

@kitsonk
Masalahnya adalah semua tipe bisa null, dan semua tipe bisa tidak terdefinisi tetapi tidak semua operasi yang bekerja pada tipe tertentu tidak bekerja pada nilai null atau tidak terdefinisi (mis. bagi) yang menyebabkan kesalahan.
Apa yang mereka inginkan adalah kemampuan untuk menentukan tipe sebagai "adalah angka dan tidak pernah nol atau tidak terdefinisi" sehingga mereka dapat memastikan bahwa ketika menggunakan fungsi yang memiliki kemungkinan memperkenalkan null atau pemrogram yang tidak terdefinisi tahu untuk menjaga terhadap nilai yang tidak valid.
Ada dua argumen utama untuk dapat menentukan nilai sebagai "adalah tipe ini tetapi tidak dapat menjadi nol atau tidak ditentukan" adalah bahwa:
A) jelas bagi semua programmer yang menggunakan kode bahwa nilai ini tidak boleh nol atau tidak terdefinisi.
B) kompiler membantu menangkap pelindung tipe yang hilang.

ada variabel/properti tidak terdefinisi yang merupakan tipe primitif tidak terdefinisi. null adalah literal (yang juga merupakan tipe primitif null

@kitsonk ini adalah argumen yang bagus untuk masalah ini; null bukan number , itu tidak lebih dari Person daripada Car . Ini adalah tipenya sendiri dan masalah ini tentang mengizinkan TypeScript membuat perbedaan ini.

@kitsonk Saya pikir salah satu kesalahan paling umum adalah referensi nol/akses kesalahan yang tidak ditentukan. Memiliki kemampuan untuk menentukan properti atau variabel agar tidak menjadi nol/tidak terdefinisi akan membuat kode perangkat lunak lebih terdengar. Dan juga biarkan alat seperti LS untuk menangkap bug seperti ini https://github.com/Microsoft/TypeScript/issues/3692.

Terimakasih atas klarifikasinya. Mungkin saya kehilangan sesuatu di sini lagi, setelah melihat kembali melalui ini ... Karena sudah ada argumen "opsionalitas" di TypeScript, mengapa yang berikut ini tidak berfungsi sebagai proposal?

interface Mixed {
    optional?: string;
    notOptional: string;
    nonNullable!: string;
}

function mixed(notOptional: string, notNullable!: string, optional?: string): void {}

Meskipun ada fungsionalitas yang lebih besar di beberapa solusi yang diusulkan, saya menduga jenis menjaga terhadap mereka menjadi tantangan, di mana ini (meskipun saya tidak tahu cara kerja bagian dalam TypeScript dengan baik) sepertinya perpanjangan dari apa yang sudah diperiksa.

Karena sudah ada argumen "opsionalitas" di TypeScript, mengapa yang berikut ini tidak berfungsi sebagai proposal?

Hanya untuk memperjelas opsional sekarang di TS hanya opsional selama inisialisasi. Null dan undefined masih dapat ditetapkan untuk semua jenis.

Ini adalah beberapa kasus opsional yang tidak intuitif

interface A {
   a?: string;
}
let a: A = {} // ok as expected

interface B {
   b: string;
}
let b1: B = { b: undefined } //ok, but unintuitive
let b2: B = { b: null } // ok, but unintuitive
let b3: B = {} // error as expected

Atau sederhananya optional di TS berarti tidak dapat diinisialisasi. Tetapi proposal ini mengatakan bahwa undefined dan null tidak dapat ditetapkan ke tipe yang tidak dapat dibatalkan (non-null, non-undefined).

? ditempatkan oleh nama properti/argumen karena mengacu pada _eksistensi_ nama itu atau tidak. @tinganho menjelaskannya dengan baik. Memperluas contohnya:

function foo(arg: string, another?: string) {
  return arguments.length;
}

var a: A = {};
a.hasOwnProperty('a') // false
foo('yo') // 1, because the second argument is _non-existent_.
foo(null) // this is also ok, but unintuitive

! non-null tidak terkait dengan _eksistensi_ nama. Ini terkait dengan jenisnya, oleh karena itu harus ditempatkan berdasarkan jenisnya. Jenisnya tidak dapat dibatalkan.

interface C {
  c: !string;
}

let c1: C = { }; // error, as expected
let c2: C = { c: null }; // error, finally!
let c3: C = { c: 'str' }; // ok! :)

function bar(arg: !string) {
  return arg.length;
}

bar(null) // type error
bar('foo') // 3

@Griffork .d.ts akan berfungsi dengan baik dan dapat diperbarui dengan lancar untuk mendukung definisi yang lebih ketat.
Secara default .d.ts tidak ikut serta. Jika penulis perpustakaan memperbarui perpustakaannya dengan definisi non-null yang setia, ini menunjukkannya dengan menyetel tanda di bagian atas file.
Dalam kedua kasus semuanya hanya bekerja tanpa pemikiran / konfigurasi dari konsumen.

@kitsonk Masalah yang dipecahkan ini adalah mengurangi bug karena orang menganggap sesuatu tidak nol padahal mungkin, atau meneruskan nol ke fungsi yang tidak mendukungnya. Jika Anda mengerjakan proyek besar dengan banyak orang, Anda mungkin menggunakan fungsi yang tidak Anda tulis. Atau Anda dapat menggunakan perpustakaan pihak ketiga yang tidak Anda kenal dengan baik. Ketika Anda melakukannya: let x = array.sum(x => x.value) , dapatkah Anda berasumsi bahwa x tidak pernah nol? Mungkin 0 jika array kosong? Bagaimana Anda tahu? Apakah koleksi Anda diperbolehkan memiliki x.value === null ? Apakah itu didukung oleh fungsi sum() atau itu akan menjadi kesalahan?
Dengan fitur ini, kasus-kasus tersebut diperiksa secara statis dan meneruskan kemungkinan nilai nol ke fungsi yang tidak mendukungnya atau mengonsumsi nilai yang mungkin nol tanpa pemeriksaan dilaporkan sebagai kesalahan.
Pada dasarnya, dalam program yang benar-benar diketik, tidak akan ada "NullReferenceException" lagi (dikurangi kasus sudut yang terkait dengan undefined , sayangnya).

Diskusi tentang parameter opsional tidak terkait. Mengizinkan nol atau tidak terkait dengan sistem tipe, bukan deklarasi variabel/parameter dan terintegrasi dengan baik dengan pelindung tipe, tipe gabungan dan persimpangan, dll.

ketik _void_ tidak memiliki nilai apa pun tetapi nilai null dan undefined dapat ditetapkan untuk itu

di sini adalah ringkasan yang bagus: https://github.com/Microsoft/TypeScript/issues/185#issuecomment -71942237

@jbondc Saya pikir saya memeriksa spesifikasi baru-baru ini. void adalah istilah umum untuk null dan undefined . Tidak tahu tentang void 0 , tapi saya kira void juga memilikinya di bawah payungnya.

Menyimpulkan any . tetapi null dan undefined masih dapat ditetapkan untuk semua jenis.

void 0 selalu mengembalikan undefined juga http://stackoverflow.com/a/7452352/449132.

@jbondc jika pertanyaannya adalah tentang sistem tipe potentiel null-aware seperti yang saya jelaskan di atas,

function returnsNull() {
  return null;
}

harus menyimpulkan tipe null .

Ada banyak kasus sudut di sekitar undefined dan sejujurnya saya belum (belum?) yakin tentang apa cara terbaik untuk mengatasinya. Tetapi sebelum menghabiskan banyak waktu untuk memikirkannya, saya ingin tahu apakah tim TS akan setuju dengan strategi keikutsertaan.

Terakhir kali saya mengambil bagian dalam diskusi ini diakhiri dengan: "kami tidak akan melakukan perubahan yang melanggar dan kami tidak ingin mengubah arti kode sumber berdasarkan flag/opsi ambient". Gagasan yang saya uraikan di atas bukanlah perubahan yang melanggar dan relatif baik sehubungan dengan interpretasi sumber, meskipun tidak 100%. Area abu-abu itulah mengapa saya menanyakan pendapat tim TS.

@jods4 Saya lebih suka any , yang akan menjaga sintaks yang ada. Dan strategi opt-in akan lebih disukai. [1]

Saya lebih suka sintaks eksplisit untuk tipe yang tidak dapat dibatalkan. Untuk objek, itu akan merusak beberapa hal sekaligus. Dan juga, argumen opsional harus selalu dapat dibatalkan karena alasan yang jelas (dan seharusnya tidak mungkin untuk diubah). Argumen default harus mempertahankan tanda tangan mereka saat ini secara eksternal, tetapi di dalam fungsi itu sendiri, argumen dapat dibuat non-nullable.

Saya menemukan ini tidak masuk akal, karena angka yang dapat dibatalkan merusak banyak mesin pengoptimalan yang dapat dilakukan, dan ini bukan kasus penggunaan umum di luar argumen opsional/default. Mungkin sedikit lebih layak/tidak melanggar untuk membuat angka yang bukan argumen opsional menjadi non-nullable secara default melalui flag compiler. Ini juga memerlukan sintaks untuk secara eksplisit menandai tipe yang dapat dibatalkan, tetapi saya sudah melihatnya sebagai kemungkinan hasil dari diskusi ini.

[1] Memperkenalkan bendera untuk menjadikan ini default tidak akan berfungsi dengan baik sejak awal karena alasan praktis. Lihat saja masalah yang dihadapi orang dengan --noImplicitAny , yang mengakibatkan beberapa bahaya migrasi, banyak masalah praktis dalam menguji keberadaan properti yang mengarah ke --suppressImplicitAnyIndexErrors , dan beberapa definisi PastiTyped yang rusak.

@impinball

Saya lebih suka yang disimpulkan nullable any , yang akan menjaga sintaks yang ada. Dan strategi opt-in akan lebih disukai.

Saya telah mengedit jawaban saya di sini. Memikirkan lebih banyak tentang ini, saya hanya tidak melihat kasus berguna di mana Anda dapat secara implisit menyimpulkan tipe null saja. Seperti: () => null atau let x = null atau function y() { return null } .

Jadi saya setuju untuk terus menyimpulkan any mungkin adalah hal yang baik.

  1. Ini kompatibel ke belakang.
  2. Semua kasus itu adalah kesalahan di bawah noImplicitAny .
  3. Jika Anda memiliki kasus aneh di mana Anda ingin melakukan itu dan itu berguna bagi Anda, Anda dapat secara eksplisit mendeklarasikan jenisnya: (): null => null atau let x: null = null atau function y(): null { return null } .

Saya lebih suka sintaks eksplisit untuk tipe yang tidak dapat dibatalkan. Untuk objek, itu akan merusak beberapa hal sekaligus.

Mengatakan bahwa string sebenarnya berarti string! | null mungkin merupakan pilihan lain. Perlu dicatat bahwa jika Anda membaca semua diskusi di atas, ini pada awalnya diusulkan karena ada harapan bahwa ini akan lebih kompatibel dengan kode yang ada (tidak mengubah arti saat ini dari string ). Tetapi pada kenyataannya kesimpulannya adalah bahwa meskipun demikian, sejumlah besar kode akan tetap rusak.

Mengingat bahwa mengadopsi sistem tipe non-nullable tidak akan menjadi peralihan sepele untuk basis kode yang ada, saya akan memilih opsi string? daripada string! karena terasa lebih bersih. Terutama jika Anda melihat kode sumber kompiler TS, di mana hampir semua tipe internal akan diberi nama yang tidak akurat :(

Mungkin sedikit lebih layak/tidak melanggar untuk membuat angka yang bukan argumen opsional menjadi non-nullable secara default melalui flag compiler. Ini juga memerlukan sintaks untuk secara eksplisit menandai tipe yang dapat dibatalkan, tetapi saya sudah melihatnya sebagai kemungkinan hasil dari diskusi ini.

Saya pikir hal-hal yang semakin membingungkan pada saat ini. Bagi saya ini sepertinya argumen yang bagus untuk menggunakan string? mana-mana. Saya tidak ingin melihat pencampuran number? dan string! , ini akan menjadi terlalu membingungkan terlalu cepat.

[1] Memperkenalkan bendera untuk menjadikan ini default tidak akan berfungsi dengan baik sejak awal karena alasan praktis.

Ya itu tidak akan berfungsi dengan baik untuk basis kode yang ada tanpa perubahan. Tetapi:

  1. Ini akan bekerja dengan baik untuk basis kode baru. Kita bisa membangun masa depan yang lebih baik dengan ini.
  2. Basis kode yang ada akan menuai _some_ manfaat dan pemeriksaan tambahan, meskipun mereka tidak pernah ikut serta.
  3. Basis kode yang ada dapat ikut serta dalam basis per file, yang memungkinkan kode baru menjadi lebih ketat dan memungkinkan transisi progresif dari kode lama, jika diinginkan.

@jods4

Saya pikir hal-hal yang semakin membingungkan pada saat ini. Bagi saya ini sepertinya argumen yang bagus untuk menggunakan string? di mana pun. Saya tidak ingin melihat nomor yang tercampur? dan string!, ini akan menjadi terlalu membingungkan terlalu cepat.

Saya tidak bermaksud demikian. Maksud saya, ada lebih sedikit kasus penggunaan untuk angka yang dapat dibatalkan daripada untuk string yang dapat dibatalkan. Lagi pula, saya tidak terlalu terikat dengan saran itu (saya tidak akan peduli).

Basis kode yang ada dapat ikut serta dalam basis per file, yang memungkinkan kode baru menjadi lebih ketat dan memungkinkan transisi progresif dari kode lama, jika diinginkan.

Dengan apa artinya? Saya tidak percaya Anda saat ini dapat mengonfigurasi kompiler berdasarkan file per file. Saya terbuka untuk dibuktikan salah di sini.

@impinball

Dengan apa artinya? Saya tidak percaya Anda saat ini dapat mengonfigurasi kompiler berdasarkan file per file. Saya terbuka untuk dibuktikan salah di sini.

Mungkin saya salah. Saya tidak pernah menggunakannya tetapi ketika melihat kasus uji saya melihat banyak komentar seperti // <strong i="9">@module</strong> amd . Saya selalu berasumsi bahwa itu adalah cara untuk menentukan opsi dari dalam file ts. Saya belum pernah mencoba melakukannya, jadi mungkin saya sepenuhnya salah! Lihat contohnya di sini:
https://github.com/Microsoft/TypeScript/blob/master/tests/cases/conformance/externalModules/amdImportAsPrimaryExpression.ts

Jika ini bukan cara untuk menentukan opsi per file, kita mungkin memerlukan sesuatu yang baru. Diperlukan untuk menentukan opsi ini per file, _setidaknya_ untuk file .d.ts . Komentar berformat khusus di bagian atas file mungkin bisa digunakan, lagipula kami sudah memiliki dukungan untuk /// <amd-dependency /> dan co.

EDIT : Saya salah. Menyelidiki kode sumber telah menunjukkan kepada saya bahwa hal-hal itu disebut penanda dan telah diuraikan sebelumnya oleh pelari FourSlash. Jadi ini hanya untuk tes, bukan di dalam kompiler. Sesuatu yang baru perlu dipikirkan untuk itu.

Ada juga batasan praktis dalam beberapa kasus - ini bukan yang paling praktis untuk beberapa argumen seperti fitur ES6, karena akan memerlukan penguraian ulang file sepenuhnya. Dan IIUC, pemeriksaan tipe dilakukan dengan cara yang sama seperti pembuatan AST di tempat pertama.

Jika Anda melihat kode parser, komentar /// <amd-dependency /> dan /// <amd-module /> dibaca sebelum yang lain diuraikan dalam file. Menambahkan sesuatu seperti /// <non-null-types /> dapat dilakukan. OK itu penamaan yang mengerikan dan saya bukan untuk proliferasi opsi "ajaib" yang sewenang-wenang tapi itu hanya sebuah ide.

@ jods4 Saya pikir @I 'm pinball mengatakan bahwa IDE tidak akan mendukung tanpa perubahan besar untuk bagaimana sintaks penyorotan karya.

Apakah akan ada bentuk tipe none-nullable di 2.0 ?
Lagi pula, TypeScript membawa hal-hal yang sangat sedikit ke javascript jika tidak dapat menjamin jenisnya. Apa yang bisa kita dapatkan setelah mengatasi semua masalah dan penulisan ulang yang diperkenalkan oleh TypeScript?
IntelliSense yang lebih baik? Karena Anda masih perlu memeriksa jenisnya secara manual atau dengan kode di setiap fungsi ekspor ke modul lain.

Kabar baik semuanya, TypeScript 1.6 memiliki kekuatan ekspresif yang cukup untuk memodelkan dan melacak nilai yang hilang dengan aman (yang sebaliknya dikodekan oleh nilai null dan undefined ). Pada dasarnya pola berikut memberi Anda tipe yang tidak dapat dibatalkan:

declare module Nothing { export const enum Brand {} }
interface Nothing { 'a brand': Nothing.Brand }
export type Nullable<a> = a | Nothing;
var nothing : Nothing = null;
export function isNothing(value: Nullable<a>): value is Nothing {
    return value == null;
}
var something = Math.random() > 0.5 ? 'hey!' : nothing;
if (isNothing(something)) {
    // missing value
    // there is no way you can get anything out of it
    // there is also NO WAY to get a null reference exception out of it
    // because it doesn't have any methods or properties that could be examined
    // it is 100% explicit and typesafe to use
} else {
    // value is present, it is 100% GUARANTEED being NON-NULL
    // you just CANT get a null reference exception here either
    console.log(something.toLowerCase());
}

/** turns any unsafe values into safe ones */
export function sanitize<a>(unsafe: a) : Nullable<a> {
    return unsafe;
}

var safe = sanitize(toResultFromExternalCodeYouCannotTrust()); // <-- 100% safe to use

Dengan demikian permintaan tersebut harus ditutup karena tidak ada masalah lagi. Kasus ditutup, kelas dibubarkan.

@aleksey-bykov

Dengan demikian permintaan tersebut harus ditutup karena tidak ada masalah lagi.

Anda tidak bisa serius, kan? Kami telah memberi tahu Anda berkali-kali bahwa pola untuk tipe aljabar yang dapat dibatalkan tidak terkait dengan masalah ini.

Saya _not_ akan membungkus setiap var x: number dalam kode saya menjadi Nullable<number> atau bahkan lebih baik NonNullable<x> . Itu terlalu banyak di atas kepala. Namun saya ingin tahu bahwa melakukan x *= 2 adalah 100% aman, di mana pun ini terjadi dalam kode saya.

Kode Anda tidak menyelesaikan masalah menggunakan perpustakaan pihak ke-3. Saya _tidak_ akan memanggil sanitize di _setiap_ perpustakaan pihak ketiga, atau bahkan api DOM bawaan, yang saya panggil. Selain itu saya tidak ingin menambahkan isNothing(safe) _di semua tempat_. Yang saya inginkan adalah dapat memanggil let squares = [1,2,3].map(x => x*x) dan 100% yakin pada waktu kompilasi bahwa squares.length aman. Dengan _any_ API yang saya gunakan.

Demikian pula saya ingin dokumentasi untuk perpustakaan JS pihak ke-3 yang suatu fungsi menerima null sebagai input atau tidak. Saya ingin tahu pada waktu kompilasi apakah $(element).css(null) OK atau error.

Saya ingin bekerja di lingkungan tim, di mana saya tidak dapat memastikan bahwa setiap orang menggunakan pola kompleks seperti pola Anda secara konsisten. Jenis Nulllable sama sekali tidak melakukan apa pun untuk mencegah dev melakukan let x: number = null; x.toString() (atau sesuatu yang kurang bodoh, tetapi dengan efek yang sama).

Dan seterusnya dan seterusnya. Tiket ini _jauh_ dari tutup dan masalahnya masih 100% ada.

Anda tidak bisa serius, kan?

Saya cukup serius.

Saya tidak akan membungkus setiap ...

Kenapa tidak? Dengan sintaks ! atau apa pun yang Anda dorong, Anda tetap harus melakukannya.

atau bahkan lebih baik NonNullable

Seharusnya Nullable apa yang kita modelkan adalah tipe nullable yang dapat memiliki nilai non-nullable atau null dan dinyatakan secara eksplisit. Bertentangan dengan tipe konvensional yang SELALU dapat memiliki nilai atau nol dan disebut number menyiratkan bahwa Anda harus memeriksanya untuk nol sebelum menggunakan.

Itu terlalu banyak di atas kepala.

Di mana?

Kode Anda tidak menyelesaikan masalah menggunakan perpustakaan pihak ke-3.

Itu tidak.

Saya tidak akan memanggil sanitize di setiap perpustakaan pihak ke-3, atau bahkan api DOM bawaan, yang saya panggil.

Anda tidak harus. Yang perlu Anda lakukan adalah memperbaiki file definisi lib pihak ke-3 itu dengan mengganti semua yang bisa menjadi null dengan Nullable<*>

Demikian pula saya ingin dokumentasi untuk perpustakaan JS pihak ke-3 yang suatu fungsi menerima null sebagai input atau tidak.

Hal yang sama. Tetapkan metode itu sebagai menerima Nullable<string> alih-alih string .

Jenis Nullable Anda sama sekali tidak mencegah dev melakukan let x: number = null; x.toString()

Memang tidak. Anda perlu memblokir kata kunci null menggunakan linter.

Ayo solusi saya adalah 95% bekerja dan 100% praktis dan tersedia HARI INI tanpa harus membuat keputusan sulit dan membuat semua orang pada halaman yang sama. Ini adalah pertanyaan apa yang Anda cari: solusi yang bekerja yang tidak terlihat seperti yang Anda harapkan tetapi tetap berhasil, atau membuatnya persis seperti yang Anda inginkan dengan semua jack belakang dan ceri di atasnya

Dalam 1.4 dan 1.5, tipe void tidak mengizinkan anggota Object manapun termasuk seperti .toString(), jadi type Nothing = void; seharusnya cukup daripada membutuhkan modul (kecuali ini diubah lagi di 1.6). http://bit.ly/1OC5h8d

@aleksey-bykov itu tidak benar. Anda masih harus berhati-hati untuk membersihkan semua nilai yang mungkin nol. Anda tidak akan mendapatkan peringatan jika Anda lupa melakukannya. Ini adalah peningkatan, pasti (lebih sedikit tempat untuk berhati-hati).

Juga, untuk mengilustrasikan bagaimana peretasan tidak akan membuat Anda terlalu jauh, coba saja

if (isNothing(something)) {
  console.log(something.toString())
}

Saya tidak akan membungkus setiap ...

Kenapa tidak? Dengan ! sintaks atau apa pun yang kalian dorong, Anda harus tetap melakukannya.

OK, saya _bisa_ melakukan itu jika semuanya sudah terpecahkan. Dan jika semuanya terpecahkan, mungkin TS bahkan dapat mempertimbangkan beberapa gula bahasa untuk itu.

atau bahkan lebih baik NonNullable

Seharusnya Nullable apa yang kita modelkan adalah tipe nullable yang dapat memiliki nilai non-nullable atau null dan dinyatakan secara eksplisit. Bertentangan dengan tipe konvensional yang SELALU dapat memiliki nilai atau nol dan disebut nomor yang menyiratkan Anda harus memeriksanya untuk nol sebelum menggunakan.

Bukannya saya hanya ingin menyingkirkan pengecualian nol. Saya ingin dapat mengekspresikan tipe yang tidak dapat dibatalkan juga, dengan keamanan statis.
Katakanlah Anda memiliki metode yang selalu mengembalikan nilai. Seperti toString() selalu mengembalikan string non null.
Apa pilihan Anda di sana?
let x: string = a.toString();
Ini tidak baik karena tidak ada validasi statis bahwa x.length aman. Dalam hal ini, tetapi seperti yang Anda katakan string bawaan mungkin null jadi itu adalah _status quo_.
let x = sanitize(a.toString());
OK sekarang saya tidak dapat menggunakannya tanpa pemeriksaan nol sehingga kode _is_ aman. Tetapi saya tidak ingin menambahkan if (isNothing(x)) mana pun saya menggunakan x dalam kode saya! Ini jelek dan tidak efisien, karena saya sangat tahu bahwa x tidak nol pada waktu kompilasi. Melakukan (<string>x).length lebih efisien tetapi masih jelek untuk melakukannya di mana pun Anda ingin menggunakan x .

Yang ingin saya lakukan adalah:

let x = a.toString(); // documented, non-null type string (string! if you want to)
x.length; // statically OK

Anda tidak dapat mencapai ini tanpa dukungan bahasa yang tepat, karena semua tipe di JS (dan TS 1.6) selalu dapat dibatalkan.

Saya ingin mengatakan bahwa pemrograman dengan tipe non-opsional (nullable) adalah praktik yang sangat baik, sebanyak yang Anda bisa. Jadi apa yang saya jelaskan di sini adalah skenario penting, bukan pengecualian.

Itu terlalu banyak di atas kepala.

Di mana?

Lihat jawaban saya sebelumnya.

Kode Anda tidak menyelesaikan masalah menggunakan perpustakaan pihak ke-3.

Itu tidak.

Hanya setengah dari masalah yang terpecahkan. Dengan asumsi bahwa definisi pustaka di mana diperbarui seperti yang Anda usulkan: setiap input yang dapat dibatalkan atau nilai yang dikembalikan diganti dengan Nullable<X> alih-alih X.

Saya masih dapat meneruskan null ke fungsi yang tidak menerima parameter null . OK fakta ini sekarang _documented_ (yang sudah bisa kita lakukan dengan komentar JSDoc atau apa pun) tapi saya ingin _enforced_ pada waktu kompilasi.
Contoh: declare find<T>(list: T[], predicate: (T) => bool) . Kedua parameter tidak boleh nol (saya belum pernah menggunakan Nullable ) namun saya dapat melakukannya find(null, null) . Kesalahan lain yang mungkin adalah bahwa deklarasi mengatakan saya harus mengembalikan non-null bool dari predikat, namun saya dapat melakukannya: find([], () => null) .

Jenis Nullable Anda sama sekali tidak mencegah dev melakukan let x: number = null; x.toString()

Memang tidak. Anda perlu mencekal kata kunci null menggunakan linter.

Melakukannya dan memberikan variabel global nothing sebagai gantinya tidak akan membantu Anda dengan kasus yang saya sebutkan di poin preseden, bukan?

Dari sudut pandang saya, ini bukan 95%. Saya merasa sintaksnya menjadi jauh lebih buruk untuk banyak hal umum dan saya masih belum memiliki semua keamanan statis yang saya inginkan ketika saya berbicara tentang tipe yang tidak dapat dibatalkan.

Anda masih harus berhati-hati untuk membersihkan semua nilai yang mungkin nol.

Jenis pekerjaan pemisahan yang sama yang harus Anda lakukan dengan tipe hipotetis non-nullable yang kalian bicarakan di sini. Anda harus meninjau semua file definisi Anda dan meletakkan ! jika sesuai. Bagaimana itu berbeda?

Anda tidak akan mendapatkan peringatan jika Anda lupa melakukannya.

Hal yang sama. Hal yang sama.

ilustrasikan bagaimana peretasan tidak akan membuat Anda terlalu jauh, coba saja

Meskipun Anda benar dengan cara saya mendefinisikan Tidak ada yang memiliki toString dari Object , tapi.. jika kita mengambil ide @Arnavion (menggunakan void untuk Nothing ) semuanya tiba-tiba klik

Jam tangan

image

Dari sudut pandang saya, ini bukan 95%. Saya merasa sintaksnya menjadi jauh lebih buruk untuk banyak hal umum dan saya masih belum memiliki semua keamanan statis yang saya inginkan ketika saya berbicara tentang tipe yang tidak dapat dibatalkan.

man, Anda baru saja meyakinkan saya, pola ini bukan untuk Anda dan tidak akan pernah, tolong jangan pernah menggunakannya dalam kode Anda, terus minta jenis yang tidak dapat dibatalkan, semoga berhasil, maaf telah mengganggu Anda dengan omong kosong seperti itu

@aleksey-bykov
Saya melihat apa yang Anda coba lakukan, meskipun menurut saya Anda belum menyelesaikan semua kasus dan Anda tampaknya membuat solusi di sepanjang jalan ketika seseorang menunjukkan lubang (seperti penggantian null oleh void dalam contoh terakhir).

Pada akhirnya _maybe_ Anda akan membuatnya bekerja seperti yang saya inginkan. Anda akan menggunakan sintaks yang kompleks untuk tipe nullable, Anda akan melarang dengan linter sumber null dalam program yang pada akhirnya dapat membuat tipe nullable non-nulls. Sepanjang jalan Anda harus meyakinkan semua orang untuk memperbarui perpustakaan mereka menggunakan konvensi non-standar Anda, jika tidak, itu tidak ada gunanya (ingat, ini berlaku untuk _all_ solusi untuk masalah nol, bukan hanya milik Anda).

Pada akhirnya Anda mungkin berhasil memutar sistem tipe untuk menghindari tipe null built-in dan memiliki tanda centang yang kami inginkan dilampirkan ke tipe Nothing .

Pada titik ini, tidakkah Anda merasa bahasa harus mendukungnya? Bahasa dapat mengimplementasikan semuanya dengan cara yang hampir sama dengan yang Anda inginkan (secara internal). Tapi di atas itu Anda akan memiliki sintaks yang bagus, kasus tepi disetrika, tidak perlu linter eksternal dan tentu saja adopsi yang lebih baik oleh definisi pihak ke-3. Bukankah itu lebih masuk akal?

Pada titik ini, tidakkah Anda merasa bahasa harus mendukungnya?

Mari kita bicara tentang apa yang saya pikirkan. Saya pikir akan menyenangkan untuk bangun di dunia di mana TypeScript memiliki tipe yang tidak dapat dibatalkan besok. Sebenarnya ini pikiran saya pergi tidur dengan setiap malam. Tapi itu tidak pernah terjadi pada hari berikutnya dan saya merasa frustrasi. Kemudian saya pergi bekerja dan mengalami masalah yang sama dengan nol berulang-ulang sampai baru-baru ini ketika saya memutuskan untuk mencari pola yang dapat membuat hidup saya lebih mudah. Sepertinya aku menemukannya. Apakah saya masih berharap kami memiliki non-nullables di TypeScript? Tentu hal yang saya lakukan. Bisakah saya hidup tanpa mereka dan frustrasi? Sepertinya aku bisa.

Apakah saya masih berharap kami memiliki non-nullables di TypeScript? Tentu hal yang saya lakukan.

Lalu mengapa Anda ingin menutup masalah ini? :senyum:

Dengan demikian permintaan tersebut harus ditutup karena tidak ada masalah lagi. Kasus ditutup, kelas dibubarkan.

Saya senang Anda menemukan solusi untuk kebutuhan Anda, tetapi seperti Anda, saya masih berharap suatu hari TS mendapat dukungan yang layak. Dan saya percaya bahwa ini mungkin masih terjadi (tidak dalam waktu dekat, ingatlah). Tim C# saat ini sedang melakukan brainstorming yang sama dan mungkin ini dapat membantu memajukan segalanya. Jika C# 7 berhasil mendapatkan tipe non-null (yang belum pasti) tidak ada alasan TS mungkin tidak melakukan hal yang sama.

@aleksey-bykov

Jadi untuk menyederhanakan

var nothing: void = null;
function isNothing<a>(value: a | void): value is void {
    return value == null;
}
var something = Math.random() > 0.5 ? 'hey!' : nothing;

dan sekarang Anda juga dapat menggunakannya tanpa pembungkus fungsi apa pun - definisi tipe yang tepat sudah cukup.

Itu pada dasarnya solusi yang awalnya membuat saya agak senang sampai putus asa untuk mengandalkannya .

Anda harus meninjau semua file definisi Anda dan memasukkan ! di mana sesuai. Bagaimana itu berbeda?

Perbedaan utama ketika menggunakan standar, fitur bahasa resmi alih-alih peretasan adalah bahwa komunitas (DefinitelyTyped) ada di sana untuk membantu menulis, menguji, dan membagikan file definisi alih-alih semua pekerjaan ekstra yang menumpuk di atas proyek Anda (baik di bawah benar-benar :senyum:).

Saya menganjurkan untuk " --noImplicitNull ". Itu akan membuat semua jenis default ke non-nullable, segera memberikan umpan balik tentang potensi masalah dalam kode yang ada. Jika ada "!", itu harus berperilaku seperti Swift untuk membuat pemeriksa huruf mengabaikan kemungkinan nol (tidak aman dilemparkan ke non-nullable) - meskipun itu bertentangan dengan ES7+ "pengiriman akhirnya" operator pada janji jadi itu mungkin bukan yang terbaik ide.

Jika itu tampak terlalu radikal dalam hal kerusakan kompatibilitas ke belakang, itu benar-benar salah saya. Saya harus benar-benar mencoba untuk menemukan waktu dan mencobanya di garpu untuk membuktikan bahwa itu tidak benar-benar radikal seperti yang terlihat. Bahkan menambahkan "!" lebih radikal: akan membutuhkan lebih banyak perubahan untuk memanfaatkannya karena sebagian besar nilai tidak benar-benar nol; --noImplicitNull lebih bertahap karena Anda akan menemukan lebih banyak kesalahan dengan memperbaiki definisi tipe yang salah mengklaim nilai bukan nol dan tanpa perbaikan itu Anda akan mendapatkan perilaku yang sama kecuali untuk penetapan nol, nilai yang tidak diinisialisasi, dan pemeriksaan yang terlupakan untuk bidang objek opsional .

Menambahkan suara saya ke diskusi, saya suka bagaimana Kotlin menyelesaikan masalah, yaitu

  • variabel sendiri tidak memiliki tipe nullable secara default, '?' perubahan itu
  • variabel eksternal dapat dibatalkan, a '!!' menimpa
  • cek nol adalah tipe pemeran
  • kode eksternal dapat dianalisis dengan alat yang menghasilkan anotasi

Membandingkan:
1
2

  • jika a|void dilarang, seseorang dapat beralih ke a|Nothing

Perbedaan utama saat menggunakan fitur bahasa resmi standar dan bukan peretasan adalah bahwa komunitas (DefinitelyTyped) ada untuk membantu menulis, menguji, dan membagikan file definisi

  • tidak dapat melihat mengapa menggunakan fitur yang 100% sah adalah peretasan
  • jangan melebih-lebihkan keterampilan komunitas, banyak (jika tidak sebagian besar) definisi ada yang berkualitas rendah dan bahkan yang seperti, katakanlah, jquery cukup sering tidak dapat digunakan seperti yang tertulis tanpa beberapa perubahan
  • sekali lagi, sebuah pola tidak akan pernah sebagus fitur lengkap, harap semua orang mendapatkannya, tidak diragukan lagi
  • sebuah pola dapat menyelesaikan masalah Anda hari ini sedangkan fitur mungkin atau mungkin tidak dekat
  • jika suatu situasi dapat diselesaikan secara efisien (hingga 95%) dengan menggunakan AB dan C mengapa ada orang yang membutuhkan D untuk melakukan hal yang sama? mengidam gula? mengapa kita tidak fokus pada sesuatu yang tidak dapat diselesaikan dengan pola?

lagi pula, ada pemecah masalah dan perfeksionis, seperti yang ditunjukkan bahwa masalahnya dapat dipecahkan

(Jika Anda tidak ingin bergantung pada tipe void, Tidak ada juga class Nothing { private toString: any; /* other Object.prototype members */ } yang akan memiliki efek yang sama. Lihat #1108)

_Catatan: tidak ada di sini tentang bikeshedding sintaks. Tolong jangan ambil itu
Dengan demikian._

Sederhananya: inilah masalah dengan solusi TS 1.6 yang diusulkan
di sini: ini dapat berfungsi dengan baik, tetapi tidak berlaku untuk perpustakaan standar atau
sebagian besar file definisi. Ini juga tidak berlaku untuk operator matematika. jika kamu mau
untuk mengeluh tentang tipe / runtime boilerplate yang berlebihan, coba lakukan generik
koleksi bekerja di C++ - ini artinya jika dibandingkan, terutama jika Anda mau
iterasi malas atau koleksi yang bukan array (atau lebih buruk lagi, coba
menggabungkan keduanya).

Masalah dengan solusi yang saat ini diusulkan oleh @aleksey-bykov adalah
itu tidak diberlakukan dalam inti bahasa. Misalnya, Anda tidak dapat melakukannya
Object.defineProperty(null, "name", desc) - Anda akan mendapatkan TypeError dari
objek target null . Hal lain: Anda tidak dapat menetapkan properti apa pun ke a
null objek, seperti di var o = null; o.foo = 'foo'; . Anda akan mendapatkan
ReferensiError IIRC. Solusi yang diusulkan ini tidak dapat menjelaskan kasus-kasus tersebut.
Inilah sebabnya kami membutuhkan dukungan bahasa untuk itu.


_Sekarang, sedikit bikeshedding ringan..._

Dan untuk sintaksnya, saya suka singkatnya "?string" dan
Sintaks "!string", tetapi sejauh hasil akhirnya, saya tidak peduli selama saya
tidak menulis ThisIsANullableType<T, U, SomeRidiculousAndUnnecessaryExtraGeneric<V, W>> .

Pada Rabu, 29 Juli 2015, 16:10 Aleksey Bykov [email protected] menulis:

  • jika a|void dilarang, seseorang dapat beralih ke a|Nothing

    Perbedaan utama saat menggunakan standar, fitur bahasa resmi
    alih-alih peretasan adalah bahwa komunitas (DefinitelyTyped) ada untuk membantu
    tulis, uji, dan bagikan file definisi

    tidak dapat melihat mengapa menggunakan fitur yang 100% sah adalah peretasan

    jangan melebih-lebihkan keterampilan komunitas, banyak (jika tidak sebagian besar)
    definisi ada yang berkualitas rendah dan bahkan yang seperti, katakanlah, jquery cukup

    sering tidak dapat digunakan seperti yang tertulis tanpa beberapa perubahan

    sekali lagi, sebuah pola tidak akan pernah sebagus fitur lengkap, harap

    semua orang mendapatkannya, tidak diragukan lagi

    sebuah pola dapat memecahkan masalah Anda hari ini sedangkan fitur mungkin atau mungkin

    jangan dekat-dekat

    jika suatu situasi dapat diselesaikan secara efisien (hingga 95%) dengan menggunakan AB
    dan C mengapa ada orang yang membutuhkan D untuk melakukan hal yang sama? mengidam gula? kenapa harus
    kita fokus pada sesuatu yang tidak bisa diselesaikan dengan pola?

lagi pula, ada pemecah masalah dan perfeksionis, seperti yang ditunjukkan
masalah bisa dipecahkan


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -126082053
.

Dan bisakah kita setidaknya mendapatkan gambaran seperti apa struktur gudang sepeda sebelum kita terobsesi dengan warna apa untuk mengecatnya? Sebagian besar yang saya lihat sejauh ini setelah ~10 komentar pertama adalah "Hei, kami ingin membuat gudang sepeda! Warna apa yang harus kami cat?" Tidak ada tentang memastikan desainnya secara struktural baik. (Lihat # 3192 dan paruh pertama # 1206 untuk beberapa contoh lain dari ini - sebagian besar kebisingan mereda ketika saya akhirnya membuat proposal serius dengan sintaks yang dibuat secara logis dan semantik yang ditentukan sepenuhnya.)

Perlu diingat: ini kemungkinan besar akan menghasilkan refactor utama dan penulisan ulang sebagian dari definisi tipe pustaka standar. Ini juga akan menghasilkan sebagian besar definisi tipe pada PastiTyped yang perlu ditulis ulang untuk versi pertama TS yang mendukung ini. Jadi perlu diingat ini pasti akan melanggar. (Solusinya bisa selalu memancarkan secara default, bahkan ketika kesalahan terkait null terjadi, tetapi untuk memberikan tanda ala --noImplicitAny untuk mengubah perilaku itu.)

Diakui saya sedikit di atas kepala saya di sini. Namun, saya mencari cendekiawan.google untuk "nullable javascript" dan "nullability javascript" :
Memahami TypeScript
memiliki tabel tipe yang bagus di TypeScript (modul generik).

Jenis Dependen untuk JavaScript
tampaknya penting. kode sumber menghilang

Percaya, tapi Verifikasi: Pengetikan Dua Fase untuk DinamisBahasa
Jenis Penyempurnaan untuk Bahasa Scripting
menawarkan solusi berdasarkan TypeScript.
("Singkatan t? singkatan dari t + null." ; terdengar seperti #186 )

@afrische Banyak dari ini sudah digunakan secara praktis di pemeriksa tipe JS. Flow menggunakan sebagian besar idiom dalam "Percayai, tetapi Verifikasi", misalnya. Ada juga Infernu , pemeriksa tipe WIP JS yang sangat bergantung pada inferensi tipe 1 Hindley-Milner untuk menyimpulkan tipenya. Tapi saya ngelantur...

[1] Haskell, OCaml, dll. menggunakan versi modifikasi juga untuk sistem tipenya.

Saat ini saya bermain-main dengan garpu TS yang menganggap tipe tidak dapat dibatalkan secara default dan itu membuat tipe Null (yang ada) dapat direferensikan dengan null , misalnya string | null . Saya juga menambahkan sintaks untuk string? yang hanya merupakan jalan pintas untuk tipe gabungan yang sama.

Ketika Anda memikirkannya secara mendalam, itu sama dengan @aleksey-bykov, tetapi di mana null adalah tipe Nothing , built-in.

Dan itu bahkan tidak rumit untuk dilakukan karena sistem tipenya sudah cukup baik dalam menangani semua ini!

Di mana hal-hal menjadi berbulu adalah bahwa kami ingin memiliki jalur transisi yang mulus. Kami membutuhkan beberapa kompatibilitas mundur, _setidaknya_ dengan file definisi yang ada (kompatibilitas dengan proyek itu sendiri bisa menjadi tanda pilihan, meskipun akan lebih baik jika arti dari sepotong kode -- katakanlah di internet -- tidak' t bergantung pada flag compiler global).

@afrische ide menggunakan string? dalam proyek Anda sendiri tetapi menggunakan string! di .d.ts bisa menjadi pendekatan baru. Meskipun saya tidak begitu menyukai dualitas sewenang-wenang yang diciptakannya. Mengapa string dibatalkan dan beberapa file dan tidak dapat dibatalkan pada yang lain? Sepertinya aneh.

Jika lebih suka tidak memiliki string yang dapat dibatalkan di beberapa file dan tidak di yang lain.
Saya suka ide @impinball untuk memiliki flag opt-out di kompiler dan memperkenalkannya sebagai perubahan yang melanggar (terkesiap: perubahan besar dari argumen saya sebelumnya). Dengan asumsi bahwa sintaks baru tidak akan menyebabkan kesalahan saat menggunakan flag opt-out tentunya.

Utas ini berusia lebih dari satu tahun dan sulit untuk mengetahui apa semua desain dan masalah yang relevan dengan hampir 300 komentar dan hanya sedikit dari tim TS itu sendiri.

Saya berencana untuk memigrasi basis kode besar ke Flow, Closure Compiler atau TypeScript tetapi kurangnya keamanan nol di TypeScript adalah pemecah masalah nyata.

Tanpa membaca seluruh utas, saya tidak dapat menemukan apa yang salah dengan menambahkan specifier nullability ! dan ? sambil mempertahankan perilaku yang ada untuk tipe yang tidak memilikinya, jadi ini sebagai proposal :

Usul

declare var foo:string // nullability unspecified
declare var foo:?string // nullable
declare var foo:!string // non-nullable

Dengan ?string superset dari !string , dan string keduanya superset dan subset dari keduanya, yaitu hubungan antara string dan keduanya ?string dan !string sama dengan hubungan antara any dan semua tipe lainnya, yaitu tipe telanjang tanpa ! atau ? seperti any sehubungan dengan nullibility.

Aturan berikut berlaku:

| Ketik | Berisi | Menyediakan | Dapat menetapkan null ? | Bisakah berasumsi tidak null ? |
| --- | --- | --- | --- | --- |
| T | T , !T , ?T | T , ?T , !T | ya | ya |
| ?T | T , !T , ?T | T , ?T | ya | tidak (diperlukan jenis pelindung) |
| !T | T , !T | T , ?T , !T | tidak | ya |

Ini memberikan keamanan nol tanpa merusak kode yang ada, dan memungkinkan basis kode yang ada untuk memperkenalkan keamanan nol secara bertahap, seperti any memungkinkan basis kode yang ada untuk memperkenalkan keamanan jenis secara bertahap.

Contoh

Berikut adalah beberapa kode dengan kesalahan referensi nol:

function test(foo:Foo) { foo.method(); }
test(null);

Kode ini masih lolos. null masih dapat ditetapkan ke Foo dan Foo masih dapat dianggap bukan nol.

function test(foo:!Foo) { foo.method(); }
test(null);

Sekarang test(null) salah, karena null tidak dapat ditetapkan ke !Foo .

function test(foo:?Foo) { foo.method(); }
test(null);

Sekarang foo.bar() salah, karena pemanggilan metode pada ?Foo tidak diizinkan (yaitu kode harus memeriksa null ).

Terima kasih! Dan saya sangat menyukai ide ini. Meskipun, demi kompatibilitas,
bisakah kita membuat ?T dan T hanya alias, seperti Array<T> dan T[] ? Dia
akan menyederhanakan banyak hal, dan versi yang tidak didekorasi masih secara teknis
tidak dapat dibatalkan.

Pada Jumat, 28 Agustus 2015, 07:14 Jesse Schalken [email protected] menulis:

Utas ini sudah berumur lebih dari satu tahun dan sulit untuk mengetahui apa itu semua
desain dan masalah yang relevan dengan hampir 300 komentar dan hanya sedikit
dari tim TS sendiri.

Saya berencana untuk memigrasi basis kode besar ke Flow, Closure Compiler atau
TypeScript tetapi kurangnya keamanan jenis di TypeScript adalah pemecah masalah nyata.

Tanpa membaca seluruh utas, saya tidak dapat mengetahui apa yang salah
dengan menambahkan keduanya! dan ? penentu nullability sambil mempertahankan
perilaku yang ada untuk tipe yang tidak memilikinya, jadi ini sebagai proposal:
Usul

deklarasikan var foo:string // nullability tidak ditentukandeklarasikan var foo:?string // nullable,deklarasikan var foo:!string // non-nullable

Dengan ?string sebuah superset dari !string, dan string baik superset maupun subset
keduanya, yaitu hubungan antara string dan keduanya ?string dan !string
adalah sama dengan hubungan antara setiap dan semua jenis lainnya, yaitu a
tipe telanjang tanpa ! atau ? adalah seperti apapun sehubungan dengan nullibility.

Aturan berikut berlaku:
Jenis Berisi Menyediakan Dapat menetapkan null? Bisa berasumsi tidak nol? TT, !T, ?TT,
?T, !T ya ya ?TT, !T, ?TT, ?T ya tidak (diperlukan jenis pelindung) !TT, !TT,
?T, !T tidak ya

Ini memberikan keamanan nol tanpa melanggar kode yang ada, dan memungkinkan
basis kode yang ada untuk memperkenalkan keamanan nol secara bertahap, sama seperti apa pun
memungkinkan basis kode yang ada untuk memperkenalkan keamanan tipe secara bertahap.
Contoh

Berikut adalah beberapa kode dengan kesalahan referensi nol:

tes fungsi(foo:Foo) { foo.method(); }tes(nol);

Kode ini masih lolos. null masih dapat dialihkan ke Foo dan Foo masih bisa
diasumsikan tidak nol.

tes fungsi(foo:!Foo) { foo.method(); }tes(nol);

Sekarang tes(null) salah, karena null tidak dapat ditetapkan ke !Foo.

tes fungsi(foo:?Foo) { foo.method(); }tes(nol);

Sekarang foo.bar() dalam kesalahan, karena pemanggilan metode pada ?Foo tidak diizinkan
(yaitu kode harus memeriksa nol).


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135743011
.

@impinball

Terima kasih! Dan saya sangat menyukai ide ini. Meskipun, demi kompatibilitas, bisakah kita membuat ?T dan T hanya alias, seperti Array<T> dan T[] ? Itu akan menyederhanakan banyak hal, dan versi yang tidak didekorasi secara teknis masih dapat dibatalkan.

_Keseluruhan ide_ adalah bahwa T , ?T dan !T adalah tiga hal yang berbeda, dan itu _is_ demi kompatibilitas (kompatibilitas dengan kode TS yang ada). Saya tidak tahu bagaimana menjelaskannya dengan lebih baik, maaf. Maksudku, aku membuat meja kecil dan semuanya.

Oke. Poin bagus. Saya salah mengartikan tabel di bagian itu, dan saya mengabaikannya
kasus perpindahan file definisi yang ada, menyebabkan masalah dengan
aplikasi dan perpustakaan lainnya.

Pada Jumat, 28 Agustus 2015, 11:43 Jesse Schalken [email protected] menulis:

@impinball https://github.com/impinball

Terima kasih! Dan saya sangat menyukai ide ini. Meskipun, demi kompatibilitas,
bisakah kita membuat ?T dan T hanya alias, seperti Arraydan T[]? Itu akan
menyederhanakan banyak hal, dan versi yang tidak didekorasi secara teknis masih dapat dibatalkan.

_Keseluruhan ide_ adalah bahwa T, ?T dan !T adalah tiga hal yang berbeda,
dan _is_ itu demi kompatibilitas (kompatibilitas dengan TS yang ada
kode). Saya tidak tahu bagaimana menjelaskannya dengan lebih baik, maaf. Maksudku, aku membuat
meja kecil dan segalanya.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135810311
.

maaf karena keledai pintar, tapi ini tidak masuk akal

?string superset dari !string , dan string keduanya superset dan subset dari keduanya

dari teori himpunan kita mengetahui bahwa himpunan A adalah himpunan bagian dan superset dari himpunan B adalah jika dan hanya jika A = B

jika demikian, ketika Anda mengatakan "dari keduanya" itu hanya bisa berarti segalanya-dari-?string = segalanya-dari-string dan segalanya-dari-!string = segalanya-dari-string

jadi akhirnya semuanya-dari-?string = segalanya-dari-!string

dari mana semua orang turun, bus tidak ke mana-mana, bus keluar dari layanan

@aleksey-bykov Mungkin kasus ungkapan yang buruk. Saya pikir maksudnya string lebih seperti ?string | !string , mengambil batasan yang paling permisif.

Semua hal ini serupa dalam cakupannya dengan anotasi tipe waktu kompilasi @Nullable dan @NonNull / @NotNull yang semakin umum, dan cara kerjanya dengan tipe yang tidak diberi notasi.

Dan saya pasti akan mendukung flag baru untuk tipe yang secara implisit dianggap tidak dapat dibatalkan, terutama primitif.

Bendera "tidak dapat dibatalkan secara default" akan bagus, tetapi itu juga akan merusak sejumlah besar definisi PastiTyped. Ini termasuk definisi Angular dan Node di sana, dan itu akan membutuhkan banyak pekerjaan yang membosankan untuk diperbaiki. Mungkin juga memerlukan backport sehingga tipe baru masih mengurai bukan sebagai kesalahan sintaks, tetapi nullability tidak dicentang. Backport seperti itu akan menjadi satu-satunya cara praktis untuk mengurangi kerusakan dengan flag seperti itu, terutama karena definisi diperbarui untuk tipe nullable. (Orang masih menggunakan TypeScript 1.4 dalam pengembangan, terutama dalam proyek yang lebih besar.)

@impinball

Orang masih menggunakan TypeScript 1.4 dalam pengembangan, terutama dalam proyek yang lebih besar.

Saya pikir cerita kompatibilitas untuk fitur ini sudah cukup sulit tanpa mencoba membuat definisi baru yang kompatibel dengan kompiler lama (tipe non-null FWIW hampir sepele untuk ditambahkan ke kompiler TS saat ini).

Jika orang tetap menggunakan TS lama maka mereka harus menggunakan definisi lama. (Saya tahu itu tidak praktis).
Maksudku hal-hal yang akan pecah pula. Segera TS 1.6 akan menambahkan jenis persimpangan dan definisi yang menggunakannya juga tidak akan kompatibel.

BTW Saya terkejut orang tetap di 1.4, ada begitu banyak cinta di 1.5.

@aleksey-bykov

maaf karena keledai pintar, tapi ini tidak masuk akal

Saya berasumsi Anda menyebut diri Anda "keledai pintar" karena Anda tahu betul dari sisa posting apa arti "superset dan subset dari keduanya". Tentu saja tidak masuk akal jika diterapkan pada set yang sebenarnya.

any dapat diberikan jenis apa pun (berperilaku sebagai superset dari semua jenis) dan diperlakukan sebagai jenis apa pun (berperilaku sebagai subset dari semua jenis). any berarti "memilih keluar dari jenis memeriksa nilai ini".

string dapat ditetapkan dari !string atau ?string (berperilaku sebagai superset dari keduanya) dan dapat diperlakukan sebagai !string dan ?string (berperilaku sebagai bagian dari mereka). string berarti "memilih keluar dari pemeriksaan nullability untuk nilai ini", yaitu perilaku TS saat ini.

@impinball

Dan saya pasti akan mendukung flag baru untuk tipe yang secara implisit dianggap tidak dapat dibatalkan, terutama primitif.

@RyanCavanaugh secara eksplisit mengatakan:

Bendera yang mengubah semantik suatu bahasa adalah hal yang berbahaya. [...] Sangat penting bahwa seseorang yang melihat sepotong kode dapat "mengikuti" sistem tipe dan memahami kesimpulan yang dibuat. Jika kita mulai memiliki banyak flag yang mengubah aturan bahasa, ini menjadi tidak mungkin.

Satu-satunya hal yang aman untuk dilakukan adalah menjaga semantik penugasan tetap sama dan mengubah apa yang salah vs apa yang tidak bergantung pada bendera, seperti cara kerja noImplicitAny hari ini.

Itu sebabnya proposal saya mempertahankan perilaku yang ada untuk tipe yang tidak memiliki penentu nullability ( ! atau ? ). Bendera _only_ aman untuk ditambahkan akan menjadi salah satu yang melarang jenis yang tidak memiliki penentu nullability, yaitu hanya membutuhkan kode yang lewat dan menyebabkannya salah, itu tidak mengubah artinya. Saya berasumsi noImplicitAny diizinkan untuk alasan yang sama.

@jesseschalken

Maksud saya bahwa dalam konteks variabel yang diketik secara implisit tidak diinisialisasi
ke null atau undefined dalam kasus seperti ini:

var a = new Type(); // type: !Type
var b = 2; // type: !number
var c = 'string'; // type: !string
// etc...

Maaf bila membingungkan.

Pada Jumat, 28 Agustus 2015, 19:54 Notifikasi Jesse [email protected] menulis:

@impinball https://github.com/impinball

Dan saya pasti akan mendukung bendera baru untuk tipe makhluk
secara implisit dianggap sebagai non-nullable, terutama primitif.

@RyanCavanaugh https://github.com/RyanCavanaugh secara eksplisit mengatakan:

Bendera yang mengubah semantik suatu bahasa adalah hal yang berbahaya. [...]
Sangat penting bahwa seseorang yang melihat sepotong kode dapat "mengikuti"
dengan sistem tipe dan memahami kesimpulan yang dibuat. Jika
kami mulai memiliki banyak bendera yang mengubah aturan bahasa,
ini menjadi tidak mungkin.

Satu-satunya hal yang aman untuk dilakukan adalah menjaga semantik dari
penugasan sama dan ubah apa yang salah vs apa yang tidak tergantung
pada bendera, seperti cara kerja noImplicitAny hari ini.

Itu sebabnya proposal saya mempertahankan perilaku yang ada untuk tipe yang kurang
penentu nullability (! atau ?). Bendera _only_ aman untuk ditambahkan adalah
yang tidak mengizinkan tipe yang tidak memiliki penentu nullability, yaitu hanya
mengambil kode yang lewat dan menyebabkannya salah, itu tidak mengubah artinya. Saya
menganggap noImplicitAny diizinkan untuk alasan yang sama.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135916676
.

@jeffmcaffer apa yang Anda coba katakan dengan mengubah arti asli kata superset dan subset masih dapat diartikulasikan dalam set dengan mudah: himpunan nilai string adalah gabungan nilai !string dan ?string berarti apa saja dari !string atau/dan ?string milik string

@impinball Sekali lagi, bendera seperti itu akan mengubah arti kode yang ada, yang (dari membaca komentar @RyanCavanaugh ) tidak diperbolehkan. export var b = 5; sekarang akan mengekspor !number mana sebelumnya mengekspor number .

Ya, dalam arti tertentu. Untuk sedikit lebih teknis, ia menerima serikat pekerja, tapi
itu menyediakan persimpangan. Pada dasarnya, kedua tipe tersebut dapat dihitung sebagai
string , dan string dapat diteruskan untuk kedua jenis.

Pada Jum, 28 Agustus 2015, 20:20 Aleksey Bykov [email protected] menulis:

@impinball https://github.com/impinball apa yang Anda coba katakan oleh
mengubah arti asli kata superset dan subset masih bisa
diartikulasikan dalam bentuk himpunan dengan mudah: himpunan nilai string adalah a
_union_ nilai !string dan ?string yang berarti apa saja dari !string
atau/dan ?string milik string


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135920233
.

Jelas, itu tidak dimaksudkan untuk diaktifkan secara default. Dan sebagian besar file definisi
tidak akan terpengaruh. Secara teknis, perubahan apa pun yang tidak murni
aditif (bahkan menambahkan argumen baru ke suatu fungsi tidak ada dalam JavaScript) memiliki
kapasitas untuk memecahkan aplikasi. Ini mirip dalam cakupannya dengan
noImplicitAny karena memaksa pengetikan yang sedikit lebih eksplisit. Dan saya
tidak percaya itu bisa merusak lebih dari itu, terutama karena satu-satunya
cara ini dapat memengaruhi file lain adalah melalui ekspor dari TypeScript yang sebenarnya
file sumber. (Bendera lainnya merusak banyak file definisi, dan dinonaktifkan
cara pengujian bebek yang sering.)

Pada Jumat, 28 Agustus 2015, 20:21 Jesse Schalken [email protected] menulis:

@impinball https://github.com/impinball Sekali lagi, bendera seperti itu akan berubah
arti dari kode yang ada, yang (dari membaca @RyanCavanaugh
https://github.com/RyanCavanaugh's comments) tidak diperbolehkan. ekspor var
b = 5; sekarang akan mengekspor nomor ! yang sebelumnya mengekspor nomor.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135920315
.

Jelas, itu tidak dimaksudkan untuk diaktifkan secara default.

Itu masih mengubah arti kode. Segera setelah Anda melakukannya, Anda tidak dapat membaca beberapa kode TypeScript dan mengatakan "Saya tahu apa artinya ini", Anda juga tidak dapat menulis perpustakaan yang akan digunakan orang lain, karena arti kode tergantung pada flag yang digunakan untuk kompilasi, dan itu tidak dapat diterima. Anda benar-benar membagi bahasa menjadi dua.

Itu sebabnya proposal saya mempertahankan perilaku yang ada untuk tipe yang tidak memiliki penentu nullability ( ? atau ! ).

@jesseschalken
Jika Anda ingin (dan mengelola) untuk mempertahankan dengan kesetiaan 100% arti dari kode yang ada, maka Anda harus menyerah pada gagasan keamanan nol pada TypeScript. Tidak ada solusi yang dapat digunakan dalam praktik.

Harus meletakkan ? dan ! pada setiap jenis anotasi sudah cukup verbose bagi saya untuk tidak ingin melakukannya, tetapi Anda juga harus menyerah pada semua jenis inferensi. let x = 3 menyimpulkan number hari ini. Jika Anda tidak menerima perubahan di sini, itu berarti Anda harus mengetikkan semuanya secara eksplisit untuk mendapatkan manfaat dari keamanan nol. Bukan sesuatu yang ingin saya lakukan juga.

Ketika manfaatnya lebih besar daripada kerugiannya, beberapa konsesi dapat dibuat. Seperti yang telah ditunjukkan oleh @impinball , tim TS melakukan hal itu dengan noImplicitAny . Ini adalah tanda yang membuat kesalahan baru dalam kode Anda saat Anda mengaktifkannya. Dengan demikian, menyalin-menempelkan kode dari internet, atau bahkan hanya menggunakan perpustakaan TS dapat rusak jika kode yang Anda ambil tidak ditulis di bawah asumsi noImplicitAny (dan itu terjadi pada saya).

Saya pikir keamanan nol dapat diperkenalkan dengan cara yang sama. Arti kodenya sama dan berjalan dengan semantik yang persis sama. Tetapi di bawah bendera baru (misalnya noImplicitNull atau apa pun) Anda akan mendapatkan pemeriksaan tambahan dan peringatan/kesalahan dari kode yang tidak ditulis dengan asumsi noImplicitNull .

Saya pikir keamanan nol dapat diperkenalkan dengan cara yang sama. Arti kodenya sama
dan itu berjalan dengan semantik yang persis sama. Tetapi di bawah bendera baru (katakan noImplicitNull atau apa pun)
Anda akan mendapatkan pemeriksaan tambahan dan peringatan/kesalahan dari kode yang tidak ditulis dengan
asumsi noImplicitNull.

Saya suka pendekatan itu dan tampaknya cara yang logis untuk mengembangkan bahasa. Saya berharap bahwa seiring waktu itu akan menjadi standar de-facto dengan cara yang sama seperti pengetikan yang biasanya ditulis dengan mempertimbangkan noImplicitAny.

Namun, saya pikir hal penting sejauh menyangkut adopsi bertahap adalah memastikan bahwa kode yang ada dapat dimigrasikan modul pada satu waktu dan bahwa kode baru yang ditulis dengan null eksplisit dalam pikiran dapat dengan mudah bekerja dengan kode yang ada yang menggunakan null implisit.

Jadi bagaimana dengan ini:

  • Dengan -noImplicitNull, T menjadi alias untuk !T . Jika tidak, nullability dari T tidak diketahui.
  • Bendera harus dapat diganti per modul dengan menambahkan anotasi di bagian atas, mis. <strong i="18">@ts</strong>:implicit_null . Ini mirip dengan cara Flow mengaktifkan pengecekan tipe pada basis per-modul.
  • Mengubah basis kode yang ada dilakukan dengan terlebih dahulu menambahkan opsi kompiler -noImplicitNull, kemudian memberi anotasi pada semua modul yang ada dengan tanda ' @ts :implicit_null'. Langkah kedua ini bisa jadi sangat otomatis.
  • Saat mengimpor modul, perlu ada kebijakan tentang cara mengonversi jenis jika modul yang diimpor dan yang diimpor memiliki pengaturan implisit yang berbeda.

Ada opsi berbeda untuk poin terakhir itu jika modul dengan null eksplisit mengimpor modul dengan null implisit.

  • Pendekatan ekstrem adalah dengan memperlakukan nullability tipe T sebagai tidak diketahui dan mengharuskan importir untuk secara eksplisit mentransmisikan tipe ke ?T atau !T . Itu akan membutuhkan banyak anotasi di callee tetapi safar.
  • Pendekatan lain adalah memperlakukan semua jenis T diimpor sebagai ?T . Ini juga akan membutuhkan banyak anotasi di pemanggil.
  • Terakhir, semua jenis T diimpor dapat diperlakukan sebagai !T . Ini tentu saja salah dalam beberapa kasus, tetapi ini mungkin pilihan yang paling pragmatis. Mirip dengan cara variabel tipe any dapat ditetapkan ke nilai tipe T .

Pikiran?

@jods4 Bendera noImplicitAny _tidak_ mengubah arti dari kode yang ada, hanya memerlukan kode untuk eksplisit tentang sesuatu yang seharusnya implisit.

| Kode | Tandai | Artinya |
| --- | --- | --- |
| interface Foo { blah; } | | interface Foo { blah:any; } |
| interface Foo { blah; } | noImplicitAny | kesalahan, jenis eksplisit diperlukan |
| var foo = 'blah' | | var foo:string = 'blah' |
| var foo = 'blah' | noImplicitNull | var foo:!string = 'blah' |

Dengan noImplicitNull , sebelum Anda memiliki variabel tempat null dapat ditulis. Sekarang Anda memiliki variabel yang null _tidak dapat_ ditulis. Itu binatang yang sama sekali berbeda dengan noImplicitAny .

@RyanCavanaugh telah mengesampingkan flag yang mengubah semantik kode yang ada. Jika Anda akan mengabaikan persyaratan ekspres dari tim TS maka tiket ini akan bertahan selama satu tahun lagi.

@jesseschalken Maaf tapi saya gagal melihat perbedaannya.
Sebelum noImplicitAny Anda mungkin memiliki ini di kode Anda:
let double = x => x*2;
Ini mengkompilasi dan bekerja dengan baik. Tetapi begitu Anda mengaktifkan noImplicitAny , maka kompiler memberikan kesalahan kepada Anda dengan mengatakan bahwa x secara implisit adalah sembarang. Anda harus memodifikasi kode Anda agar berfungsi dengan flag baru:
let double = (x: any) => x*2 atau lebih baik lagi let double = (x: number) => x*2 .
Perhatikan bahwa meskipun kompiler memunculkan kesalahan, itu masih akan memancarkan kode JS yang berfungsi dengan baik (kecuali Anda mematikan emisi saat kesalahan).

Situasi dengan nol hampir sama menurut saya. Mari kita asumsikan untuk diskusi bahwa dengan flag baru, T tidak dapat dibatalkan dan T? atau T | null menunjukkan gabungan dari tipe T dan null .
Sebelum Anda mungkin memiliki:
let foo: string; foo = null; atau bahkan hanya let foo = "X"; foo = null yang akan disimpulkan menjadi string sama saja.
Ini mengkompilasi dan bekerja dengan baik. Sekarang nyalakan bendera noImplicitNull . Tiba-tiba TS memberikan kesalahan yang menunjukkan bahwa Anda tidak dapat menetapkan null untuk sesuatu yang tidak dinyatakan secara eksplisit. Tetapi kecuali untuk kesalahan pengetikan, kode Anda masih memancarkan _yang sama_, kode JS yang benar.
Dengan bendera Anda perlu menyatakan niat Anda secara eksplisit dan memodifikasi kode:
string? foo; foo = null;

Jadi apa bedanya, sih? Kode selalu terpancar dengan baik dan perilaku runtime-nya tidak berubah sama sekali. Dalam kedua kasus Anda mendapatkan kesalahan dari sistem pengetikan dan Anda harus memodifikasi kode Anda agar lebih eksplisit dalam deklarasi tipe Anda untuk menghilangkannya.

Juga, dalam kedua kasus, dimungkinkan untuk mengambil kode yang ditulis di bawah bendera ketat dan mengompilasinya dengan bendera dimatikan dan masih berfungsi sama dan tanpa kesalahan.

@robertknight Sangat dekat dengan pemikiran saya saat ini.

Untuk modul/definisi yang tidak memilih jenis non-null yang ketat, T pada dasarnya harus berarti: matikan semua jenis kesalahan nol pada jenis ini. Mencoba memaksanya ke T? masih dapat menimbulkan masalah kompatibilitas.

Masalahnya adalah hari ini beberapa T sebenarnya tidak dapat dibatalkan dan beberapa dapat dibatalkan. Mempertimbangkan:

// In a strict module, function len does not accept nulls
function len(x: string): number { return x.length; }
// In a legacy module, some calls to len
let abc: string = "abc";
len(abc);

Jika Anda alias string ke string? di modul lawas, maka panggilan menjadi kesalahan karena Anda meneruskan kemungkinan variabel null ke parameter non-nullable.

@jods4 Baca komentar saya lagi. Lihatlah meja. Saya tidak tahu bagaimana mengungkapkannya dengan lebih jelas.

Contoh Anda secara eksplisit dibuat untuk sampai pada kesimpulan Anda dengan meletakkan definisi foo dan penugasan ke foo samping satu sama lain. Dengan noImplicitAny , satu-satunya kesalahan yang dihasilkan secara khusus dari kode yang perlu diubah (karena tidak mengubah artinya, hanya diperlukan untuk diungkapkan lebih eksplisit). Dengan noImplicitNull , kode yang menyebabkan kesalahan adalah _assignment_ ke foo tetapi kode yang perlu diubah untuk memperbaikinya (untuk memiliki arti lama) adalah _definition_ dari foo . Ini sangat penting, karena flag telah _mengubah arti dari definisi foo _. Penugasan dan definisi dapat dengan jelas berada di sisi yang berbeda dari batas perpustakaan, di mana flag noImplicitNull telah mengubah _makna_ antarmuka publik perpustakaan itu!

bendera mengubah arti dari definisi foo.

Ya benar sekali. Itu berubah dari "Saya tidak tahu sedikit pun apakah variabel dapat berisi nol atau tidak -- dan saya tidak peduli" menjadi "Variabel ini bebas nol". Ada kemungkinan 50/50 bahwa itu benar dan jika tidak, Anda harus menyatakan niat Anda dalam deklarasi. Pada akhirnya, hasilnya sama saja dengan noImplicitAny : Anda harus membuat niat Anda lebih eksplisit dalam deklarasi.

Penugasan dan definisi jelas bisa berada di sisi yang berbeda dari batas perpustakaan

Memang, biasanya deklarasi di perpustakaan dan penggunaan dalam kode saya. Sekarang:

  1. Jika perpustakaan telah memilih null ketat, maka perpustakaan harus mendeklarasikan tipenya dengan benar. Jika perpustakaan mengatakan memiliki tipe null yang ketat dan x tidak dapat dibatalkan, maka saya mencoba menetapkan nol _ memang kesalahan yang harus dilaporkan_.
  2. Jika perpustakaan belum memilih null ketat maka kompiler tidak boleh menimbulkan kesalahan apa pun untuk penggunaannya.

Bendera ini (seperti noImplicitAny ) tidak dapat diaktifkan tanpa menyesuaikan kode Anda.

Saya mengerti maksud Anda, tetapi saya akan mengatakan bahwa kami tidak _mengubah_ kode artinya; lebih tepatnya kita _mengungkapkan arti yang tidak tercacat_ oleh tipe sistem saat ini.

Ini tidak hanya bagus karena akan menangkap kesalahan dalam kode hari ini, tetapi saya akan mengatakan bahwa tanpa mengambil langkah seperti itu tidak akan pernah ada tipe non-null yang dapat digunakan di TS.

Kabar baik untuk tipe yang tidak dapat dibatalkan! Sepertinya tim TS baik-baik saja dengan memperkenalkan perubahan yang melanggar dalam pembaruan TS!
Jika Anda melihat ini:
http://blogs.msdn.com/b/typescript/archive/2015/09/02/announcing-typescript-1-6-beta-react-jsx-better-error-checking-and-more.aspx
Mereka memperkenalkan perubahan yang melanggar (sintaks opsional yang saling eksklusif) dengan jenis file baru, dan mereka memperkenalkan perubahan yang melanggar _tanpa_ jenis file baru (mempengaruhi semua orang).
Itu adalah preseden yang dapat kita perdebatkan dengan tipe yang tidak dapat dibatalkan (misalnya ekstensi .sts atau TypeScript yang ketat dan .sdts yang sesuai).

Sekarang kita hanya perlu mencari tahu apakah kita ingin kompiler mencoba memeriksa tipe yang tidak terdefinisi atau hanya tipe nol (dan sintaks apa) dan kita memiliki proposal yang solid.

@jbondc Bacaan yang sangat menarik. Senang melihat bahwa intuisi saya tentang migrasi ke NNBD (non-nullable secara default) lebih mudah daripada migrasi ke non-nullable opsional telah dikonfirmasi oleh penelitian (urutan besarnya lebih sedikit perubahan untuk dimigrasi, dan dalam kasus Dart, 1- 2 anotasi per 1000 baris kode membutuhkan perubahan nullity, tidak lebih dari 10 bahkan dalam kode null-berat)

Saya tidak yakin apakah kompleksitas dokumen benar-benar mencerminkan kompleksitas tipe yang tidak dapat dibatalkan. Misalnya, di bagian generik mereka membahas 3 jenis parameter tipe formal, lalu menunjukkan bahwa Anda tidak benar-benar membutuhkannya. Di TS, null hanya akan menjadi jenis catatan yang benar-benar kosong (tidak ada properti, tidak ada metode) sementara {} akan menjadi tipe root non-null, dan kemudian generik yang tidak dapat dibatalkan hanyalah G<T extends {}> - tidak perlu membahas beberapa jenis parameter tipe formal sama sekali.

Selain itu tampaknya mereka mengusulkan banyak gula non-esensial, seperti var !x

Survei bahasa yang ada yang telah menangani masalah yang sama adalah permata yang sebenarnya.

Membaca dokumen saya menyadari bahwa Optional / Maybe tipe lebih kuat daripada tipe nullable, terutama dalam konteks umum - sebagian besar karena kemampuan untuk menyandikan Just(Nothing) . Misalnya, jika kita memiliki antarmuka Peta generik yang berisi nilai tipe T dan mendukung get yang mungkin atau mungkin tidak mengembalikan nilai tergantung pada keberadaan kunci:

interface Map<T> {
  get(s:string):Maybe<T>
}

tidak ada yang mencegah T bertipe Maybe<U> ; kode akan bekerja dengan baik dan mengembalikan Just(Nothing) jika ada kunci tetapi berisi nilai Nothing , dan hanya akan mengembalikan Nothing jika kunci tidak ada sama sekali.

Sebaliknya, jika kita menggunakan tipe nullable

interface Map<T> {
  get(s:string):T?
}

maka tidak mungkin untuk membedakan antara kunci yang hilang dan nilai nol ketika T adalah nullable.

Either way, kemampuan untuk membedakan nilai nullable dari non-nullable dan model metode dan properti yang tersedia sesuai merupakan prasyarat untuk semua jenis keamanan.

@jbondc Ini adalah

Saya merasa terhibur bahwa penelitian menunjukkan bahwa 80% dari deklarasi sebenarnya dimaksudkan bukan nol atau bahwa hanya ada 20 anotasi nol per KLOC (hal. 21). Seperti disebutkan dalam dokumen ini adalah argumen kuat untuk non-null secara default, yang juga merupakan perasaan saya.

Argumen lain yang mendukung non-null adalah ia menciptakan sistem tipe yang lebih bersih: null adalah tipenya sendiri, T adalah non-null dan T? adalah sinonim untuk T | null . Karena TS sudah memiliki tipe union semuanya bagus, bersih dan berfungsi dengan baik.

Melihat daftar bahasa terbaru yang mengatasi masalah itu, saya benar-benar berpikir bahwa bahasa pemrograman modern baru harus menangani masalah lama ini dan mengurangi bug nol dalam basis kode. Ini adalah masalah yang terlalu umum untuk sesuatu yang seharusnya dimodelkan dalam sistem tipe. Saya masih berharap TS akan mendapatkannya suatu hari nanti.

Saya menemukan ide operator T! menarik dan mungkin berguna. Saya sedang memikirkan sistem di mana T adalah tipe non-null, T? adalah T | null . Tapi itu mengganggu saya bahwa Anda tidak bisa benar-benar membuat API generik yang menjamin hasil non-null bahkan dalam menghadapi input nol. Saya tidak memiliki kasus penggunaan yang baik, tetapi secara teori saya tidak dapat memodelkan ini dengan setia: function defined(x) { return x || false; } .
Menggunakan operator pembalikan non-null, seseorang dapat melakukan: function defined<T>(x: T): T! | boolean . Artinya jika defined mengembalikan T itu dijamin bukan nol, bahkan jika batasan T generik adalah nullable, katakan string? . Dan saya rasa tidak sulit untuk membuat model dalam TS: mengingat T! , jika T adalah tipe gabungan yang menyertakan tipe null , kembalikan tipe yang dihasilkan dari penghapusan null dari serikat pekerja.

@spion

Membaca dokumen, saya menyadari bahwa tipe Opsional/Mungkin lebih kuat daripada tipe yang dapat dibatalkan

Anda dapat membuat sarang Maybe struktur, Anda tidak dapat membuat sarang null , memang.

Ini adalah diskusi yang menarik dalam konteks mendefinisikan api baru, tetapi ketika memetakan api yang ada, ada sedikit pilihan. Membuat peta bahasa Nulls menjadi Maybe tidak akan memanfaatkan manfaat itu, kecuali jika fungsinya ditulis ulang seluruhnya.

Maybe mengkodekan dua bagian informasi yang berbeda: apakah ada nilai dan apa nilainya. Mengambil contoh Map dan melihat C# ini jelas, dari Dictionary<T,K> :
bool TryGet(K key, out T value) .
Perhatikan bahwa jika C# memiliki tupel (mungkin C# 7), ini pada dasarnya sama dengan:
(bool hasKey, T value) TryGet(K key)
Yang pada dasarnya adalah Maybe dan memungkinkan penyimpanan null .

Perhatikan bahwa JS memiliki caranya sendiri untuk menangani masalah ini dan menciptakan banyak masalah baru yang menarik: undefined . JS Map tipikal akan mengembalikan undefined jika kunci tidak ditemukan, nilainya sebaliknya, termasuk null .

Proposal terkait untuk C# 7 - https://github.com/dotnet/roslyn/issues/5032

Kalian menyadari bahwa masalahnya tidak terpecahkan kecuali jika Anda memodelkan tidak terdefinisi dengan cara yang sama?
Kalau tidak, semua masalah nol Anda hanya akan diganti dengan masalah yang tidak ditentukan (yang lebih umum lagi).

@Griffork

semua masalah nol Anda hanya akan diganti dengan masalah yang tidak ditentukan

Tidak, mengapa mereka?
Masalah nol saya akan hilang dan masalah saya yang tidak terdefinisi akan tetap ada.

Benar, undefined masih menjadi masalah. Tapi itu sangat tergantung pada gaya pengkodean Anda. Saya hampir tidak membuat kode undefined kecuali jika browser memaksa mereka pada saya, yang berarti 90% dari kode saya akan lebih aman dengan pemeriksaan nol.

Saya membuat kode dengan hampir tidak ada yang tidak ditentukan kecuali di mana browser memaksa mereka pada saya

Saya akan berpikir bahwa JavaScript memaksa undefined pada satu di setiap kesempatan.

  • Variabel yang tidak diinisialisasi. let x; alert(x);
  • Argumen fungsi yang dihilangkan. let foo = (a?) => alert(a); foo();
  • Mengakses elemen array yang tidak ada. let x = []; alert(x[0]);
  • Mengakses properti objek yang tidak ada. let x = {}; alert(x['foo']);

Null, di sisi lain, terjadi dalam situasi yang lebih sedikit dan lebih dapat diprediksi:

  • akses DOM. alert(document.getElementById('nonExistent'));
  • Tanggapan layanan web pihak ketiga (sejak JSON.stringify menghapus undefined ) . { name: "Joe", address: null }
  • ekspresi reguler.

Untuk alasan ini, kami melarang penggunaan null , mengonversi semua null diterima melalui kabel menjadi undefined , dan selalu menggunakan pemeriksaan kesetaraan yang ketat untuk undefined . Ini telah bekerja dengan baik bagi kami dalam praktik.

Akibatnya saya setuju bahwa masalah undefined adalah yang paling umum.

@NoelAbrahams Gaya pengkodean, saya beri tahu Anda :)

Variabel yang tidak diinisialisasi

Saya selalu menginisialisasi variabel dan noImplicitAny diaktifkan, jadi let x akan menjadi kesalahan. Semakin dekat yang akan saya gunakan dalam proyek saya adalah let x: any = null , meskipun itu kode yang tidak akan sering saya tulis.

Parameter fungsi opsional

Saya menggunakan nilai parameter default untuk parameter opsional, menurut saya itu lebih masuk akal (kode Anda _will_ membaca dan menggunakan parameter entah bagaimana, bukan?). Jadi bagi saya: function f(name?: string = null, options?: any = {}) .
Mengakses nilai parameter undefined mentah akan menjadi kasus _exceptional_ bagi saya.

Mengakses elemen array yang tidak ada

Ini adalah sesuatu yang saya usahakan untuk tidak dilakukan dalam kode saya. Saya memeriksa panjang array saya agar tidak melampaui batas dan saya tidak menggunakan array yang jarang (atau mencoba mengisi slot kosong dengan nilai default seperti null , 0 , ...).
Sekali lagi, Anda mungkin menemukan kasus khusus di mana saya akan melakukan itu, tetapi itu akan menjadi _pengecualian_, bukan aturannya.

Mengakses properti objek yang tidak ada.

Hampir sama dengan array. Objek saya diketik, jika nilai tidak tersedia saya set ke null , bukan undefined . Sekali lagi Anda mungkin menemukan kasus tepi (seperti melakukan penyelidikan kamus) tetapi mereka adalah _kasus tepi_ dan tidak mewakili gaya pengkodean saya.

Dalam semua kasus _exceptional_ di mana saya mendapatkan undefined kembali, saya segera mengambil tindakan pada hasil undefined dan tidak menyebarkannya lebih lanjut atau "bekerja" dengannya. Contoh dunia nyata yang khas dalam kompiler TS fiksi dengan pemeriksaan nol:

let cats: Cat[];
// Note that find returns undefined if there's no cat named Kitty
let myFavoriteCat = cats.find(c => c.name === 'Kitty'); 
if (myFavoriteCat === undefined) {
  // Immediately do something to compensate here:
  // return false; or 
  // myFavoriteCat = new Cat('Kitty'); or
  // whatever makes sense.
}
// Continue with assurance that myFavoriteCat is not null (it was an array of non-nullable cats after all).

Untuk alasan ini, kami melarang penggunaan null , mengonversi semua null yang diterima melalui kabel menjadi undefined , dan selalu menggunakan pemeriksaan kesetaraan yang ketat untuk undefined . Ini telah bekerja dengan baik bagi kami dalam praktik.

Dari sini saya mengerti bahwa Anda menggunakan gaya pengkodean yang sangat berbeda dari saya. Jika pada dasarnya Anda menggunakan undefined mana-mana maka ya, Anda akan mendapat manfaat dari tipe nol yang diperiksa secara statis jauh lebih sedikit daripada saya
akan.

Ya, masalahnya tidak 100% kedap air karena undefined dan saya tidak percaya seseorang dapat membuat bahasa yang dapat digunakan secara wajar yang 100% benar dalam hal ini. JS memperkenalkan undefined di terlalu banyak tempat.

Tetapi seperti yang saya harap Anda dapat mengetahui dari jawaban saya di atas, dengan gaya pengkodean yang sesuai ada _banyak_ manfaat dari pemeriksaan nol. Setidaknya pendapat saya adalah bahwa dalam basis kode saya itu akan membantu menemukan dan mencegah banyak bug bodoh dan menjadi penambah produktivitas di lingkungan tim saya.

@jods4 , itu menarik untuk membaca pendekatan Anda.

Saya pikir keberatan saya terhadap pendekatan itu adalah tampaknya ada banyak aturan yang harus dipatuhi - ini seperti komunisme vs. pasar bebas :smile:

Tim TS secara internal memiliki aturan yang mirip dengan kami untuk gaya mereka sendiri.

@NoelAbrahams "Gunakan undefined " adalah aturan sebanyak "Gunakan null ".

Bagaimanapun, konsistensi adalah kuncinya dan saya tidak akan menyukai proyek di mana saya tidak yakin apakah semuanya seharusnya null atau undefined (atau string kosong atau nol). Terutama karena TS saat ini tidak membantu dengan masalah ini ...

Saya tahu TS memiliki aturan untuk mendukung undefined daripada null , saya ingin tahu apakah ini pilihan "demi konsistensi" yang sewenang-wenang atau jika ada lebih banyak argumen di balik pilihan tersebut.

Mengapa saya suka menggunakan null daripada undefined :

  1. Ini bekerja dengan cara yang akrab bagi para pengembang kami, banyak yang berasal dari bahasa OO statis seperti C#.
  2. Variabel yang tidak diinisialisasi biasanya dianggap sebagai bau kode dalam banyak bahasa, tidak yakin mengapa itu harus berbeda di JS. Perjelas niat Anda.
  3. Meskipun JS adalah bahasa yang dinamis, kinerjanya lebih baik dengan tipe statis yang tidak berubah. Lebih efisien untuk menetapkan properti ke null daripada delete itu.
  4. Ini mendukung perbedaan bersih antara null yang didefinisikan tetapi menandakan tidak adanya nilai, dan undefined , yang... undefined. Tempat di mana perbedaan antara keduanya jelas: parameter opsional. Tidak meneruskan parameter menghasilkan undefined . Bagaimana Anda _pass_ nilai kosong jika Anda menggunakan undefined untuk itu di basis kode Anda? Menggunakan null tidak ada masalah di sini.
  5. Ini meletakkan jalur bersih ke pemeriksaan nol, seperti yang dibahas di utas ini, yang tidak terlalu praktis dengan undefined . Meskipun mungkin aku melamun tentang yang satu ini.
  6. Anda harus membuat pilihan untuk konsistensi, IMHO null sebaik undefined .

Saya pikir alasan memilih undefined null adalah karena
argumen default dan konsistensi dengan obj.nonexistentProp kembali
undefined .

Selain itu, saya tidak mengerti apa yang dianggap sebagai null
cukup untuk mengharuskan variabel menjadi nullable.

Pada Selasa, 8 Sep 2015, 06:48 jods [email protected] menulis:

@NoelAbrahams https://github.com/NoelAbrahams "Gunakan tidak terdefinisi" adalah sebagai
banyak aturan sebagai "Gunakan null".

Bagaimanapun, konsistensi adalah kuncinya dan saya tidak akan menyukai proyek di mana saya berada
tidak yakin apakah sesuatu seharusnya nol atau tidak terdefinisi (atau kosong
string atau nol). Terutama karena TS saat ini tidak membantu dengan ini
masalah...

Saya tahu TS memiliki aturan untuk mendukung undefined daripada null, saya ingin tahu apakah ini
adalah pilihan "demi konsistensi" yang sewenang-wenang atau jika ada lebih banyak
argumen di balik pilihan.

Mengapa _I_ suka menggunakan null daripada tidak terdefinisi:

  1. Ini bekerja dengan cara yang akrab bagi para pengembang kami, banyak yang berasal dari OO statis
    bahasa seperti C#.
  2. Variabel yang tidak diinisialisasi biasanya dianggap sebagai kode yang berbau
    banyak bahasa, tidak yakin mengapa harus berbeda di JS. Jadikan milikmu
    niat jelas.
  3. Meskipun JS adalah bahasa yang dinamis, kinerja lebih baik dengan
    tipe statis yang tidak berubah. Lebih efisien untuk mengatur properti ke
    null daripada menghapusnya.
  4. Ini mendukung perbedaan bersih antara nol yang didefinisikan tetapi
    menandakan tidak adanya nilai, dan tidak terdefinisi, yang... tidak terdefinisi.
    Tempat di mana perbedaan antara keduanya jelas: opsional
    parameter. Tidak melewati hasil parameter tidak terdefinisi. Apa kabar
    _pass_ nilai kosong jika Anda menggunakan undefined untuk itu dalam kode Anda
    basis? Menggunakan null tidak ada masalah di sini.
  5. Ini meletakkan jalur yang bersih ke pemeriksaan nol, seperti yang dibahas dalam ini
    utas, yang tidak terlalu praktis dengan tidak terdefinisi. Meskipun mungkin
    Aku sedang melamun tentang yang satu ini.
  6. Anda harus membuat pilihan untuk konsistensi, IMHO null sama baiknya dengan
    tidak terdefinisi.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -138514437
.

@impinball
Bisakah kita berhenti menggunakan "bikeshedding" di setiap diskusi github? Kita dapat dengan aman mengatakan bahwa menggunakan undefined atau null adalah preferensi tim; tetapi masalah apakah akan mencoba memasukkan undefined dalam cek nol (atau tidak) dan cara kerjanya bukanlah masalah sepele. Jadi saya tidak mengerti bagaimana bikeshedding itu?

Saya telah membangun garpu TS 1.5 dengan tipe yang tidak dapat dibatalkan dan ternyata sangat mudah. Tetapi saya pikir ada dua masalah sulit yang memerlukan konsensus untuk memiliki tipe yang tidak dapat dibatalkan di kompiler TS resmi, keduanya telah dibahas panjang lebar di atas tanpa kesimpulan yang jelas:

  1. Apa yang kita lakukan dengan undefined ? (pendapat saya: masih ada di mana-mana dan tidak dicentang)
  2. Bagaimana kita menangani kompatibilitas dengan kode yang ada, dalam definisi tertentu? (pendapat saya: tanda keikutsertaan, setidaknya per file definisi. Mengaktifkan tanda itu "melanggar" karena Anda mungkin harus menambahkan anotasi nol.)

Pendapat pribadi saya adalah bahwa null dan undefined harus diperlakukan
setara untuk tujuan nullability. Keduanya digunakan untuk kasus penggunaan itu,
menunjukkan tidak adanya nilai. Salah satunya adalah bahwa nilai tidak pernah ada,
dan yang lainnya adalah bahwa nilai itu pernah ada dan tidak lagi ada. Keduanya
harus dihitung untuk nullability. Sebagian besar fungsi JS mengembalikan tidak terdefinisi, tetapi banyak
Fungsi DOM dan pustaka mengembalikan nol. Keduanya melayani kasus penggunaan yang sama. Jadi,
mereka harus diperlakukan setara.

Referensi bikeshedding adalah tentang argumen gaya kode di mana
harus digunakan untuk mewakili tidak adanya nilai. Beberapa berdebat untuk
hanya nol, ada yang berdebat untuk tidak terdefinisi, dan ada yang berdebat untuk a
campuran. Proposal ini seharusnya tidak membatasi diri hanya pada salah satu dari itu.

Pada Selasa, 8 Sep 2015, 13:28 jods [email protected] menulis:

@impinball https://github.com/impinball
Bisakah kita berhenti menggunakan "bikeshedding" di setiap diskusi github? Kita bisa dengan aman
katakan bahwa menggunakan undefined atau null adalah preferensi tim; tapi masalahnya
apakah akan mencoba memasukkan undefined dalam cek nol (atau tidak) dan bagaimana
itu akan berhasil tidak sepele. Jadi saya tidak mengerti bagaimana itu bikeshedding di
tempat pertama?

Saya telah membangun garpu TS 1.5 dengan tipe yang tidak dapat dibatalkan dan itu
sangat mudah. Tapi saya pikir ada dua masalah sulit yang
perlu konsensus untuk memiliki tipe yang tidak dapat dibatalkan di kompiler TS resmi,
keduanya telah dibahas panjang lebar di atas tanpa kesimpulan yang jelas:

  1. Apa yang kita lakukan dengan tidak terdefinisi? (Pendapat saya: masih ada dimana-mana
    dan tidak dicentang)
  2. Bagaimana kami menangani kompatibilitas dengan kode yang ada, khususnya
    definisi? (Pendapat saya: bendera keikutsertaan, setidaknya per file definisi.
    Mengaktifkan bendera adalah "melanggar" karena Anda mungkin harus menambahkan null
    anotasi.)


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -138641395
.

@ jods4 Saya mengambilnya dari poin 2 Anda bahwa garpu Anda juga menyimpulkan tipe non-nullable secara default alih-alih memerlukan anotasi tipe non-nullable eksplisit?

Bisa tolong linknya? Saya ingin mencobanya.

@impinball
Saya ingin melihat (beberapa) keamanan terhadap undefined juga, tetapi cukup meresap.

Secara khusus, dapatkah kita mendefinisikan array tipe yang tidak dapat dibatalkan?
Mengingat bahwa akses array di luar batas (atau jarang) mengembalikan undefined , dapatkah kita mendefinisikan dan menggunakan array dengan mudah?
Saya pikir mengharuskan semua array menjadi nullable terlalu membebani dalam praktik.

Haruskah kita membedakan tipe null dan undefined ? Itu tidak sulit: T | null , T | undefined , T | null | undefined dan dapat memberikan jawaban yang mudah untuk pertanyaan di atas. Tapi bagaimana dengan sintaks singkatan: apa kepanjangan dari T? ? Baik nol dan tidak terdefinisi? Apakah kita membutuhkan dua singkatan yang berbeda?

@Arnavion
Tipe Null dan Undefined sudah ada di TS.
Saya mengambil adalah untuk:

  1. Jadikan semua tipe tidak dapat dibatalkan (termasuk tipe yang disimpulkan);
  2. Beri nama tipe null ( null ) yang dapat Anda gunakan dalam deklarasi tipe;
  3. Hapus pelebaran dari null menjadi any ;
  4. Perkenalkan sintaks singkatan T? yang sama dengan T | null ;
  5. Hapus konversi implisit dari null ke jenis lainnya.

Tanpa akses ke sumber saya, saya pikir itulah intinya. Tipe Null yang ada dan sistem tipe TS yang luar biasa (terutama tipe serikat pekerja dan pelindung tipe) melakukan sisanya.

Saya belum melakukan pekerjaan saya di github, jadi saya tidak dapat membagikan tautan untuk saat ini. Saya ingin mengkodekan sakelar kompatibilitas terlebih dahulu tetapi saya sangat sibuk dengan hal-hal lain :(
Sakelar kompatibilitas jauh lebih terlibat _<. Tapi ini penting karena saat ini kompilator TS mengkompilasi dirinya sendiri dengan banyak kesalahan dan banyak tes yang ada gagal.
Tetapi tampaknya benar-benar berfungsi dengan baik pada kode baru.

Biarkan saya meringkas apa yang telah saya lihat dari beberapa komentator sejauh ini dengan harapan saya dapat mengilustrasikan bagaimana percakapan ini berputar-putar:
Masalah: Nilai 'kasus khusus' tertentu ditemukan dalam kode yang tidak dirancang untuk menanganinya karena nilai tersebut diperlakukan secara berbeda untuk setiap jenis lain dalam bahasa (yaitu null dan tidak terdefinisi).
Saya: mengapa Anda tidak membuat standar pemrograman saja agar masalah itu tidak terjadi?
Lainnya: karena alangkah baiknya jika niat dapat tercermin dalam pengetikan, karena itu tidak akan didokumentasikan secara berbeda oleh setiap tim yang bekerja dalam skrip tipe, dan asumsi yang salah tentang perpustakaan pihak ketiga akan lebih sedikit.
Everone: Bagaimana kita akan menangani masalah ini?
Lainnya: mari kita buat nol lebih ketat dan gunakan standar untuk menangani yang tidak terdefinisi!

Saya tidak melihat bagaimana ini dapat dianggap sebagai solusi ketika undefined jauh lebih merupakan masalah daripada null!

Penafian: ini tidak mencerminkan sikap semua orang di sini, hanya saja cukup muncul sehingga saya ingin menyoroti ini.
Satu-satunya cara ini akan diterima jika itu adalah solusi untuk suatu masalah, bukan sedikit solusi untuk bagian yang lebih kecil dari suatu masalah.

Telepon dong!
*setiap orang

* sudah cukup muncul sehingga saya ingin menyorotnya.

Juga paragraf terakhir harus membaca "satu-satunya cara proposal ini"

@ jods4 saya akan mengatakan bahwa itu akan tergantung pada konstruksi tertentu. Dalam beberapa kasus, Anda dapat menjamin non-nullability dalam kasus tersebut, seperti berikut ini:

declare const list: T![];

for (const entry of list) {
  // `entry` is clearly immutable here.
}

list.forEach(entry => {
  // `entry` is clearly immutable here.
})

list.map(entry => {
  // `entry` is clearly immutable here.
})

Dalam hal ini, kompiler harus memiliki banyak logika untuk memastikan bahwa array diperiksa dalam batas:

declare const list: T![]

for (let i = 0; i < list.length; i++) {
  // This could potentially fail if the compiler doesn't correctly do the static bounds check.
  const entry: T![] = list[i];
}

Dan dalam hal ini, tidak ada cara Anda dapat menjamin bahwa kompiler dapat memverifikasi akses untuk mendapatkan entry dalam batas tanpa benar-benar mengevaluasi bagian dari kode:

declare const list: T![]

const end = round(max, list.length);

for (let i = 0; i < end; i++) {
  const entry: T![] = list[i];
}

Ada beberapa yang mudah dan jelas, tetapi ada beberapa yang lebih sulit.

@impinball Memang, API modern seperti map , forEach atau for..of tidak masalah karena mereka melewatkan elemen yang tidak pernah diinisialisasi atau dihapus. (Mereka memang menyertakan elemen yang telah disetel ke undefined , tetapi TS hipotetis aman-nol kami akan melarangnya.)

Tetapi akses array klasik adalah skenario penting dan saya ingin melihat solusi yang baik untuk itu. Melakukan analisis kompleks seperti yang Anda sarankan jelas tidak mungkin kecuali dalam kasus-kasus sepele (namun kasus-kasus penting karena sering terjadi). Tetapi perhatikan bahwa meskipun Anda dapat membuktikan bahwa i < array.length itu tidak membuktikan bahwa elemen tersebut diinisialisasi.

Perhatikan contoh berikut, menurut Anda apa yang harus dilakukan TS?

let array: T![] = [];  // an empty array of non-null, non-undefined T
// blah blah
array[4] = new T();  // Fine for array[4], but it means array[0..3] are undefined, is that OK?
// blah blah
let i = 2;
// Note that we could have an array bounds guard
if (i < array.length) {
  let t = array[i];  // Inferred type would be T!, but this is actually undefined :(
}

Ada juga masalah dengan Object.defineProperty.

let array = new Array(5)
Object.defineProperty(array, "length", 2, {
  get() { return 10 },
})

Pada Rabu, 9 Sep 2015, 17:49 jods [email protected] menulis:

@impinball https://github.com/impinball Memang, API modern seperti peta,
forEach atau for..of tidak apa-apa karena mereka melewatkan elemen yang tidak pernah
diinisialisasi atau dihapus. (Mereka memang menyertakan elemen yang telah disetel ke
tidak terdefinisi, tetapi TS hipotetis aman-nol kami akan melarangnya.)

Tetapi akses array klasik adalah skenario penting dan saya ingin
melihat solusi yang baik untuk itu. Melakukan analisis kompleks seperti yang Anda sarankan adalah
jelas tidak mungkin kecuali dalam kasus-kasus sepele (namun kasus-kasus penting karena
mereka umum). Tetapi perhatikan bahwa meskipun Anda dapat membuktikan bahwa saya <
array.length itu tidak membuktikan bahwa elemen diinisialisasi.

Perhatikan contoh berikut, menurut Anda apa yang harus dilakukan TS?

biarkan larik: T![] = []; // array kosong dari T non-null, non-undefined // bla bla
array[4] = baru T(); // Baik untuk array[4], tapi itu berarti array[0..3] tidak terdefinisi, apakah itu OK?// blah blahlet i = 2;// Perhatikan bahwa kita dapat memiliki batasan array guardif (i < array. panjang) {
misalkan t = larik[i]; // Tipe yang disimpulkan adalah T!, tetapi ini sebenarnya tidak terdefinisi :(
}


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139055786
.

Yang perlu Anda lakukan adalah:

  1. Jadikan tipe Null dan Undefined dapat direferensikan.
  2. Cegah nilai null dan undefined agar dapat dialihkan ke apa pun.
  3. Gunakan tipe gabungan dengan Null dan/atau Undefined di mana nullability kami tersirat.

Ini memang perubahan yang berani dan terlihat lebih cocok untuk sebuah bahasa
perpanjangan.
Pada 10 Sep 2015 09:13, "Isiah Meadows" [email protected] menulis:

Ada juga masalah dengan Object.defineProperty.

let array = new Array(5)
Object.defineProperty(array, "length", 2, {
get() { return 10 },
})

Pada Rabu, 9 Sep 2015, 17:49 jods [email protected] menulis:

@impinball https://github.com/impinball Memang, API modern seperti
peta,
forEach atau for..of tidak apa-apa karena mereka melewatkan elemen yang tidak pernah
diinisialisasi atau dihapus. (Mereka memang menyertakan elemen yang telah disetel ke
tidak terdefinisi, tetapi TS hipotetis aman-nol kami akan melarangnya.)

Tetapi akses array klasik adalah skenario penting dan saya ingin
melihat solusi yang baik untuk itu. Melakukan analisis kompleks seperti yang Anda sarankan adalah
jelas tidak mungkin kecuali dalam kasus-kasus sepele (namun kasus-kasus penting karena
mereka umum). Tetapi perhatikan bahwa meskipun Anda dapat membuktikan bahwa saya <
array.length itu tidak membuktikan bahwa elemen diinisialisasi.

Perhatikan contoh berikut, menurut Anda apa yang harus dilakukan TS?

biarkan larik: T![] = []; // array kosong dari T yang tidak nol, tidak terdefinisi//
bla bla
array[4] = baru T(); // Baik untuk array[4], tapi artinya array[0..3] adalah
undefined, apakah itu OK?// bla blahlet i = 2;// Perhatikan bahwa kita dapat memiliki
array batas guardif (i < array.length) {
misalkan t = larik[i]; // Tipe yang disimpulkan adalah T!, tapi ini sebenarnya
tidak terdefinisi :(
}


Balas email ini secara langsung atau lihat di GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-139055786>
.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139230568
.

@impinball
Saya merasa baik-baik saja tentang contoh Anda. Menggunakan defineProperty dengan cara ini berarti melangkah keluar dari kotak pengaman TS dan masuk ke ranah JS dinamis, bukan begitu? Saya rasa saya tidak pernah menelepon defineProperty secara langsung dalam kode TS.

@aleksey-bykov

terlihat lebih cocok untuk ekstensi bahasa.

Masalah lain dengan proposal ini adalah bahwa jika tidak diterima secara luas, nilainya berkurang.
Akhirnya, kami membutuhkan definisi TS yang diperbarui dan saya pikir itu tidak akan terjadi jika itu adalah ekstensi atau fork TS yang jarang digunakan, tidak kompatibel.

Saya tahu bahwa array yang diketik memang memiliki jaminan, karena IIRC mereka melempar
ReferenceError saat memuat dan menyimpan array di luar batas. Array reguler dan
objek argumen kembali tidak terdefinisi ketika indeks berada di luar batas. di my
pendapat, itu adalah kelemahan bahasa JS, tetapi memperbaikinya pasti akan
merusak Web.

Satu-satunya cara untuk "memperbaiki" ini adalah di ES6, melalui konstruktor yang mengembalikan proxy,
dan prototipe instans dan prototipe diri disetel ke Array asli
konstruktor. Sesuatu seperti ini:

Array = (function (A) {
  "use strict";
  function check(target, prop) {
    const i = +prop;
    if (prop != i) return target[prop];
    if (i >= target.length) {
      throw new ReferenceError();
    }
    return i;
  }

  function Array(...args) {
    return new Proxy(new Array(...args), {
      get(target, prop) {
        return target[check(target, prop)];
      },
      set(target, prop, value) {
        return target[check(target, prop)] = value;
      },
    });
  }

  Array.prototype = A.prototype;
  Array.prototype.constructor = Array
  Object.setPrototypeOf(Array, A);
  return Array;
})(Array);

(catatan: belum diuji, diketik di ponsel...)

Pada Kam, 10 Sep 2015, 10:09 jods [email protected] menulis:

@impinball https://github.com/impinball
Saya merasa baik-baik saja tentang contoh Anda. Menggunakan defineProperty dengan cara ini adalah
melangkah keluar dari kotak pengaman TS dan masuk ke ranah JS dinamis, jangan
kamu pikir? Saya rasa saya tidak pernah memanggil defineProperty secara langsung dalam kode TS.

@aleksey-bykov https://github.com/aleksey-bykov

terlihat lebih cocok untuk ekstensi bahasa.

Masalah lain dengan proposal ini adalah bahwa jika tidak diterima secara luas
memiliki nilai yang berkurang.
Akhirnya, kami membutuhkan definisi TS yang diperbarui dan saya rasa itu tidak akan terjadi
terjadi jika itu adalah ekstensi TS yang jarang digunakan, tidak kompatibel, atau garpu.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139245706
.

@impinball Tidak yakin ada yang harus "diperbaiki" sejak awal...
Itu adalah semantik JS dan TS harus mengakomodasi mereka entah bagaimana.

Bagaimana jika kita hanya memiliki mark up (sedikit berbeda) untuk mendeklarasikan array sparse vs array non-sparse, dan membuat yang non-sparse secara otomatis menginisialisasi (mungkin programmer dapat memberikan nilai atau persamaan untuk inisialisasi). Dengan cara itu kita dapat memaksa array sparse menjadi tipe T|undefined (yang akan berubah menjadi tipe T menggunakan for... of dan operasi 'aman' lainnya) dan membiarkan tipe array non-sparse saja.

//not-sparse
var array = [arrlength] => index*3;
var array = <number[]>[3];
//sparse
var array = [];

Jelas ini bukan sintaks akhir.
Contoh kedua akan menginisialisasi setiap nilai ke compiler-default untuk tipe itu.
Ini berarti bahwa untuk array non-jarang Anda harus mengetiknya, jika tidak, saya curiga Anda harus melemparkannya ke sesuatu setelah sepenuhnya diinisialisasi.
Juga kita harus membutuhkan tipe non-jarang untuk array sehingga pemrogram dapat mentransmisikan array mereka ke non-jarang.

@Griffork
Entahlah... jadi membingungkan.

Dengan begitu kita dapat memaksa array jarang menjadi tipe T|undefined (yang akan berubah menjadi tipe T menggunakan for... dari dan operasi 'aman' lainnya)

Karena kekhasan di JS itu tidak berfungsi seperti itu. Asumsikan let arr: (T|undefined)[] .
Jadi saya bebas melakukan: arr[0] = undefined .
Jika saya melakukannya maka menggunakan fungsi "aman" itu _will_ mengembalikan undefined untuk slot pertama. Jadi di arr.forEach(x => ...) Anda tidak bisa mengatakan itu x: T . Itu masih harus x: T|undefined .

Contoh kedua akan menginisialisasi setiap nilai ke compiler-default untuk tipe itu.

Ini sangat tidak seperti TS dalam semangat. Mungkin saya salah, tetapi menurut saya filosofi TS adalah bahwa tipe hanyalah lapisan tambahan di atas JS dan mereka tidak memengaruhi codegen. Ini memiliki implikasi perf demi kebenaran tipe parsial yang tidak saya sukai.

TS jelas tidak dapat melindungi Anda dari semua yang ada di JS dan ada beberapa fungsi / konstruksi yang dapat Anda panggil dari TS yang valid, yang memiliki efek dinamis pada jenis runtime objek Anda dan merusak analisis jenis TS statis.

Apakah akan buruk jika ini adalah lubang di sistem tipe? Maksud saya, kode ini benar-benar tidak umum: let x: number[] = []; x[3] = 0; dan jika itu hal yang ingin Anda lakukan maka mungkin Anda harus mendeklarasikan array Anda let x: number?[] .

Ini tidak sempurna tetapi saya pikir itu cukup baik untuk sebagian besar penggunaan di dunia nyata. Jika Anda seorang puritan yang menginginkan sistem tipe suara, maka Anda tentu harus mencari bahasa lain, karena sistem tipe TS tidak suara pula. Bagaimana menurut anda?

Itu sebabnya saya mengatakan Anda juga membutuhkan kemampuan untuk melakukan casting ke array yang tidak jarang
ketik, sehingga Anda dapat menginisialisasi array sendiri tanpa kinerja
dampak.
Saya akan puas hanya dengan perbedaan antara (apa yang dimaksudkan untuk menjadi) jarang
array dan array non-jarang menurut jenisnya.

Bagi mereka yang tidak tahu mengapa ini penting, itu adalah alasan yang sama dengan Anda
ingin perbedaan antara T dan T|null .

Pada 09:11, Jum, 11 Sep 2015 jods [email protected] menulis:

@Griffork https://github.com/Griffork
Entahlah... jadi membingungkan.

Dengan cara itu kita bisa memaksa array jarang menjadi tipe T|undefined (yang akan
ubah ke tipe T menggunakan for... of dan operasi 'aman' lainnya)

Karena kekhasan di JS itu tidak berfungsi seperti itu. Asumsikan biarkan arr:
(T|tidak ditentukan)[].
Jadi saya bebas melakukan: arr[0] = undefined.
Jika saya melakukannya maka gunakan fungsi "aman" itu _will_ return undefined
untuk slot pertama. Jadi di arr.forEach(x => ...) Anda tidak dapat mengatakan bahwa x: T.
Itu masih harus x: T|tidak terdefinisi.

Contoh kedua akan menginisialisasi setiap nilai ke compiler-default
untuk tipe itu.

Ini sangat tidak seperti TS dalam semangat. Mungkin saya salah tapi menurut saya
bahwa filosofi TS adalah bahwa tipe hanyalah lapisan tambahan di atas JS
dan mereka tidak memengaruhi codegen. Ini memiliki implikasi kinerja demi
sebagian tipe-kebenaran yang saya tidak begitu suka.

TS jelas tidak dapat melindungi Anda dari semua yang ada di JS dan ada
beberapa fungsi / konstruksi yang dapat Anda panggil dari TS yang valid, yang memiliki
efek dinamis pada jenis runtime objek Anda dan hancurkan TS statis
analisis tipe.

Apakah akan buruk jika ini adalah lubang di sistem tipe? Maksudku, kode ini
benar-benar tidak umum: misalkan x: angka[] = []; x[3] = 0; dan jika itu
hal-hal yang ingin Anda lakukan maka mungkin Anda harus mendeklarasikan array Anda let
x: nomor?[].

Ini tidak sempurna tetapi saya pikir itu cukup baik untuk sebagian besar penggunaan di dunia nyata.
Jika Anda seorang puritan yang menginginkan sistem tipe suara, maka Anda pasti harus
lihat bahasa lain, karena sistem tipe TS tidak terdengar. Apa
Menurut mu?


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139408240
.

@ jods4 Yang saya maksud dengan "memperbaiki" adalah "memperbaiki" apa yang IMHO cacat desain bahasa JS. Bukan TypeScript, tetapi di _JavaScript sendiri_.

@jods Saya mengeluh tentang JS, bukan TS. Saya akui itu di luar topik.

Pada Kamis, 10 Sep 2015, 19:19 Grifork [email protected] menulis:

Itu sebabnya saya mengatakan Anda juga membutuhkan kemampuan untuk melakukan casting ke array yang tidak jarang
ketik, sehingga Anda dapat menginisialisasi array sendiri tanpa kinerja
dampak.
Saya akan puas hanya dengan perbedaan antara (apa yang dimaksudkan untuk menjadi) jarang
array dan array non-jarang menurut jenisnya.

Bagi mereka yang tidak tahu mengapa ini penting, itu adalah alasan yang sama dengan Anda
ingin perbedaan antara T dan T|null .

Pada 09:11, Jum, 11 Sep 2015 jods [email protected] menulis:

@Griffork https://github.com/Griffork
Entahlah... jadi membingungkan.

Dengan cara itu kita bisa memaksa array jarang menjadi tipe T|undefined (yang akan
ubah ke tipe T menggunakan for... of dan operasi 'aman' lainnya)

Karena kekhasan di JS itu tidak berfungsi seperti itu. Asumsikan biarkan arr:
(T|tidak ditentukan)[].
Jadi saya bebas melakukan: arr[0] = undefined.
Jika saya melakukannya maka gunakan fungsi "aman" itu _will_ return undefined
untuk slot pertama. Jadi di arr.forEach(x => ...) Anda tidak dapat mengatakan bahwa x: T.
Itu masih harus x: T|tidak terdefinisi.

Contoh kedua akan menginisialisasi setiap nilai ke compiler-default
untuk tipe itu.

Ini sangat tidak seperti TS dalam semangat. Mungkin saya salah tapi menurut saya
bahwa filosofi TS adalah bahwa tipe hanyalah lapisan tambahan di atas
JS
dan mereka tidak memengaruhi codegen. Ini memiliki implikasi kinerja demi
sebagian tipe-kebenaran yang saya tidak begitu suka.

TS jelas tidak dapat melindungi Anda dari semua yang ada di JS dan ada
beberapa fungsi / konstruksi yang dapat Anda panggil dari TS yang valid, yang
memiliki
efek dinamis pada jenis runtime objek Anda dan hancurkan TS statis
analisis tipe.

Apakah akan buruk jika ini adalah lubang di sistem tipe? Maksudku, kode ini
benar-benar tidak umum: misalkan x: angka[] = []; x[3] = 0; dan jika itu
jenis hal yang ingin Anda lakukan maka mungkin Anda harus mendeklarasikan array Anda
membiarkan
x: nomor?[].

Ini tidak sempurna tetapi saya pikir itu cukup baik untuk sebagian besar penggunaan di dunia nyata.
Jika Anda seorang puritan yang menginginkan sistem tipe suara, maka Anda harus
tentu
lihat bahasa lain, karena sistem tipe TS tidak terdengar.
Apa
Menurut mu?


Balas email ini secara langsung atau lihat di GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-139408240>
.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139409349
.

Dan untuk pernyataan saya dengan panjang Array, kami dapat beroperasi dengan asumsi bahwa semua akses array berada dalam batas, dan bahwa akses di luar batas tidak ditentukan kecuali secara eksplisit ditentukan dalam antarmuka. Itu sangat mirip dengan apa yang dilakukan C/C++, dan itu akan memungkinkan pengetikan yang lebih baik, dan berpotensi memuat seluruh pengoptimalan kompiler, jika seseorang memutuskan untuk akhirnya menulis kompiler pihak ketiga yang menggunakan spesifikasi bahasa, tetapi tidak terlalu peduli tentang mencocokkan emisi.

Saya tahu mendukung pencocokan perilaku tidak terdefinisi C/C++ terdengar sangat bodoh di permukaan, tetapi saya pikir dalam kasus ini, itu bisa jadi sepadan. Jarang melihat sesuatu yang benar-benar dilakukan _lebih baik_ dengan membuat akses di luar batas. 99,99% penggunaan yang pernah saya lihat hanyalah bau kode yang sangat menyengat, hampir selalu dilakukan oleh orang yang hampir tidak terbiasa dengan JavaScript.

(Sebagian besar dari orang-orang ini, menurut pengalaman saya, bahkan belum pernah mendengar tentang CoffeeScript, apalagi TypeScript. Banyak dari mereka bahkan tidak mengetahui versi baru JS yang baru saja diselesaikan dan distandarisasi, ES2015.)

Apakah ada resolusi yang muncul untuk ini?

Karena memiliki tipe yang tidak dapat dibatalkan, tampaknya TypeScript masih berguna untuk gagal jika seseorang mencoba mengakses properti pada variabel yang _assuredly_ null.

var o = null;
console.log(o.x);

... harus gagal.

Memastikan melalui sistem tipe bahwa semua akses array diperiksa batasnya sepertinya melayang ke ranah tipe dependen. Sementara tipe dependen cukup rapi, itu sepertinya fitur yang jauh lebih besar daripada tipe non-nullable.

Sepertinya ada tiga opsi dengan asumsi bahwa pemeriksaan batas tidak diterapkan pada array pada waktu kompilasi:

  1. Pengindeksan array (dan akses elemen array arbitrer apa pun berdasarkan indeks) dianggap mengembalikan tipe nullable, bahkan pada array tipe non-nullable. Pada dasarnya, "metode" [] memiliki tanda tangan tipe T? . Jika Anda tahu bahwa Anda hanya melakukan pengindeksan yang diperiksa batas, Anda dapat memasukkan T? ke T! dalam kode aplikasi Anda.
  2. Pengindeksan array mengembalikan tipe yang persis sama (dengan nullability yang sama) sebagai parameter tipe generik array, dan diasumsikan bahwa semua akses array diperiksa batasnya oleh aplikasi. Akses di luar batas akan mengembalikan undefined , dan tidak akan ditangkap oleh pemeriksa tipe.
  3. Opsi nuklir: semua array dikodekan ke dalam bahasa sebagai nullable, dan upaya untuk menggunakan array non-nullable gagal dalam pemeriksaan ketik.

Ini semua berlaku untuk akses properti berbasis indeks pada objek juga, misalnya object['property'] mana object bertipe { [ key: string ]: T! } .

Secara pribadi saya lebih suka opsi pertama, di mana pengindeksan ke dalam array atau objek mengembalikan tipe nullable. Tetapi bahkan opsi kedua tampaknya lebih baik daripada semuanya dapat dibatalkan, yang merupakan keadaan saat ini. Opsi nuklirnya menjijikkan tetapi sejujurnya juga masih lebih baik daripada semuanya dapat dibatalkan.

Ada pertanyaan kedua apakah tipe harus secara default non-nullable, atau secara default nullable. Sepertinya dalam kedua kasus akan berguna untuk memiliki sintaks baik untuk tipe yang dapat dibatalkan secara eksplisit dan tidak dapat dibatalkan secara eksplisit untuk menangani obat generik; misalnya bayangkan metode get pada kelas wadah (misalnya Peta) yang mengambil beberapa nilai dan mungkin mengembalikan tipe, _bahkan jika wadah hanya berisi nilai yang tidak dapat dibatalkan_:

class Container<K,V> {
  get(key: K): V? {
    // fetch from some internal data structure and return the value, if it exists
    // return null otherwise
  }
}

// only non-nullable values allowed in the container
const container = new Container<SomeKeyClass!, SomeValueClass!>();
const val: SomeValueClass!;
// ... later, we attempt to read from the container with a get() call
// even though only non-nullables are allowed in the container, the following should fail:
// get() explicitly returns null when the item can't be found
val = container.get(someKey);

Demikian pula, kami mungkin (ini bukan argumen yang kuat) ingin memastikan kelas penampung kami hanya menerima kunci non-null pada sisipan, bahkan saat menggunakan jenis kunci nullable:

class Container<K, V> {
  insert(key: K!, val: V): void {
    // put the val in the data structure
    // the key must not be null here, even if K is elsewhere a nullable type
  }
}

const container = new Container<SomeKeyClass?, SomeValueClass>();
container.insert(null, new SomeValueClass()); // fails

Jadi terlepas dari apakah default berubah, sepertinya akan berguna untuk memiliki sintaks eksplisit untuk tipe nullable dan tipe non-nullable. Kecuali aku kehilangan sesuatu?

Pada titik di mana ada sintaks untuk keduanya, defaultnya sepertinya bisa berupa flag kompiler yang mirip dengan --noImplicitAny . Secara pribadi saya akan memilih default tetap sama sampai rilis 2.0, tetapi tampaknya baik-baik saja selama ada pintu keluar (setidaknya untuk sementara).

Saya lebih suka opsi kedua, karena meskipun itu akan membuat akses di luar batas perilaku tidak terdefinisi (dalam bidang pengetikan TS), saya pikir itu kompromi yang bagus untuk ini. Ini dapat sangat meningkatkan kecepatan, dan lebih mudah untuk ditangani. Jika Anda benar-benar mengharapkan akses di luar batas dimungkinkan, Anda harus menggunakan tipe nullable secara eksplisit, atau melemparkan hasilnya ke tipe nullable (yang selalu memungkinkan). Dan umumnya, jika array tidak dapat dibatalkan, akses di luar batas hampir selalu merupakan bug, yang akan meledak dengan hebat di beberapa titik (Ini adalah cacat JS).

Cukup banyak membutuhkan programmer untuk menjadi eksplisit dalam apa yang mereka harapkan. Ini kurang aman untuk tipe, tetapi dalam kasus ini, saya pikir keamanan tipe mungkin akan menghalangi.

Berikut perbandingan dengan setiap opsi, menggunakan fungsi penjumlahan sebagai contoh (primitif adalah yang paling bermasalah):

// Option 1
function sum(numbers: !number[]) {
  let res = 0
  for (let i = 0; i < numbers.length; i++) {
    res += <!number> numbers[i]
  }
  return res
}

// Option 2
function sum(numbers: !number[]) {
  let res = 0
  for (let i = 0; i < numbers.length; i++) {
    res += numbers[i]
  }
  return res
}

// Option 3
function sum(numbers: number[]) {
  let res = 0
  for (let i = 0; i < numbers.length; i++) {
    res += <!number> numbers[i]
  }
  return res
}

Contoh lain: fungsi map .

// Option 1
function map<T>(list: !T[], f: (value: !T, index: !number) => !T): !T[] {
  let res: !T[] = []
  for (let i = 0; i < list.length; i++) {
    res.push(f(<!T> list[i], i));
  }
  return res
}

// Option 2
function map<T>(list: !T[], f: (value: !T, index: !number) => !T): !T[] {
  let res: !T[] = []
  for (let i = 0; i < list.length; i++) {
    res.push(f(list[i], i));
  }
  return res
}

// Option 3
function map<T>(list: T[], f: (value: !T, index: !number) => !T): T[] {
  let res: T[] = []
  for (let i = 0; i < list.length; i++) {
    const entry = list[i]
    if (entry !== undefined) {
      res.push(f(<!T> entry, i));
    }
  }
  return res
}

Pertanyaan lain: apa jenis entry di masing-masing ini? !string , ?string , atau string ?

declare const regularStrings: string[];
declare const nullableStrings: ?string[];
declare const nonnullableStrings: !string[];

for (const entry of regularStrings) { /* ... */  }
for (const entry of nullableStrings) { /* ... */  }
for (const entry of nonnullableStrings) { /* ... */  }

Opsi tiga adalah sedikit saran lidah-di-pipi :stuck_out_tongue:

Re: pertanyaan terakhir Anda:

declare const regularStrings: string[];
declare const nullableStrings: string?[];
declare const nonNullableStrings: string![]; // fails typecheck in option three

for(const entry of regularStrings) {
  // option 1: entry is of type string?
  // option 2: depends on default nullability
}

for(const entry of nullableStrings) {
  // option 1 and 2: entry is of type string?
}

for(const entry of nonNullableStrings) {
  // option 1: entry is of type string?
  // option 2: entry is of type string!
}

Dalam beberapa kasus — di mana Anda ingin mengembalikan tipe yang tidak dapat dibatalkan dan Anda mendapatkannya dari array, misalnya — Anda harus melakukan pemeran tambahan dengan opsi satu dengan asumsi Anda telah menjamin di tempat lain tidak ada nilai yang tidak ditentukan dalam array (persyaratan jaminan ini tidak berubah terlepas dari pendekatan mana yang diambil, hanya perlu mengetik as string! ). Secara pribadi saya masih lebih suka karena keduanya lebih eksplisit (Anda harus menentukan kapan Anda mengambil perilaku yang mungkin berbahaya, sebagai lawan dari itu terjadi secara implisit) dan lebih konsisten dengan cara kerja sebagian besar kelas kontainer: misalnya, get Fungsi Map.prototype.get mengembalikan nullable maka object['property'] mungkin harus dilakukan sama, karena mereka membuat jaminan serupa tentang nullability dan digunakan dengan cara yang sama. Yang meninggalkan array sebagai yang aneh di mana kesalahan referensi nol dapat merayap kembali, dan di mana akses acak diizinkan untuk tidak dapat dibatalkan oleh sistem tipe.

Pasti ada pendekatan lain; misalnya, saat ini Flow menggunakan opsi dua , dan terakhir saya memeriksa SoundScript membuat array jarang secara eksplisit ilegal dalam spesifikasinya (well, mode kuat/"SaneScript" membuatnya ilegal, dan SoundScript adalah superset dari aturan baru), yang sampai batas tertentu menghindari masalah meskipun mereka masih perlu mencari cara untuk menangani perubahan panjang manual dan dengan alokasi awal. Saya menduga mereka akan mendekati opsi satu dalam kenyamanan vs. tradeoff keamanan — yaitu, akan kurang nyaman untuk menulis tetapi lebih aman — karena penekanan mereka pada jenis sistem kesehatan, tetapi mungkin akan terlihat agak berbeda daripada salah satu dari pendekatan ini karena larangan array jarang.

Saya pikir aspek kinerja sangat teoretis pada saat ini, karena AFAIK TypeScript akan terus memancarkan JS yang sama terlepas dari gips untuk pilihan apa pun di sini dan VM yang mendasarinya akan terus memeriksa array batas di bawah tenda. Jadi saya tidak terlalu terpengaruh dengan argumen itu. Pertanyaan di benak saya sebagian besar seputar kenyamanan vs. keamanan; bagi saya, kemenangan keamanan di sini tampaknya sepadan dengan pengorbanan kenyamanan. Tentu saja, keduanya merupakan peningkatan dari membuat semua tipe menjadi nullable.

Saya setuju bahwa bagian kinerja sebagian besar teoretis, tetapi saya tetap akan
seperti kenyamanan asumsi. Kebanyakan array padat, dan nullability oleh
default tidak masuk akal untuk array boolean dan numerik. Jika tidak
dimaksudkan sebagai array padat, itu harus ditandai sebagai nullable secara eksplisit jadi
niatnya jelas.


TypeScript benar-benar membutuhkan cara untuk menegaskan sesuatu, karena pernyataan sering
digunakan untuk membantu dalam pengecekan tipe statis dalam bahasa lain. Saya pernah melihat di
Basis kode V8 makro UNREACHABLE(); yang memungkinkan asumsi menjadi a
sedikit lebih aman, menerjang program jika invarian dilanggar. C++
memiliki static_assert untuk pernyataan statis guna membantu pengecekan tipe.

Pada Selasa, 20 Okt 2015 pukul 04.01, Matt Baker [email protected]
menulis:

Opsi tiga adalah sedikit saran yang tidak masuk akal [gambar:
:stuck_out_tongue:]

Re: pertanyaan terakhir Anda:

deklarasikan const regularStrings: string[];deklarasikan const nullableStrings: string?[];deklarasikan const nonNullableStrings: string![]; // gagal typecheck di opsi tiga
for(entri const dari regularStrings) {
// opsi 1: entri bertipe string?
// opsi 2: tergantung pada nullability default
}
for(entri const dari nullableStrings) {
// opsi 1 dan 2: entri bertipe string?
}
for(entri const dari nonNullableStrings) {
// opsi 1: entri bertipe string?
// opsi 2: entri bertipe string!
}

Dalam beberapa kasus — di mana Anda ingin mengembalikan tipe yang tidak dapat dibatalkan dan Anda
mendapatkannya dari array, misalnya — Anda harus melakukan pemeran tambahan
dengan opsi satu dengan asumsi Anda telah menjamin di tempat lain tidak ada yang tidak ditentukan
nilai dalam array (persyaratan jaminan ini tidak berubah
terlepas dari pendekatan mana yang diambil, hanya perlu mengetik sebagai string!).
Secara pribadi saya masih lebih suka karena keduanya lebih eksplisit (Anda harus
tentukan kapan Anda mengambil perilaku yang mungkin berbahaya, sebagai lawan dari itu
terjadi secara implisit) dan lebih konsisten dengan bagaimana sebagian besar kelas kontainer
berfungsi: misalnya, fungsi get Map dengan jelas mengembalikan tipe yang dapat dibatalkan
(itu mengembalikan objek jika ada di bawah kunci, atau null jika tidak),
dan jika Map.prototype.get mengembalikan nullable maka object['property']
mungkin harus melakukan hal yang sama, karena mereka membuat jaminan serupa tentang
nullability dan digunakan dengan cara yang sama. Yang meninggalkan array sebagai yang aneh
di mana kesalahan referensi nol dapat merayap kembali, dan di mana akses acak berada
diizinkan untuk tidak dapat dibatalkan oleh sistem tipe.

Pasti ada pendekatan lain; misalnya, saat ini Flow menggunakan
opsi dua http://flowtype.org/docs/nullable-types.html , dan terakhir saya
memeriksa SoundScript membuat array jarang secara eksplisit ilegal dalam spesifikasinya
https://github.com/rwaldron/tc39-notes/blob/master/es6/2015-01/JSExperimentalDirections.pdf
(yah, mode kuat/"SaneScript" membuatnya ilegal, dan SoundScript adalah a
superset dari aturan baru), yang sampai batas tertentu menghindari masalah
meskipun mereka masih perlu mencari cara untuk menangani panjang manual
perubahan dan dengan alokasi awal.

Saya pikir aspek kinerja sangat teoretis pada saat ini,
karena AFAIK TypeScript akan terus memancarkan JS yang sama terlepas dari
gips untuk pilihan apa pun di sini dan VM yang mendasarinya akan terus berlanjut
array pemeriksaan batas di bawah tenda terlepas. Jadi saya tidak terlalu terpengaruh oleh
argumen itu. Pertanyaan di benak saya sebagian besar seputar kenyamanan vs.
keamanan; bagi saya, kemenangan keamanan di sini tampaknya sepadan dengan pengorbanan kenyamanan. Dari
tentu saja, keduanya merupakan peningkatan dari membuat semua jenis menjadi nullable.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -149468527
.

Isiah Padang Rumput

Haruskah kita mulai memanggil mereka tipe non-void ? Bagaimanapun, saya pikir secara eksplisit mendefinisikan non-void dengan T! atau !T adalah sebuah kesalahan. Sulit untuk dibaca sebagai manusia, dan juga sulit untuk menangani semua kasus untuk kompiler TypeScript.

Masalah utama yang saya lihat dengan tipe non-void secara default adalah bahwa ini adalah perubahan yang melanggar. Nah bagaimana jika kita menambahkan beberapa analisis yang lebih statis, mirip dengan Flow, yang tidak mengubah perilaku sama sekali, tetapi akan menangkap lebih banyak bug? Kemudian kita dapat menangkap sebagian besar bug dari kelas ini sekarang, tetapi jangan mengubah sintaksnya, dan di masa mendatang, akan jauh lebih mudah untuk memperkenalkan flag kompiler atau perilaku default yang tidak terlalu berubah.

``` .ts
// fungsi mengkompilasi dengan senang hati
fungsi len(x: string): angka {
kembali x.panjang;
}

len("bekerja"); // 5
len(nol); // kesalahan, tidak ada panjang properti null

``` .ts
function len(x: string): number {
    if (x === null) {
        return -1;
    }
    return x.length;
}

len("works"); // 5
len(null); // null

Apa yang sebenarnya terjadi di sini adalah memodelkan data input sebagai non-void , tetapi menambahkan void secara implisit ketika ditangani dalam fungsi. Demikian pula, tipe yang dikembalikan adalah non-void kecuali jika dapat secara eksplisit mengembalikan null atau undefined

Kita juga dapat menambahkan tipe ?T atau T? , yang memaksa pemeriksaan null (dan/atau tidak terdefinisi) sebelum digunakan. Secara pribadi saya suka T? , tetapi ada preseden untuk menggunakan ?T dengan Flow.

``` .ts
fungsi len(x: ?string): angka {
kembali x.panjang; // error: tidak ada properti panjang pada tipe ?string, Anda harus menggunakan tipe guard
}

One more example -- what about using function results?

``` .js
function len(x: string): number {
    return x.length;
}

function identity(f: string): string {
    return f;
}

function unknown(): string {
    if (Math.random() > 0.5) {
        return null;
    }
    return "maybe";
}

len("works"); // 5
len(null); // error, no property length of null

identity("works"); // "works": string
identity(null); // null: void
unknown(); // ?string

len(identity("works")); // 5
len(identity(null)); // error, no property length of null
len(unknown()); // error: no length property on type ?string, you must use a type guard

Di bawah tenda, apa yang sebenarnya terjadi di sini adalah bahwa TypeScript menyimpulkan apakah suatu tipe dapat menjadi nol atau tidak dengan melihat apakah itu menangani nol, dan kemungkinan diberi nilai nol.

Satu-satunya bagian yang sulit di sini adalah bagaimana berinteraksi dengan file definisi. Saya pikir ini dapat diselesaikan dengan memiliki default untuk mengasumsikan bahwa file definisi yang mendeklarasikan function(t: T) tidak memeriksa null/ void , seperti contoh kedua. Ini berarti fungsi-fungsi tersebut akan dapat mengambil nilai nol tanpa kompiler menghasilkan kesalahan.

Ini sekarang memungkinkan dua hal:

  1. Adopsi bertahap sintaks tipe ?T , yang parameter opsionalnya sudah akan ditukar.
  2. Di masa depan, flag compiler --noImplicitVoid , dapat ditambahkan yang akan memperlakukan file deklarasi sama dengan file kode yang dikompilasi. Ini akan menjadi "melanggar", tetapi jika dilakukan jauh di jalan, sebagian besar perpustakaan akan mengadopsi praktik terbaik menggunakan ?T ketika jenisnya bisa void dan T ketika tidak bisa. Itu juga akan menjadi pilihan, jadi hanya mereka yang memilih untuk menggunakannya yang akan terpengaruh. Ini juga dapat mengharuskan sintaks ?T digunakan dalam kasus di mana suatu objek dapat dibatalkan.

Saya pikir ini adalah pendekatan yang realistis, karena memberikan keamanan yang jauh lebih baik ketika sumber tersedia di TypeScript, menemukan masalah rumit itu, sambil tetap memungkinkan integrasi yang mudah, cukup intuitif, dan kompatibel dengan file definisi.

Varian awalan ? juga digunakan dalam anotasi Closure Compiler, IIRC.

Pada Selasa, 17 November 2015, 13:37 Tom Jacques [email protected] menulis:

Haruskah kita mulai memanggil mereka tipe non-void? Bagaimanapun, saya pikir
secara eksplisit mendefinisikan non-void dengan T! atau !T adalah kesalahan. Ini sulit
untuk dibaca sebagai manusia, dan juga sulit untuk menangani semua kasus untuk
kompiler TypeScript.

Masalah utama yang saya lihat dengan tipe non-void secara default adalah bahwa itu adalah
melanggar perubahan. Nah bagaimana jika kita menambahkan beberapa analisis yang lebih statis,
mirip dengan Flow, yang tidak mengubah perilaku sama sekali, tetapi akan menangkap
lebih banyak bug? Maka kita bisa menangkap sebagian besar bug dari kelas ini sekarang, tapi
jangan ubah sintaksnya, dan di masa mendatang, akan lebih mudah untuk
perkenalkan flag kompiler atau perilaku default yang tidak terlalu mengganggu
mengubah.

// fungsi mengkompilasi dengan senang hatifungsi len(x: string): number {
kembali x.panjang;
}

len("bekerja"); // 5
len(nol); // kesalahan, tidak ada panjang properti null

fungsi len(x: string): angka {
jika (x === nol) {
kembali -1;
}
kembali x.panjang;
}

len("bekerja"); // 5
len(nol); // nol

Apa yang sebenarnya terjadi di sini adalah memodelkan data input sebagai non-void,
tetapi menambahkan void secara implisit ketika ditangani dalam fungsi. Demikian pula,
tipe pengembalian tidak batal kecuali jika dapat secara eksplisit mengembalikan nol atau
tidak terdefinisi

Kita juga bisa menambahkan ?T atau T? type, yang memaksa null (dan/atau
tidak terdefinisi) periksa sebelum digunakan. Secara pribadi saya suka T?, tetapi ada preseden
menggunakan ?T dengan Flow.

fungsi len(x: ?string): angka {
kembali x.panjang; // error: tidak ada properti panjang pada tipe ?string, Anda harus menggunakan tipe guard
}

Satu contoh lagi -- bagaimana dengan menggunakan hasil fungsi?

fungsi len(x: string): angka {
kembali x.panjang;
}
identitas fungsi (f: string): string {
kembali f;
}
fungsi tidak diketahui(): string {
if (Matematika.acak() > 0,5) {
kembali nol;
}
kembali "mungkin";
}

len("bekerja"); // 5
len(nol); // kesalahan, tidak ada panjang properti null

identitas("karya"); // "berfungsi": string
identitas (nol); // nol: batal
tidak dikenal(); // ?rangkaian

len(identitas("berfungsi")); // 5
len(identitas(null)); // kesalahan, tidak ada panjang properti null
len(tidak diketahui()); // error: tidak ada properti panjang pada tipe ?string, Anda harus menggunakan tipe guard

Di bawah tenda, apa yang sebenarnya terjadi di sini adalah TypeScript itu
menyimpulkan apakah suatu tipe dapat menjadi nol dengan melihat apakah itu menangani
null, dan diberi kemungkinan nilai null.

Satu-satunya bagian yang sulit di sini adalah bagaimana berinteraksi dengan file definisi. Saya
pikir ini dapat diselesaikan dengan memiliki default untuk mengasumsikan bahwa a
file definisi yang mendeklarasikan suatu fungsi (t: T) melakukan pengecekan null/void, banyak
seperti contoh kedua. Ini berarti fungsi-fungsi itu akan dapat mengambil
nilai nol tanpa kompiler menghasilkan kesalahan.

Ini sekarang memungkinkan dua hal:

  1. Adopsi secara bertahap dari sintaks tipe ?T, yang opsional
    parameter sudah akan ditukar.
  2. Di masa depan, flag compiler --noImplicitVoid, dapat ditambahkan
    yang akan memperlakukan file deklarasi sama dengan file kode yang dikompilasi. Ini
    akan "melanggar", tetapi jika dilakukan jauh di jalan, sebagian besar
    perpustakaan akan mengadopsi praktik terbaik menggunakan ?T ketika tipenya bisa
    menjadi batal dan T ketika tidak bisa. Itu juga akan menjadi pilihan, jadi hanya mereka yang
    yang memilih untuk menggunakannya akan terpengaruh.

Saya pikir ini adalah pendekatan yang realistis, karena memberikan keamanan yang jauh lebih baik
ketika sumber tersedia di TypeScript, menemukan masalah rumit itu,
sambil tetap memungkinkan mudah, cukup intuitif, dan kompatibel ke belakang
integrasi dengan file definisi.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -157463734
.

Poin bagus. Ada juga banyak kesamaan dengan deklarasi parameter opsional yang ada:

``` .ts
antarmuka denganOpsionalProperty {
o?: string
}
antarmuka denganVoidableProperty {
o: ?string
}

fungsi denganOptionalParam(o?: string) {}
fungsi denganVoidableParam(o: ?string) {}
```

Sebenarnya, mereka memang menggunakan awalan . Mereka menggunakan ? untuk tipe nullable eksplisit dan ! untuk tipe non-nullable, dengan nullable sebagai default.

Perbedaan voidable vs nullable sangat masuk akal. :+1:

Saya merasa ini berputar-putar.

Sudahkah Anda semua membaca di atas mengapa definisi yang dapat dibatalkan/tidak dapat dibatalkan tidak akan menyelesaikan masalah yang mendasarinya?

@Griffork Saya sudah membaca setiap komentar. Saya akui bahwa apa yang saya katakan agak merupakan pengulangan / kombinasi dari apa yang dikatakan orang lain, tetapi saya pikir itu adalah jalan ke depan yang paling realistis. Saya tidak tahu apa yang Anda lihat sebagai masalah mendasar, tetapi bagi saya masalah mendasarnya adalah null dan undefined adalah bagian dari setiap jenis, dan kompiler saat ini tidak memastikan keamanan saat mencoba untuk menggunakan argumen tipe T . Sesuai contoh saya:

``` .ts
fungsi len(x: string): angka {
kembali x.panjang;
}

len("bekerja");
// Tidak ada kesalahan -- 5

len(nol);
// Kompilator mengizinkan ini tetapi harus kesalahan di sini dengan sesuatu seperti
// kesalahan: tidak ada properti 'panjang' dari null

len(tidak ditentukan);
// Kompilator mengizinkan ini tetapi harus kesalahan di sini dengan sesuatu seperti
// kesalahan: tidak ada properti 'panjang' yang tidak ditentukan
```

Itulah pendapat saya tentang masalah mendasar dengan _bahasa_ -- kurangnya keamanan. Sejumlah besar ini dapat diperbaiki dengan melihat aliran argumen ke dalam fungsi menggunakan analisis statis dan tipe, dan memberikan kesalahan ketika sesuatu dapat dilakukan dengan tidak aman. Tidak perlu ada tipe ?T sama sekali ketika kode tersedia karena analisis itu dapat dilakukan (walaupun tidak selalu akurat dalam setiap kasus). Alasan untuk menambahkan tipe ?T adalah karena memaksa pemeriksaan keamanan dan membuat niat programmer sangat jelas.

Masalah dengan _implementation_ adalah kompatibilitas ke belakang. Jika tim TS besok merilis perubahan yang sekarang menjadi tipe T non-void secara default, itu akan merusak kode yang ada yang saat ini menerima dan menangani input void dengan tanda tangan tipe tersebut. Anggota tim TS telah menyatakan dalam masalah ini bahwa mereka tidak mau membuat perubahan besar seperti itu. Mereka rela merusak beberapa hal, jika efeknya cukup kecil dan manfaatnya cukup besar, tetapi ini akan berdampak terlalu besar.

Proposal saya ada dalam dua bagian terpisah, salah satunya menurut saya akan menjadi tambahan yang bagus untuk bahasa yang tidak mengubah sintaks/semantik yang ada kecuali untuk menemukan bug yang benar-benar nyata, dan yang lainnya adalah cara yang mungkin untuk mengurangi dampak dari melanggar perubahan untuk mendapatkan jaminan yang lebih kuat dari jenis non-void di masa depan. Ini masih merupakan perubahan yang melanggar, tetapi dengan ukuran dan sifat yang mudah-mudahan lebih dapat diterima.

Bagian satu:

  • Tambahkan analisis untuk mengidentifikasi kapan tipe null/undefined/void dapat diteruskan ke fungsi yang tidak menanganinya (hanya berfungsi saat kode TS ada, bukan dalam file definisi).
  • Tambahkan tipe ?T yang memaksa cek batal sebelum menggunakan argumen. Ini benar-benar hanya gula sintaksis di sekitar jenis opsi tingkat bahasa.
  • Kedua fitur ini dapat diimplementasikan secara independen karena masing-masing memiliki kelebihan masing-masing

Bagian kedua:

  • Tunggu. Nanti setelah Bagian Satu diperkenalkan, dan komunitas serta pengguna TS memiliki waktu untuk menggunakan fitur-fitur tersebut, standarnya adalah menggunakan T ketika jenisnya bukan null, dan ?T ketika tipenya bisa nol. Ini tidak dijamin, tetapi saya pikir ini akan menjadi praktik terbaik yang jelas dan jelas.
  • Sebagai fitur terpisah, tambahkan opsi kompiler --noImplicitVoid yang mengharuskan tipe menjadi ?T jika dapat dibatalkan. Ini hanyalah kompiler yang menerapkan praktik terbaik yang sudah ada. Jika ada file definisi yang tidak mematuhi praktik terbaik, itu akan salah, tapi itulah mengapa itu adalah keikutsertaan.
  • Jika Anda benar-benar ingin ketat, flag dapat menerima argumen yang menentukan direktori/file mana yang harus diterapkan. Kemudian Anda dapat menerapkan perubahan hanya pada kode Anda, dan mengecualikan node_modules .

Menurut saya ini adalah pilihan yang paling realistis karena Part One bisa dilakukan meski tanpa part kedua. Ini masih merupakan fitur bagus yang sebagian besar akan mengurangi masalah ini. Tentu itu tidak sempurna, tapi saya akan mengambil cukup baik jika itu berarti itu bisa terjadi. Itu juga meninggalkan opsi di atas meja untuk tipe non-null yang sebenarnya di masa depan. Masalah saat ini adalah semakin lama masalah ini berlanjut, semakin banyak perubahan yang merusak untuk memperbaikinya karena ada lebih banyak kode yang ditulis dalam TS. Dengan Bagian Satu, paling buruk akan memperlambatnya secara dramatis, dan paling baik mengurangi dampaknya dari waktu ke waktu.

@tejacques Saya, misalnya, benar-benar

@tejacques - FWIW, saya sepenuhnya setuju dengan penilaian dan proposal Anda. Semoga tim TS setuju :)

Sebenarnya, dua hal:

Satu, saya tidak yakin analisis seperti Aliran yang disebutkan Bagian Satu itu perlu. Meskipun sangat keren dan berguna, saya tentu tidak ingin itu menahan tipe voidable/ ?T , yang tampaknya jauh lebih layak dalam desain bahasa saat ini dan memberikan nilai jangka panjang yang jauh lebih banyak.

Saya juga akan mengucapkan --noImplicitVoid sedikit berbeda - katakanlah itu melarang menetapkan null dan undefined ke tipe yang tidak dapat dibatalkan (yaitu, default) daripada "memerlukan tipe ?T jika bisa dibatalkan". Saya cukup yakin maksud kami sama, hanya semantik; berfokus pada penggunaan daripada definisi, yang, jika saya memahami cara kerja TS, adalah satu-satunya hal yang benar-benar dapat ditegakkan.

Dan sesuatu baru saja muncul di benak: kita akan memiliki empat tingkat kekosongan (ini juga berlaku untuk Flow, yang menurut saya sangat menginspirasi percakapan ini):

interface Foo {
  w: string;
  x?: string;
  y: ?string;
  z?: ?string;
}

Di bawah --noImplicitVoid , w hanya dapat berupa string yang valid. Ini adalah kemenangan besar untuk keamanan tipe. Selamat tinggal, kesalahan miliaran dolar! Tanpa --noImplicitVoid , tentu saja, satu-satunya batasan adalah harus ditentukan, tetapi bisa null atau tidak ditentukan. Ini adalah perilaku bahasa yang agak berbahaya, saya pikir, karena sepertinya itu menjamin lebih dari yang sebenarnya.

x sepenuhnya lunak di bawah pengaturan saat ini. Itu bisa berupa string, null, undefined, dan bahkan tidak perlu ada di objek yang mengimplementasikannya. Di bawah --noImplicitVoid , segalanya menjadi sedikit lebih rumit... Anda mungkin tidak mendefinisikannya, tetapi jika Anda _melakukan_ menetapkan sesuatu padanya, tidak bisakah itu batal? Saya pikir cara Flow menangani ini adalah Anda dapat mengatur x ke undefined (meniru ketidakberadaan), tetapi tidak null . Ini mungkin sedikit terlalu keras untuk TypeScript.

Juga, masuk akal untuk meminta pemeriksaan batal x sebelum menggunakannya, tetapi sekali lagi itu akan menjadi perubahan yang melanggar. Bisakah perilaku ini menjadi bagian dari flag --noImplicitVoid ?

y dapat disetel ke string atau nilai batal apa pun, tetapi _harus_ ada dalam beberapa bentuk, bahkan jika batal. Dan, tentu saja, untuk mengaksesnya memerlukan pemeriksaan batal. Perilaku ini mungkin sedikit mengejutkan. Haruskah kita menganggap _not_ menyetelnya sama dengan menyetelnya ke undefined ? Jika demikian, apa yang membuatnya berbeda dari x , kecuali memerlukan cek batal?

Dan akhirnya, z tidak perlu ditentukan, dapat diatur ke apa saja (well, kecuali non-string tentu saja), dan (untuk alasan yang baik) memerlukan pemeriksaan kosong sebelum mengaksesnya. Semuanya masuk akal di sini!

Ada sedikit tumpang tindih antara x dan y , dan saya menduga bahwa x pada akhirnya akan ditinggalkan oleh komunitas, lebih memilih formulir z untuk keamanan maksimum .

@tejacques

Tidak perlu ada tipe ?T sama sekali ketika kode tersedia karena analisis itu dapat dilakukan (walaupun tidak selalu akurat dalam setiap kasus).

Pernyataan ini tidak benar. Banyak aspek nullability tidak dapat dipertanggungjawabkan bahkan dengan sumber kode lengkap yang tersedia.

Misalnya: saat memanggil ke antarmuka (statis), Anda tidak dapat mengetahui apakah melewatkan null baik-baik saja atau tidak jika antarmuka tidak diberi keterangan yang sesuai. Dalam sistem tipe struktural seperti TS, bahkan tidak mudah untuk mengetahui objek mana yang merupakan implementasi dari antarmuka dan mana yang tidak. Secara umum Anda tidak peduli, tetapi jika Anda ingin menyimpulkan nullability dari kode sumber, Anda akan melakukannya.

Contoh lain: jika saya memiliki array list dan fungsi unsafe(x) yang kode sumbernya menunjukkan bahwa ia tidak menerima argumen null , kompiler tidak dapat mengatakan jika itu baris aman atau tidak: list.filter(unsafe) . Dan faktanya, kecuali Anda dapat mengetahui secara statis semua kemungkinan isi list , ini tidak dapat dilakukan.

Kasus-kasus lain terkait dengan warisan dan banyak lagi.

Saya tidak mengatakan bahwa alat analisis kode yang akan menandai pelanggaran terang-terangan terhadap kontrak nol tidak memiliki nilai (ya). Saya hanya menunjukkan bahwa mengecilkan kegunaan anotasi nol ketika kode sumber tersedia adalah kesalahan IMHO.

Saya telah mengatakan di suatu tempat dalam diskusi ini bahwa saya pikir kesimpulan dari nullability dapat membantu mengurangi kompatibilitas mundur dalam banyak kasus sederhana. Tapi itu tidak bisa sepenuhnya menggantikan anotasi sumber.

@tejacques saya buruk, saya salah membaca komentar Anda (untuk beberapa alasan otak saya memutuskan void===null :( saya menyalahkan baru bangun).
Terima kasih untuk posting tambahannya, itu jauh lebih masuk akal bagi saya daripada posting asli Anda. Saya sebenarnya cukup menyukai ide itu.

Saya akan membalas Anda bertiga secara terpisah karena menuliskannya di satu posting adalah gumpalan yang tidak dapat dibaca.

@Griffork Tidak masalah. Terkadang sulit untuk menyampaikan semuanya dengan benar melalui teks dan saya pikir itu layak untuk diklarifikasi.

@dallonf Pengulangan kata Anda persis seperti yang saya maksud -- kami berada di halaman yang sama.

Saya pikir perbedaan antara x?: T dan y: ?T adalah dalam penggunaan tooltips/fungsi, dan dalam pengetikan dan penjaga yang digunakan.

Mendeklarasikan sebagai argumen opsional mengubah penggunaan tooltips/fungsi sehingga jelas itu opsional:
a?: T tidak perlu diteruskan sebagai argumen dalam pemanggilan fungsi, dapat dikosongkan
Jika deklarasi tidak memiliki ?: , deklarasi harus diteruskan sebagai argumen dalam pemanggilan fungsi, dan tidak boleh dikosongkan

w: T adalah argumen non- void (dengan --noImplicitVoid )
tidak membutuhkan penjaga

x?: T adalah argumen opsional, jadi tipenya benar-benar T | undefined
membutuhkan penjaga if (typeof x !== 'undefined') .
Perhatikan mesin terbang rangkap tiga !== untuk pemeriksaan yang tepat pada undefined .

y: ?T adalah argumen yang diperlukan, dan tipenya benar-benar T | void
membutuhkan penjaga if (y == null) .
Perhatikan mesin terbang ganda == yang cocok dengan null dan undefined yaitu void

z?: ?T adalah argumen opsional, dan tipenya benar-benar T | undefined | void yaitu T | void
membutuhkan penjaga if (z == null) .
Perhatikan lagi mesin terbang ganda == yang cocok dengan null dan undefined yaitu void

Seperti semua argumen opsional, Anda tidak dapat memiliki argumen wajib yang mengikuti argumen opsional. Jadi itulah perbedaannya. Sekarang, Anda juga bisa melakukan penjagaan null pada argumen opsional, dan itu juga akan berhasil, tetapi perbedaan utamanya adalah Anda tidak dapat meneruskan nilai null ke fungsi jika Anda menyebutnya; namun, Anda dapat melewati undefined .

Saya pikir semua ini benar-benar memiliki tempat, jadi itu tidak serta-merta mencela sintaks argumen opsional saat ini, tetapi saya setuju bahwa formulir z adalah yang paling aman.

Sunting: Perbarui kata-kata dan perbaiki beberapa kesalahan ketik.

@ jods4 Saya setuju dengan hampir semua yang Anda katakan. Saya tidak mencoba untuk mengecilkan pentingnya non- void mengetik. Saya hanya mencoba untuk mendorongnya satu fase pada satu waktu. Jika tim TS tidak dapat melakukannya nanti, setidaknya kita lebih baik, dan jika mereka dapat melakukannya di kemudian hari setelah menerapkan lebih banyak pemeriksaan dan ?T maka misi itu tercapai.

Saya pikir kasus dengan Arrays benar-benar rumit. Anda selalu dapat melakukan sesuatu seperti ini:

``` .ts
fungsi numToString(x: angka) {
kembalikan x.toString();
}
var angka: angka[] = Array(100);
angkaToString(angka[0]); // Kamu kacau!

You can try to do something specifically for uninitialized arrays, like typing the `Array` function as `Array<?T>` / `?T[]` and upgrading it to `T[]` after a for-loop initializing it, but I agree that you can't catch everything. That said, that's already a problem anyway, and arrays typically don't even send uninitialized values to `map`/`filter`/`forEach`.

Here's an example -- the output is the same on Node/Chrome/IE/FF/Safari.

``` .ts
function timesTwo(x: number) {
    return x * 2;
}
function all(x) {
    return true;
}
var nums: number[] = Array(100);
nums.map(timesTwo);
// [undefined x 100]
nums.filter(all);
// []
nums.forEach(function(x) { console.log(x); })
// No output

Itu benar-benar tidak banyak membantu Anda, karena ini tidak terduga, tetapi itu bukan kesalahan dalam JavaScript nyata hari ini.

Satu-satunya hal lain yang ingin saya tekankan adalah Anda dapat membuat kemajuan bahkan dengan antarmuka, itu hanya lebih banyak pekerjaan dan upaya melalui analisis statis daripada melalui sistem tipe, tetapi itu tidak terlalu berbeda dengan apa yang sudah terjadi sekarang.

Berikut ini contoh. Mari kita asumsikan bahwa --noImplicitVoid tidak aktif

``` .ts
antarmuka ITransform{
(x: T): U;
}

antarmuka IHaveName {
nama: tali;
}

transformasi fungsi(x: T, fn: ITransform) {
kembali fn(x);
}

var bernama = {
nama: "Fu"
};

var salahNama = {
nama: 1234
};

var bernamaNull = {
nama: null
};

var someFun = (x: IHaveName) => x.name;
var someFunHandlesVoid = (x: IHaveName) => {
if (x != null && x.name != null) {
kembali x.nama;
}
kembalikan "Tidak Ada Nama";
};

All of the above code compiles just fine -- no issues. Now let's try using it

``` .ts
someFun(named);
// "Foo"
someFun(wrongName);
// error TS2345: Argument of type '{ name: number; }' is not assignable to parameter
// of type 'IHaveName'.
//   Types of property 'name' are incompatible.
//     Type 'number' is not assignable to type 'string'.
someFun(null);
// Not currently an error, but would be something like this:
// error TS#: Argument of type 'null' is not assignale to parameter of type 'IHaveName'.
someFun(namedNull);
// Not currently an error, but would be something like this:
// error TS#: Argument of type '{ name: null; }' is not assignable to parameter of
// type 'IHaveName'.
//   Types of property 'name' are incompatible.
//     Type 'null' is not assignable to type 'string'.

someFunHandlesVoid(named);
// "Foo"
someFunHandlesVoid(wrongName);
// error TS2345: Argument of type '{ name: number; }' is not assignable to parameter
// of type 'IHaveName'.
someFunHandlesVoid(null);
// "No Name"
someFunHandlesVoid(namedNull);
// "No Name"

transform(named, someFun);
// "Foo"
transform(wrongName, someFun);
// error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage.
// Consider specifying the type arguments explicitly.
//   Type argument candidate '{ name: number; }' is not a valid type argument because it
//   is not a supertype of candidate 'IHaveName'.
//     Types of property 'name' are incompatible.
//       Type 'string' is not assignable to type 'number'.
transform(null, someFun);
// Not currently an error, but would be something like this:
// error TS#: The type argument for type parameter 'T' cannot be inferred from the usage.
// Consider specifying the type arguments explicitly.
//   Type argument candidate 'null' is not a valid type argument because it
//   is not a supertype of candidate 'IHaveName'.
transform(namedNull, someFun);
// Not currently an error, but would be something like this:
// error TS#: The type argument for type parameter 'T' cannot be inferred from the usage.
// Consider specifying the type arguments explicitly.
//   Type argument candidate '{ name: null; }' is not a valid type argument because it
//   is not a supertype of candidate 'IHaveName'.
//     Types of property 'name' are incompatible.
//       Type 'string' is not assignable to type 'null'.

transform(named, someFunHandlesVoid);
// "Foo"
transform(wrongName, someFunHandlesVoid);
// error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage.
// Consider specifying the type arguments explicitly.
//   Type argument candidate '{ name: number; }' is not a valid type argument because it
//   is not a supertype of candidate 'IHaveName'.
transform(null, someFunHandlesVoid);
// "No Name"
transform(namedNull, someFunHandlesVoid);
// "No Name"

Anda benar bahwa Anda tidak dapat menangkap semuanya, tetapi Anda dapat menangkap banyak hal.

Catatan terakhir -- bagaimana seharusnya perilaku di atas ketika --noImplicitVoid aktif?

Sekarang someFun dan someFunHandlesVoid keduanya typechecked sama dan menghasilkan pesan kesalahan yang sama yang dihasilkan someFun . Meskipun someFunHandlesVoid menangani void, memanggilnya dengan null atau undefined adalah kesalahan karena tanda tangan menyatakan dibutuhkan non void . Itu perlu diketik sebagai (x: ?IHaveName) : string untuk menerima null atau undefined . Jika kita mengubah jenisnya, maka itu akan terus berfungsi seperti sebelumnya.

Ini adalah bagian yang merupakan perubahan utama, tetapi yang harus kita lakukan untuk memperbaikinya adalah menambahkan satu karakter ? ke tanda tangan tipe. Kita bahkan dapat memiliki flag lain --warnImplicitVoid yang melakukan hal yang sama sebagai peringatan sehingga kita dapat berpindah secara perlahan.

Saya merasa seperti orang brengsek karena melakukan ini, tetapi saya akan membuat satu posting lagi.

Pada titik ini saya tidak yakin apa yang harus dilakukan untuk melanjutkan. Apakah ada ide yang lebih baik? Haruskah kita:

  • terus mendiskusikan/mencari tahu bagaimana ini harus berperilaku?
  • mengubah ini menjadi tiga proposal fitur baru?

    • Analisis yang Ditingkatkan

    • Jenis Mungkin/Opsi ?T

    • --noImplicitVoid opsi kompiler

  • ping anggota tim TypeScript untuk masukan?

Saya condong ke proposal baru dan melanjutkan diskusi di sana karena hampir tidak manusiawi meminta tim TypeScript untuk mengejar utas ini mengingat berapa lama.

@tejacques

  • Anda kehilangan typeof dalam contoh triple sama dengan dallonf.
  • Anda tampaknya kehilangan beberapa ? dalam contoh ke jods4.

Meskipun saya pikir hal-hal itu harus tetap ada di utas ini, saya pikir utas ini tidak benar-benar "diawasi" lagi (mungkin lebih seperti melirik sesekali). Jadi membuat beberapa utas baru pasti akan membangun daya tarik.
Tapi tunggu beberapa hari/minggu sampai orang-orang muncul dan memberikan umpan balik terlebih dahulu. Anda ingin proposal Anda cukup solid.

Sunting: penurunan harga yang diingat ada.

Mengomentari utas ini cukup sia-sia pada tahap ini. Bahkan jika proposal dibuat yang dianggap dapat diterima oleh tim TypeScript (saya mencoba ini di atas pada bulan Agustus), tidak mungkin mereka akan menemukannya di antara kebisingan.

Yang terbaik yang dapat Anda harapkan adalah bahwa tingkat perhatian cukup untuk mendorong tim TypeScript untuk membuat proposal mereka sendiri dan mengimplementasikannya. Jika tidak, lupakan saja dan gunakan Flow.

+1 untuk memisahkan ini, tetapi untuk saat ini, opsi --noImplicitVoid dapat
tunggu hingga tipe nullable diimplementasikan.

Sejauh ini, sebagian besar kita telah mencapai kesepakatan tentang sintaks dan semantik dari
tipe nullable, jadi jika seseorang bisa menulis proposal dan implementasi
itu, itu akan menjadi emas. Saya mendapat proposal dari proses serupa
tentang enum jenis lain, tetapi saya tidak punya waktu untuk
mengimplementasikannya karena proyek lain.

Pada Rabu, 18 November 2015, 21:24 Tom Jacques [email protected] menulis:

Saya merasa benar-benar brengsek karena melakukan ini, tetapi saya akan membuat satu lagi
Pos.

Pada titik ini saya tidak yakin apa yang harus dilakukan untuk melanjutkan. Apakah ada ide yang lebih baik?
Haruskah kita:

  • terus mendiskusikan/mencari tahu bagaimana ini harus berperilaku?
  • mengubah ini menjadi tiga proposal fitur baru?

    • Analisis yang Ditingkatkan

    • Jenis Mungkin/Opsi ?T

    • --noImplicitVoid opsi kompiler

  • ping anggota tim TypeScript untuk masukan?

Saya condong ke proposal baru dan melanjutkan diskusi di sana sejak
hampir tidak manusiawi meminta tim TypeScript untuk mengikuti utas ini
mengingat berapa lama.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -157928828
.

+1 untuk opsi --noImplicitNull (larang batal dan penetapan nol).

Saya mencoba mengurangi masalah ini dengan tipe khusus Op<A> = A | NullType . Tampaknya bekerja dengan cukup baik. Lihat di sini .

+1 untuk _--noImplicitNull_ juga TOLONG :+1:

+1 untuk --noImplicitNull

Apakah ini harus ditutup?

@Gaelan Mengingat #7140 digabungkan, jika Anda ingin mengajukan masalah khusus baru untuk --noImplicitNull seperti yang disarankan oleh beberapa orang di sini, maka mungkin aman untuk melakukannya sekarang.

@isiahmeadows Mungkin akan lebih baik membiarkan ini terbuka.

Apakah ini harus ditutup?

Kami pikir https://github.com/Microsoft/TypeScript/issues/2388 adalah bagian penggantian nama dari pekerjaan ini. Inilah sebabnya mengapa kami belum menyatakan fitur ini lengkap.

jika Anda ingin mengajukan masalah khusus baru untuk --noImplicitNull seperti yang disarankan oleh beberapa orang di sini, maka mungkin aman untuk melakukannya sekarang.

Saya tidak yakin saya mengerti apa yang diminta semantik dari bendera baru ini. saya akan merekomendasikan membuka edisi baru dengan proposal yang jelas.

@mhegazy Ide yang diajukan sebelumnya dalam masalah ini untuk --noImplicitNull adalah bahwa semuanya harus secara eksplisit ?Type atau !Type . IMHO Saya tidak merasa itu layak untuk boilerplate ketika ada flag lain yang menyimpulkan non-nullable secara default bahwa IIRC sudah diimplementasikan ketika tipe nullable itu sendiri.

Menutup sekarang karena #7140 dan #8010 keduanya digabungkan.

Maaf jika saya mengomentari masalah yang ditutup tetapi saya tidak tahu tempat yang lebih baik untuk bertanya dan saya tidak berpikir ini layak untuk masalah baru jika tidak ada minat.
Apakah layak untuk menangani null implisit berdasarkan per-file?
Seperti, menangani banyak file td dengan noImplicitNull (karena mereka berasal dari pasti diketik dan dikandung seperti itu) tetapi menangani sumber saya sebagai implisitNull?
Apakah ada yang menganggap ini berguna?

@massimiliano-mantione, silakan lihat https://github.com/Microsoft/TypeScript/issues/8405

Apakah halaman ini membantu?
0 / 5 - 0 peringkat