- Konu sahibi
- #1
Union türünü daraltmak için TypeScript’in çalışma zamanında gerçekten kontrol edebildiği bir koşul kullanmalısınız. En güvenilir yöntem, seçenekleri ayıran ortak bir alan tanımlayıp bu alanı kontrol etmektir. Kontrol başarılı olduğunda TypeScript ilgili dalda hangi özelliklerin kullanılabileceğini bilir; koşul dışında ise diğer union üyelerini kullanmaya devam eder.
Bir union içindeki nesneler aynı alan adına, fakat farklı sabit değerlere sahipse buna ayırt edici alanla daraltma denir. Bu alan genellikle
Burada
Bu kontrol yalnızca çalışma zamanında gerçekten mevcut olan bir değeri sınar. TypeScript’e “bu nesne kesinlikle başarılıdır” demek, nesnenin çalışma zamanındaki içeriğini değiştirmez ve yanlış veri gelmesini engellemez. Verinin dış kaynaktan geldiği durumlarda çalışma zamanı doğrulaması yine gerekebilir.
Kullanacağınız yöntem, union üyelerinin nasıl ayrıldığına bağlıdır.
Bu yöntemlerin hepsi aynı işi yapmaz.
İlkel değerlerde
Aynı kontrolü birden fazla yerde kullanıyorsanız, sonucu
Dışarıdan gelen JSON verilerinde type guard, veriyi otomatik olarak güvenilir hâle getirmez. Örneğin yalnızca
Union üyelerinin tümünü işlediğinizden emin olmak için son dalda
Daha sonra
Bir alanı kontrol ettiğiniz hâlde TypeScript türü daraltmıyorsa önce kontrolün union’ın gerçek yapısıyla örtüşüp örtüşmediğine bakın. Kontrol edilen alanın her union üyesinde bulunması, değerlerinin sabit türlerle tanımlanması ve koşulun aynı değişken üzerinde yapılması gerekir.
Örneğin aşağıdaki tanımda
Bu yapı,
Artık
Ortak ayırt edici alanla daraltma
Bir union içindeki nesneler aynı alan adına, fakat farklı sabit değerlere sahipse buna ayırt edici alanla daraltma denir. Bu alan genellikle
type, kind veya status adını taşır.
Kod:
type Success = {
type: "success";
data: string;
};
type Failure = {
type: "failure";
error: string;
};
type Result = Success | Failure;
function printResult(result: Result): void {
if (result.type === "success") {
console.log(result.data);
return;
}
console.error(result.error);
}
Burada
result.type === "success" koşulu doğruysa result artık Success olarak değerlendirilir ve data alanı kullanılabilir. Koşul yanlış olduğunda geriye Failure kalır; bu nedenle error alanına erişilebilir.Bu kontrol yalnızca çalışma zamanında gerçekten mevcut olan bir değeri sınar. TypeScript’e “bu nesne kesinlikle başarılıdır” demek, nesnenin çalışma zamanındaki içeriğini değiştirmez ve yanlış veri gelmesini engellemez. Verinin dış kaynaktan geldiği durumlarda çalışma zamanı doğrulaması yine gerekebilir.
Temel daraltma yöntemleri
Kullanacağınız yöntem, union üyelerinin nasıl ayrıldığına bağlıdır.
| Kontrol | Uygun olduğu durum | Örnek |
|---|---|---|
| typeof | İlkel türleri ayırma | typeof value === "string" |
| in | Nesnelerin sahip olduğu alanları ayırma | "radius" in shape |
| instanceof | Sınıf örneklerini ayırma | value instanceof Date |
| Eşitlik kontrolü | Ortak sabit alanla ayrılan union türleri | === "success" |
| Özel type guard | Tekrarlanan veya karmaşık kontroller | isSuccess(result) |
typeof ve instanceof çalışma zamanı değerlerini kontrol ederken, ayırt edici alan kontrolü nesnenin belirli bir özelliğine dayanır. Alan adını kontrol etmek için in kullanabilirsiniz:
Kod:
type Circle = {
radius: number;
};
type Square = {
sideLength: number;
};
type Shape = Circle | Square;
function getArea(shape: Shape): number {
if ("radius" in shape) {
return Math.PI * shape.radius ** 2;
}
return shape.sideLength ** 2;
}
"radius" in shape doğru olduğunda TypeScript nesneyi Circle olarak daraltır. Ancak alanların aynı anda bulunabileceği karmaşık modellerde yalnızca alan varlığına güvenmek yerine ayırt edici bir alan kullanmak daha açıktır.İlkel değerlerde
typeof kullanılabilir:
Kod:
function formatValue(value: string | number): string {
if (typeof value === "string") {
return value.trim();
}
return value.toFixed(2);
}
value ilk dalda string, ikinci dalda number olur. typeof ile yalnızca JavaScript’in desteklediği temel tür kontrollerini yapabilirsiniz; özel arayüz adları çalışma zamanında mevcut olmadığı için typeof value === "User" gibi bir kontrol geçerli değildir.Özel type guard yazma
Aynı kontrolü birden fazla yerde kullanıyorsanız, sonucu
value is Type biçiminde bildirilen bir type guard işlevine taşıyabilirsiniz.
Kod:
type User = {
id: number;
name: string;
};
type ErrorResponse = {
message: string;
};
type Response = User | ErrorResponse;
function isUser(value: Response): value is User {
return "id" in value && "name" in value;
}
function showResponse(response: Response): string {
if (isUser(response)) {
return response.name;
}
return response.message;
}
isUser true döndürdüğünde TypeScript, çağrının yapıldığı yerde response değerini User kabul eder. Type guard içindeki koşul yanlış yazılırsa derleyici bu mantık hatasını tamamen yakalayamaz; bu nedenle işlevin döndürdüğü boolean ile bildirilen türün gerçekten eşleşmesi sizin sorumluluğunuzdadır.Dışarıdan gelen JSON verilerinde type guard, veriyi otomatik olarak güvenilir hâle getirmez. Örneğin yalnızca
id alanının bulunması, alanın gerçekten sayı olduğu anlamına gelmez. Böyle bir durumda türü ve gerekli alanların değerlerini ayrıca kontrol edin.Union türü tamamen ele alındığında kontrol etmek
Union üyelerinin tümünü işlediğinizden emin olmak için son dalda
never kullanabilirsiniz. never, o noktaya ulaşabilecek hiçbir değer olmaması gerektiğini ifade eder.
Kod:
type Action =
| { type: "add"; value: number }
| { type: "remove"; value: number }
| { type: "reset" };
function assertNever(value: never): never {
throw new Error(`Bilinmeyen işlem: ${String(value)}`);
}
function applyAction(action: Action, total: number): number {
switch (action.type) {
case "add":
return total + action.value;
case "remove":
return total - action.value;
case "reset":
return 0;
default:
return assertNever(action);
}
}
Daha sonra
Action union’ına yeni bir seçenek eklerseniz ve switch içine karşılığını yazmazsanız, action değeri never bekleyen işleve uymaz ve eksik dalı fark edersiniz. Bu yaklaşım çalışma zamanında bilinmeyen veriyi güvenli biçimde karşılamanın yerine geçmez; derleme aşamasında union tanımının bütün üyelerinin ele alınmasını sağlar.Daraltma çalışmadığında kontrol edilecek noktalar
Bir alanı kontrol ettiğiniz hâlde TypeScript türü daraltmıyorsa önce kontrolün union’ın gerçek yapısıyla örtüşüp örtüşmediğine bakın. Kontrol edilen alanın her union üyesinde bulunması, değerlerinin sabit türlerle tanımlanması ve koşulun aynı değişken üzerinde yapılması gerekir.
Örneğin aşağıdaki tanımda
kind alanı genel string türündedir:
Kod:
type A = { kind: string; value: number };
type B = { kind: string; message: string };
type Item = A | B;
Bu yapı,
kind alanının hangi üyeyi temsil ettiğini TypeScript’e bildirmez. Daha belirgin bir union için sabit değerler kullanın:
Kod:
type A = { kind: "value"; value: number };
type B = { kind: "message"; message: string };
type Item = A | B;
Artık
item.kind === "value" kontrolü Item türünü doğru biçimde daraltır. Nesneyi as ile zorla başka bir türe çevirmek ise daraltma yapmaz; yalnızca derleyiciye bir varsayımı kabul ettirir. Bu varsayım yanlışsa çalışma zamanında hata alabilirsiniz. Önce gerçek bir kontrol, karmaşık durumlarda da doğrulama işlevi kullanın.