Home > Software design >  Generic function in Typescript
Generic function in Typescript

Time:01-03

I'm trying to create a function with generic type in typescript. I have these function whose do the same

const convertBeverages = (beveragesCollection: BeveragesQuery): Beverage[] => {
    let beverages: Beverage[] = [];

    if (beveragesCollection) {
        beverages =
            beveragesCollection.data.map(({ id, attributes }) => {
                const restaurant =
                    attributes?.restaurant?.reduce((acc, currentValue) => {
                        const restaurantId = currentValue?.restaurant?.data?.id ?? "-1";

                        return currentValue && currentValue.available
                            ? [...acc, { price: currentValue.price, restaurantId }]
                            : acc;
                    }, [] as PriceRestaurant[]) ?? [];

                const beverage = {
                    id: id ?? "",
                    name: attributes?.name ?? "",
                    position: attributes?.position ?? 0,
                    restaurant,
                };

                return beverage;
            }) ?? [];
    }
    return beverages;
};
const convertDesserts = (dessertCollection: DessertsQuery): Dessert[] => {
    let desserts: Dessert[] = [];

    if (dessertCollection) {
        desserts =
            dessertCollection.data.map(({ id, attributes }) => {
                const restaurant =
                    attributes?.restaurant?.reduce((acc, currentValue) => {
                        const restaurantId = currentValue?.restaurant?.data?.id ?? "-1";

                        return currentValue && currentValue.available
                            ? [...acc, { price: currentValue.price, restaurantId }]
                            : acc;
                    }, [] as PriceRestaurant[]) ?? [];

                const dessert: Dessert = {
                    id: id ?? "",
                    name: attributes?.name ?? "",
                    position: attributes?.position ?? 0,
                    restaurant,
                };

                return dessert;
            }) ?? [];
    }

    return desserts;
};

So i want to create a generic function but i have an error and i don't know why. Basically , i copied the previous function and I added the TData with the input for the previous functions and the TModel for the value of return

typescript error

CodePudding user response:

Here's how you fix the issue:

// A simplified example

type A = {
    data: string
}

type B = {
    data: number
}


function test1<TData extends A | B>(collection: TData) {
    // compiles
    console.log(collection.data) // type is 'number | string'
}

What you're trying to do is add a constraint to your generic type.

The extends keyword is how you add generic constraints (See https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-constraints).

TL:DR: Replace the = with extends.

CodePudding user response:

Firstly, replace TData = BeveragesQuery | DessertsQuery with TData extends BeveragesQuery | DessertsQuery. Using the equals sign you just give the default value to the parameter, but don't constrain it in any way, so one could call a function with convertSimpleModel<number, ...> for example and it would make no sense to access property .data on number.

Secondly, you don't need the TModel parameter, if you leave it, I could call convertSimpleModel<BeveragesQuery, Dessert> and the function would be supposed to convert beverages to desserts which doesn't make sense either. Or even worse something like convertSimpleModel<DessertsQuery, number & string>. Instead use conditional type BeveragesQuery extends TData ? Beverage : Dessert where you would put TModel otherwise. So

function convertSimpleModel<TData extends BeveragesQuery | DessertsQuery>(
  collection: TData
): BeveragesQuery extends TData ? Beverage[] : Dessert[] {...}

Notice, that this is still not perfect, for example I could call convertSimpleModel<BeveragesQuery | DessertsQuery>(...) which will definitely lead to weird results. Also you probably won't be able to type the array modelElements correctly without using type assertions. Maybe what you want is not generics, but function overloads

function convertSimpleModel(collection: BeveragesQuery): Beverage[]
function convertSimpleModel(collection: DessertsQuery): Dessert[]
function convertSimpleModel(collection: BeveragesQuery | DessertsQuery): (Beverage | Dessert)[] {
  const modelElements: (Beverage | Dessert)[] = []
  // ...
}
  • Related