TypeScript'te `never` tipi neden tanıtıldı?


Şimdi interneti, never tipinin neden getirildiğine dair bir açıklama bulmak için araştırdım. Hem TypeScript el kitabı hem de TypeScript Deep Dive bunun ne olduğunu açıklıyor ve birkaç başka makale de var. Ancak hiçbiri never tipini neden kullanacağınızı ve neyi çözdüğünü açıklamıyor. Örneklerin çoğu, derleyicinin zaten never çıkarımında bulunduğu kodları gösteriyor, o zaman tip atamak zorunda mıyım? Ne çözüyor? Bu sadece intellisense’e sonradan ne yapabileceğimi (yani hiçbir şey - ki bu yeterince yararlı olabilir) bildirmek için mi? Ve derleyici bu bilgiyi ne için kullanıyor? Her zaman olduğu gibi, NE ve NASIL açıklamaları insanların iş yapmasına yardımcı olabilir, ama sadece NEDEN onları anlamalarını sağlar.

Cevap #1

never türü, TypeScript'in tür teorisindeki alt türün temsilidir. Yani TypeScript'te never için motivasyon ve kullanım alanlarını arıyorsanız, genel olarak bilgisayar bilimlerinde alt tür üzerine bakmak isteyebilirsiniz.

“never neden TypeScript’e tanıtıldı” sorusuna verilen kanonik cevap, muhtemelen bunu tanıtan pull request üzerindeki yorumları ve ele aldığı iki konuyu analiz ederek en iyi şekilde bulunabilir: bir alt tip talebi ve ulaşılamayan fonksiyon dönüşlerini daha iyi ele alma talebi.

Geliştiriciler için ana kullanım, hiçbir değer döndürmeyen (hatta undefined bile) fonksiyonlar (veya fonksiyon bölümleri) için bir tür oluşturmaktır. Bu fonksiyonları karşılaştırın:

function u(): undefined {
    return;
}
const uRet = u();
console.log(typeof uRet); // undefined

function v(): void { }
const vRet = v();
console.log(typeof vRet); // undefined

bu fonksiyonlarla undefined değerlerini döndüren:

function throws(): never {
    throw new Error();
}
const tRet = throws();
console.log(typeof tRet); // does not run

function loops(): never {
    while (true) { }
}
const lRet = loops();
console.log(typeof lRet); // does not run

tamamen değer döndürmeyen, bir istisna veya sonsuz döngü nedeniyle. never türü, hem geliştiricilerin hem de derleyicinin hiçbir zaman çalışamayacak kod bölümleri hakkında düşünmesini sağlar.

never için başka kullanım alanları da vardır ama ben onları listelemeye çalışmayacağım, çünkü siz zaten kod örneklerinin farkındasınız. never'in göründüğü yerleri bulmak için TypeScript standart kütüphanesini incelemek aydınlatıcıdır.

Sonunda, sorduğunuzda

Örneklerin çoğu, derleyicinin zaten never türünü çıkardığı kodu gösteriyor, o halde tipi neden atamam gerekiyor?

dikkat edin ki derleyici sizin için never türünü çıkarsasa bile, dilde bulunması yine de faydalıdır, tıpkı derleyici tarafından çıkarsandığında bile string türünün faydalı olması gibi. Bu, türleri derleyicinin çıkarmasına bırakmak yerine ne zaman açıkça belirtmek isteyebileceğinize dair farklı bir soru gibi görünüyor. Ama bu gönderi zaten uzun ve muhtemelen bu konu başka yerde yanıtlanmıştır.

Umarım yardımcı olur; iyi şanslar!

Düzenleme: Bir programcının never kullanmasının birçok nedeni vardır, ancak birçok programcı buna hiç ihtiyaç duymayabilir. İşte aklıma gelen bazı nedenleri sıralayayım:

  • Eğer bir JavaScript kütüphanesi için, bir fonksiyonun döndürmek yerine istisna (exception) attığı durumlar için tip tanımlamaları yazıyorsanız, bunu gerçekten never olmadan yapamazsınız.

  • Genel tür parametreleri, derleyici tarafından çıkarılamadığında varsayılan olarak {} olur. Bu, bir anlamda "açık başarısızlık" gibidir, çünkü {} (neredeyse) tüm değerlerle uyumludur. Bazı durumlarda bunun yerine "kapalı başarısızlık" olmasını ve hiçbir değerle uyumlu olmamasını tercih edersiniz. Varsayılan parametre olarak never kullanabilirsiniz.

  • Herhangi bir tür T ile never birleşimi sadece T’dir ve T ile never kesişimi never’dir. Bu kurallar (başka kurallar arasında) bir geliştiricinin never olmadan daha zor veya imkansız olacak oldukça sofistike tür fonksiyonları oluşturmasına olanak tanır. Örneğin, işte Diff<T,U>:

    type Diff<T extends string, U extends string> =
      ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
    

    Bu, T dizesi literal birleşimini ve U dizesi literal birleşimini alan ve U içinde olmayan T içindeki tüm değerlerin birleşimini döndüren bir tür işlevdir.

    Bunu, diğer şeylerin yanı sıra, bir türden bir özelliği kaldırmak için kullanabilirsiniz:

    type Foo = { a: string, b: number, c: boolean };
    type FooWithoutB = Pick<Foo, Diff<keyof Foo, 'b'>>;
    // equivalent to { a: string, c: boolean }
    

Matematikte sıfır sayısına benziyor. Ona ihtiyacınız yok; birçok kültür sıfır kavramı olmadan da iyi geçindi. Ama inanılmaz derecede faydalı ve aksi takdirde zahmetli veya imkansız olacak fikirleri kolayca ifade etmenizi sağlıyor.

Umarım bu biraz daha ilgi çekicidir? Her neyse, yine de iyi şanslar.

İlgili makaleler