'How does Typescript infer Generics and why does it not work in one case?
Why is there a Typescript error with f2(f1)? For me it seems that f2, f3 and f4 have the same types. It's just declared different. Where is the difference of f4 to f2 or f3?
I've got the following example:
Code:
interface FullType {
id: string;
order_no: number;
}
interface OrderType {
[key: string]: any;
order_no: number;
}
const f1 = (d1: FullType): Promise<FullType> => {
return new Promise(() => {
return d1;
});
};
function f2<Type extends OrderType, Fn extends (t: Type) => Promise<Type>>(
f: Fn,
): void {
//
}
function f3<Type extends OrderType>(f: (t: Type) => Promise<Type>): void {
//
}
function f4<Type1 extends Type2, Type2 extends OrderType>(
f: (item: Type1) => Promise<Type1>,
): void {
//
}
f2(f1); // why is here a warning
f3(f1);
f4(f1);
Solution 1:[1]
In TypeScript, generics usually work just fine, however when you start using already extended generic types to define new generic types that will also be extended - this is where the trouble occurs. In general, Generic Constraints (e.g. <T>) in TypeScript are not considered as types that can be inferred further, meaning that whilst extending an extended type like you have in f4 is harmless, extending a newly defined type that also uses generics is problematic:
f4 is perfectly fine:
<Type1 extends Type2, Type2 extends OrderType>
f3 is also fine as it is not extending the type to define the callback but is instead creating directly:
f: (t: Type) => Promise<Type>
f2 is where the problem begins to occur as the parameter type is using an extended generic type that also uses extended values. This causes the type to default itself back to OrderType and because FullType does not directly equal OrderType you are observing that error:
Fn extends (t: Type) => Promise<Type>
This can be further illustrated by defining this in a separate type:
type CustomCallback<T> = (t: T) => Promise<T>;
// This will work just fine
function f2<Type extends OrderType>(f: CustomCallback<Type>): void {
//
}
// this will not
function f2<Type extends OrderType, paramType extends CustomCallback<Type>>(f: paramType): void {
//
}
Here is the Playground link.
I must additionally mention that the keyword extends extracts all of the known properties from a type and allows other values to be used due to the index signature [key: string]: any; property defined in OrderType, hence this is the reason why no errors are being displayed for f3 and f4. However if you used a direct type in any one of them you will start to see a warning because FullType does not equal OrderType.
function f3(f: (t: OrderType) => Promise<OrderType>): void {
//
}
f3(f1); // warning
To sum it all up, as a general rule, try to avoid extending generic types that are already using extended variables.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
