I have an object that is going to have field products, this field will be filled with the response returned by different endpoints which each return a type of product. something like:
const products = {
// each of this kind of products comes from endpoint response
someProduct: {},
someOtherProduct: {}
anotherProduct: {}
}
I Can type all product types like this, according this answer:
interface BaseProduct {
name: string;
description: string;
price: number;
type: string;
}
interface SomeProduct extends BaseProduct {
genericAttrOne: string;
genericAttrTwo: string;
}
interface OtherProduct extends BaseProduct {
anAttributeOne: string;
anAttributeTwo: string;
}
interface AnotherProduct extends BaseProduct {
anotherAttributeOne: string;
anotherAttributeTwo: string;
}
type Product = SomeProduct | OtherProduct | AnotherProduct;
const productsAsArray: Product[] = [];
the situation here is that sometimes I will go through all the product types(productsAsArray), and I will render it, then user click on some product and I take it as the productSelected
. how should I type this productSelected
if it can be of any type of product? and for this selectedProduct
I will access product-specific properties...
CodePudding user response:
Use discriminated unions to help you narrow your type down.
Here is an example:
interface Base {
type: string;
baseProp: string;
}
interface T1 extends Base {
type: "t1";
t1prop: string;
}
interface T2 extends Base {
type: "t2";
t2prop: string;
}
interface T3 extends Base {
type: "t3";
t3prop: string;
}
type union = T1 | T2 | T3;
function list(arr: union[]) {
const randomArrItem = arr[Math.floor(Math.random() * arr.length)];
specificMethod(randomArrItem);
}
function specificMethod(t: union) {
const { type } = t;
switch (type) {
case "t1":
console.log(t.t1prop);
// console.log(t.t2prop); // error: Property 't2prop' does not exist on type 'T1'. Did you mean 't1prop'?(2551)
break;
case "t2":
console.log(t.t2prop);
break;
case "t3":
console.log(t.t3prop);
break;
default:
throw new Error("wrong type");
}
}
See TS Playground: https://tsplay.dev/wj4obm
CodePudding user response:
Since they have the same signature, add a new property type
that is an enum.
The reason to use an enum is that you can check it with an if or switch case.
const enum ProductType {
one = "ONE",
two= "TWO",
three= "THREE",
}
interface BaseProduct {
name: string;
description: string;
price: number;
}
type ProductOne = BaseProduct & {
type: ProductType.one,
prop1: string,
prop2: string
}
type ProductTwo = BaseProduct & {
type: ProductType.two,
prop1: string,
prop2: string
}
type ProductThree = BaseProduct & {
type: ProductType.three,
prop1: string,
prop2: string
}
type Product = ProductOne | ProductTwo | ProductThree ;
function handleProductSwitch(product: Product){
switch(product.type){
case ProductType.one:
// handle ProductOne
case ProductType.two:
// handle ProductTwo
case ProductType.three:
// handle ProductThree
case default:
}
}
function handleProductIf(product: Product){
if(product.type === ProductType.one) productOneHandler(product);
if(product.type === ProductType.two) productTwoHandler(product);
if(product.type === ProductType.three) productThreeHandler(product);
}