typescript - TS2339: Ayırt edici birleşimde Array.find türünü doğru şekilde çıkarmıyor


Bu örnekte, farklı türde öğelerle doldurulabilecek bir arabam var; bu durumda bunlar golf topları ve golf sopalarıdır ve bunların kendi seçenekleri vardır.

Typescript Playground bağlantısı

Aşağıdaki kod ile şu hatayı alıyorum:

TS2339: Property 'color' does not exist on type '{ color: "blue" | "red" | "white"; } | { variant: "wedge" | "putter"; }'.
  Property 'color' does not exist on type '{ variant: "wedge" | "putter"; }'.
type ProductGolfBall = {
  type: "golfball";
  options: {
    color: "blue" | "red" | "white";
  };
};

type ProductGolfClub = {
  type: "golfclub";
  options: {
    variant: "wedge" | "putter";
  };
};

type CartItem = ProductGolfBall | ProductGolfClub;
type CartItems = Array<CartItem>;

const cart: CartItems = [
  {
    type: "golfball",
    options: {
      color: "blue"
    }
  },
  {
    type: "golfclub",
    options: {
      variant: "wedge"
    }
  },
  {
    type: "golfclub",
    options: {
      variant: "putter"
    }
  }
];

const golfball = cart.find((item) => item.type === "golfball");

if (golfball) { // Check that it's truthy
  // According to typescript this can still be either ProductGolfClub or ProductGolfBall
  console.log(golfball.type)
  console.log(golfball.options.color) // Produces the TS2339 error above
}

Şimdi sadece, `find` işlemi yalnızca `type` özelliği `golfball` olan bir dizi öğesi için true döndürürken neden `golfball` değişkeninin hala `ProductGolfClub` olabileceğini anlamıyorum.

golfball değişkenini ProductGolfBall olarak dönüştürebilirdim, ama typescript'in değişkenin hangi tipe sahip olduğunu anlaması için başka bir yol olmalı.

Cevap #1

Ne yazık ki TS bu durumdan kutudan çıktığı gibi tür koruyucu oluşturamıyor. Ama işlevin neyi garanti edeceğini açıkça belirterek elle yapabilirsiniz, şöyle düşünün:

function isGolfBall(item: CartItem): item is ProductGolfBall {
    return item.type === "golfball";
}

const golfball = cart.find(isGolfBall)

if (golfball) { // Check that it's truthy
  console.log(golfball.type)
  console.log(golfball.options.color) // no error 👌
}

En önemlisi :item is ProductGolfBall. Bu, bu fonksiyonun açıkça bir tür koruyucu olacağını ve yalnızca ProductGolfBall için geçeceğini (true döndüreceğini) ifade eder.

Satır içi çözüm de olur:

const golfball = cart.find((item): item is ProductGolfBall => item.type === "golfball")

İlgili makaleler