Consider the following code:
interface myInterface {}
type myInterfaceCreatorFunction<TMyInterface extends myInterface> = (...args: any) => TMyInterface;
declare function test<
TMyInterfaceCreator extends myInterfaceCreatorFunction<any> = myInterfaceCreatorFunction<any>,
TMyInterface extends (TMyInterfaceCreator extends myInterfaceCreatorFunction<infer U> ? U : myInterface) = myInterface /** Why is this giving me the error here? */
>(creator: TMyInterfaceCreator): TMyInterface;
As the comment states, the second generic parameters default, gives an error:
Type 'myInterface' does not satisfy the constraint 'TMyInterfaceCreator extends myInterfaceCreatorFunction ? U : myInterface'.(2344)
Based on the signature of myInterfaceCreator
type, this should work in my opinion, but regardless, this error is not helping me in any way. Why can't I set the default value of TMyInterface
to myInterface
type?
If I omit the two default values from the function definition, the function does what I expect it to do, namely, infer the myInterface
type from the input function, and set the return type accordingly, but I need to be able to set default values to these generics, as I will need other generics in this function too, which I want to define.
Playground link here
CodePudding user response:
When you have a type parameter declaration of the form
type Foo<X extends Y = Z> = void;
the type parameter default Z
must definitely meet the type parameter constraint Y
. If it is possible that Z extends Y
is not true, then the compiler will complain, because then when the type parameter falls back to the default, it might fail to meet the required constraint.
In your case,
interface MyIFace { }
type MyFunc<T extends MyIFace> = (...args: any) => T;
declare function test<
C extends MyFunc<any> = MyFunc<any>,
I extends (C extends MyFunc<infer U> ? U : MyIFace) = MyIFace // error!
>(creator: C): I;
the constraint for the I
type parameter depends on C
. We can chose C
to be such that MyIFace
is not assignable to this constraint. For example:
// in the following call
test<() => { a: number }>(() => ({ a: 1 }))
// C is ()=>{a: number} and I is MyIFace
// but (C extends MyFunc<infer U> ? U : MyIFace) evaluates to {a: number}
// and therefore I extends {a: number} is violated.
Here we have called test()
with a C
type parameter of () => {a: number}
. Therefore the constraint (C extends MyFunc<infer U> ? U : MyIFace)
evaluates to {a: number}
. And MyIFace
cannot be assigned to {a: number}
, as it is an empty interface and does not contain a known a
property of type number
.
So that's the problem; we can choose C
so that I
is constrained to almost any particular type we want with as many specific properties as we want, and MyIFace
is unlikely to have any of these specific properties, so the default is not valid.