TypeScript: Birleşim Türünde Nesne Muhtemelen Null
En az bir özelliğin gerekli olduğu bir temel arayüzüm var.
interface Base {
someProp: { subProp: string; } | null;
condProp1?: boolean;
condProp2?: boolean;
}
interface WithProp1 extends Base {
condProp1: true;
}
interface WithProp2 extends Base {
condProp2: true;
}
export type MyType = WithProp1 | WithProp2;
Benzetim someProp'un null olmadığını kontrol ettikten ve condProp1 veya condProp2'den herhangi birinin doğruluğunu kontrol ettikten sonra MyType'ı kullanmaya çalıştığımda, derleyici hâlâ someProp'un null olabileceğini düşünüyor.
function doSomething(myType: MyType) {
if (myType.someProp && (
myType.condProp1 && myType.someProp.subProp === 'foo' ||
myType.condProp2 && myType.someProp.subProp === 'bar')) {
console.log("It's true, isn't it? It's really true.");
}
}
doSomething() içinde, myType.someProp.subProp derleme zamanında bir hata veriyor ve someProp değerinin null olabileceğini söylüyor. Fonksiyon imzasında MyType'ı Base ile değiştirirsem bu durum olmuyor.
// Compiles fine
function doSomethingBase(myType: Base) {
if (myType.someProp && (
myType.condProp1 && myType.someProp.subProp === 'foo' ||
myType.condProp2 && myType.someProp.subProp === 'bar')) {
console.log("It's true, isn't it? It's really true.");
}
}
İlk satır için geçici bir çözüm olarak sıralamayı myType.someProp.subProp === 'foo' && myType.condProp1 olarak değiştirmek işe yarıyor, ancak ikinci satır için işe yaramıyor. Bunun neden olduğunu veya nasıl aşılacağını bilen var mı (bir ! kullanabilirim ama kullanmamam gerekir)? Görünüşe göre derleyicide bir hata var.
TSC sürüm 3.5.3 kullanıyorum. TypeScript oyun alanı
Cevap #1
Bunun aslında bir derleyici hatası olduğunu düşünüyorum; iç yapıları yeterince yakından bilmiyorum ama derleyicinin başlangıçtaki koruma ifadesini Base türü olarak değerlendirdiğini ve ardından içteki ifadeleri WithPropX olarak değerlendirdiğini ve myType'ın WithPropX olarak bir null someProp'a karşı zaten korunduğu bilgisini kaybettiğini düşünüyorum.
Kontrollerin sırasını tersine çevirerek bunu aşabilirsiniz, böylece daha spesifik tür çıkarımı koşulda daha erken gerçekleşir (ve sonra someProp null kontrolü, derleyicinin myType'ın bir WithPropX olduğunu, Base değil, bildiği noktada gerçekleşir):
function doSomething(myType: MyType) {
if (myType.condProp1 && myType.someProp && myType.someProp.subProp === 'foo' ||
myType.condProp2 && myType.someProp && myType.someProp.subProp === 'bar') {
console.log("It's true, isn't it? It's really true.");
}
}
Bu sorun, burada gözlemlediğiniz şeyle ilgili gibi görünüyor.
Cevap #2
Ben @Chris Helad dediğini beğendim:
Derleyicinin ilk koruma cümlesini Base türü olarak değerlendirdiğinden ve ardından iç cümleleri WithPropX olarak değerlendirdiğinden ve myType'ın WithPropX olarak bazıProp'a karşı zaten korunmuş olduğu bilgisini kaybettiğinden şüpheleniyorum.
Yani, derleyici muhtemelen böyle çalışıyor:
function doSomething(myType: MyType) {
if (myType.someProp && ( // myType is type of WithProp1 | WithProp2 AND someProp is defined
myType.condProp1 && myType.someProp.subProp === 'foo' || // condProp1 is defined, so, myType is typeof WithProp1 (forgot that someProp is defined)
myType.condProp2 && myType.someProp.subProp === 'bar')) { // condProp2 is defined, so, myType is typeof WithProp2 (forgot that someProp is defined)
console.log(`It's true, isn't it? It's really true.`);
}
}
Bunu aşmak için derleyiciye gerçek türü bildirmek amacıyla basit bir tür koruyucu tanımlayabilirsiniz:
function hasProp(t: MyType): t is MyType & {someProp: NonNullable<MyType['someProp']>} {
return !!t.someProp;
}
function doSomething(myType: MyType) {
if (hasProp(myType) && (
myType.condProp1 && myType.someProp.subProp === 'foo' ||
myType.condProp2 && myType.someProp.subProp === 'bar')) {
console.log(`It's true, isn't it? It's really true.`);
}
}
