I have a method that has 2 arguments and I want it to infer a type from the 1st argument.
For example, in the following code, I want the type T
of the function create_C<T>
to be inferred from the firstArgument
so that the return type of create_C
function would be C<type inferred from firstArgument>
interface C<T> {
firstArgument: A<T>;
secondArgument: (obj: any) => T
}
export interface A<T> {
type: T;
}
function create_C<T>(
firstArgument: A<T>,
secondArgument: (obj: any) => T
): C<T> {
return {
firstArgument,
secondArgument
}
}
However, in the following implementation, the type of const c
is being inferred as C<{ prop2: number }>
. But I am expecting it to be inferred as C<B>
and I am expecting the compiler to throw an error saying that the return type of the secondArgument
is not of type B
interface B {
prop1: string;
prop2: number
}
export class B_Component implements A<B> {
type: B = {
prop1: "",
prop2: 1
};
}
const c = create_C(
new B_Component(),
() => ({ prop2: 2 })
)
How can I make sure for the compiler to throw an error saying that the return type of the secondArgument
is not of type B
?
Here is a Stackblitz editor link: https://stackblitz.com/edit/qqddsn
Specifying Type Arguments TypeScript can usually infer the intended type arguments in a generic call, but not always. For example, let’s say you wrote a function to combine two arrays: function combine < Type > (arr1: Type [], arr2: Type []): Type [] {
In TypeScript, generics are used when we want to describe a correspondence between two values. We do this by declaring a type parameter in the function signature: By adding a type parameter Type to this function and using it in two places, we’ve created a link between the input of the function (the array) and the output (the return value).
The never type represents values which are never observed. In a return type, this means that the function throws an exception or terminates execution of the program. never also appears when TypeScript determines there’s nothing left in a union. x; // has type 'never'!
They’re also values, and just like other values, TypeScript has many ways to describe how functions can be called. Let’s learn about how to write types that describe functions. The simplest way to describe a function is with a function type expression . These types are syntactically similar to arrow functions: console. log ( s );
In your function signature
declare function create_C<T>(a1: A<T>, a2: (obj: any) => T): C<T>;
there are two inference sites for T
(an "inference site" means "someplace the compiler can use to try to infer a type for a type parameter"). One site is from the type
property of the first argument a1
, and the other site is the return type of the second argument a2
. The compiler looks at a call like
create_C(new B_Component(), () => ({ prop2: 2 });
and tries to infer T
from both sites. In this case, there is a match: both (new B_Component()).type
and {prop2: 2}
are assignable to {prop2: number}
. So there's no error, and you get C<{prop2: number>
coming out. In another situation, this might be exactly the behavior you want from the compiler.
Instead, you want to see the compiler use just a1
to infer T
, and to just verify that a2
matches it. That is, you want the T
in (obj: any) => T
to be a non-inferential type parameter (see microsoft/TypeScript#14829). Unfortunately, there is no "official" support for this. But fortunately, there are workaround techniques which can often be used to get this behavior.
Here's one such technique: if you change a type parameter in an inference site from T
to T & {}
, it lowers the site's priority. So the compiler will tend to infer T
from other inference sites first and only come back to the T & {}
one if it fails to infer from other places. And the type T & {}
is very similar to T
(if T
is an object type then it's basically the same) so it doesn't change the semantics much. Let's try it:
declare function create_C_better<T>(a: A<T>, b: (obj: any) => T & {}): C<T>;
Here goes:
const c2 = create_C_better(
new B_Component(),
() => ({ prop2: 2 }) // error!
// ~~~~~~~~~~~~~~ <-- prop1 is missing
)
const c3 = create_C_better(
new B_Component(),
() => ({ prop1: "all right", prop2: 2 })
); // C<B>
There, you get the error you wanted when prop1
is missing, and when you fix it, you get an output of type C<B>
as desired.
Okay, hope that helps; good luck!
Link to code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With