typescript - Bir tip iddiası kullanmadan bir dizgenin dize literal tipinde bulunup bulunmadığını nasıl kontrol edebilirim?
Ben sıklıkla bu türden string literal türlerine sahip oluyorum:
const supportedLanguageTags = [
"de-at",
"de-ch",
"de-de",
"en-gb",
"en-us",
] as const;
type LanguageTag = typeof supportedLanguageTags[number];
function isSupportedLanguageTag(tag: string): tag is LanguageTag {
return (supportedLanguageTags as unknown as string[]).includes(tag);
}
Burada, bir diziyi türü tanımlamak için kullanıyorum ama aynı zamanda rastgele bir stringin literal tür içinde olup olmadığını kontrol edebilmek için de kullanıyorum.
Ancak ben gerçekten bu tür onaylamayı sevmiyorum. Onu ortadan kaldırmak için önerileriniz var mı?
Bir çözüm, bir dizi yerine bu şekilde bir nesne kullanmak olurdu:
const supportedLanguageTags = {
"de-at": 1,
"de-ch": 1,
"de-de": 1,
"en-gb": 1,
"en-us": 1,
};
type LanguageTag = keyof typeof supportedLanguageTags;
function isSupportedLanguageTag(tag: string): tag is LanguageTag {
return tag in supportedLanguageTags;
}
Ama burada her nesne özelliği için rastgele bir değer tanımlamak zorunda olmamdan hoşlanmıyorum.
Cevap #1
Benzer bir soruya verilen yanıtla belirtildiği gibi, Array.prototype.includes() çağrı imzası için TypeScript tip tanımlamaları, aranan değerin dizi elemanlarıyla aynı tipte olmasını gerektirir. Eğer dizi elemanları string tipindeyse, herhangi bir string için arama yapabilirsiniz. Ancak dizi elemanları string'den daha dar bir tipteyse, örneğin string literal tiplerin bir birleşimi gibi, o zaman rastgele bir string için arama yapmanız engellenir. Bu kısıtlama, tip güvenliği için gerçekten gerekli değildir; eleman türlerinden daha geniş bir şey için güvenle arama yapabilmelisiniz.
microsoft/TypeScript#26255 sorunu burada destek istemek için açıldı, ancak microsoft/TypeScript#14520 ile aynı olduğu gerekçesiyle kapatıldı; bu sorun süpertip kısıtlamalarını temsil etme yolunu ele alıyor. TypeScript'te sadece extends var, burada U extends T, U'nun T'nin bir alt tipi olması gerektiği anlamına gelir. Array<T> için aslında includes<U super T>(searchElement: U): boolean gibi bir şey istersiniz, burada U super T, U'nun T'nin bir üst tipi olması gerektiği anlamına gelir. Ancak TypeScript böyle bir sözdizimine sahip değil. Bunu koşullu tiplerle simüle edebilirsiniz, örneğin includes<U extends (T extends U ? unknown : never)>(searchElement: U), ancak bu oldukça karmaşıktır ve şu anda TypeScript tiplerinin parçası değildir.
Burada yapabileceğiniz bir şey, dizinizin türünü güvenli bir şekilde readonly string[] olarak genişletmektir. readonly dizisi türü, push() gibi değiştirici yöntemlere sahip olmadığı bilinen bir string üst türüdür. Bir string literalleri dizisi, içeriğini değiştirmeyeceğiniz için güvenli bir şekilde readonly string[] olarak genişletilebilir ve herhangi bir string literal türündeki bir değeri, string bekleyen bir şeye okumak her zaman daha güvenlidir.
Tür tipi atamaları hem (güvenli) genişletmeye hem de (güvenli olmayan) daraltmaya izin verir. Ancak kazara güvenli olmayan bir daraltma yapmadığınızdan emin olmak istiyorsanız, tür atamalarını kullanmayabilir ve bunun yerine yeni bir değişkeni not edebilirsiniz. Tür notları yalnızca güvenli genişletmeleri destekler.
Bu, bunu yazabileceğiniz anlamına geliyor:
function isSupportedLanguageTag(tag: string): tag is LanguageTag {
const s: readonly string[] = supportedLanguageTags; // okay
return s.includes(tag); // okay
}
Ve tür güvenliğiniz var. Yine, s değişkeni gerçekten gerekli değil ve bir tür bildirimi bunu daha kısa return (supportedLanguageTage as readonly string[]).includes(tag); hâline getirecektir, eğer güvenlik garantileri yerine kısalığı önemsiyorsanız.
Koda yönelik oyun alanı bağlantısı
Cevap #2
Array.prototype.includes ile ilgili bilinen bir sorun var ama linki bulamıyorum. TypeScript’i kandırmak için fonksiyonunuzu aşırı yükleyebilirsiniz. Aşırı yüklemeler biveriyan olarak çalıştığı için TS size tag is LanguageTag dönüş türünü kullanmanıza izin verecektir:
const supportedLanguageTags = [
"de-at",
"de-ch",
"de-de",
"en-gb",
"en-us",
] as const;
type LanguageTag = typeof supportedLanguageTags[number];
function isSupported(tag: string): tag is LanguageTag
function isSupported(tag: any) {
return supportedLanguageTags.includes(tag)
}
isSupported('hello') // ok
isSupported(42) // error
Oyun alanı
Evet, any kullandım, ama isSupported fonksiyonunu sayı ile çağırmanıza izin yok - sadece string ile.
