0

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

1
  • 1
    Please edit your question to replace image with actual code and error message. Commented Jan 2, 2022 at 20:50

2 Answers 2

1

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)[] = []
  // ...
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for your answer. I'm a little confused because with the instruction TData = BeveragesQuery | DessertsQuery i thought the TData values can be one type or another, constraint the type. I thought extend was used for interfaces, not for constraint the types And yep, for my use case I think is better overload the function signature
No, TData = doesn't constrain types, it only provides the default value for the cases when typescript cannot infer anything better. extends is used to constrain it. Please read the documentation of generics
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.