Description
π Search Terms
extends does not satisfy the constraint in Omit
π Version & Regression Information
This is the behavior in every version I tried, and I reviewed the FAQ for entries about generic type parameter constraints.
β― Playground Link
π» Code
Creating a nested type with a generic type parameter that extends some type can, in some cases, throw an error, whereas using the type directly does not, in some ways that feels like unexpected behavior.
The following shows two examples, one without a generic type parameter which does not error, and one with a generic type parameter which does error:
type Test1 = { a: number, b: number };
type Problem<Base, Key extends keyof Base> = Omit<Base, Key>;
type Ex1 = Problem<Problem<Test1, "a">, "b"> // No Error
type Ex2<Test2 extends Test1> = Problem<Problem<Test2, "a">, "b"> // Error
// ^^^ Error occurs here
The specific error message is Type 'string' does not satisfy the constraint 'Exclude<keyof Test2, "a">'
. This made me wonder if the issue was that the type argument ("b"
) was being interpreted as a general string
instead of the specific type "b"
, but some more poking around shows that's not the issue:
type Problem<Base, Key extends keyof Base> = Omit<Base, Key> & { [K in Key]: any };
type Ex2<Test2 extends Test1> = Problem<Problem<Test2, "a">, "b"> // Error
// ^^^ Error occurs here
For some reason, adding the extra & { [K in Key]: any }
bit changes the error message to now specifically say Type '"b"' does not satisfy the constraint '"a" | Exclude<keyof Test2, "a">'.
(which may be its own issue because I don't really understand how adding that changes the error message in that way). Regardless, it's my understanding that "b"
should satisfy that constraint, since Test2
extends Test1
and therefore must have a key called b
.
Weirdly, changing the value of Problem
from Omit<Base, Key>
to just Base
removes the error:
type Test1 = { a: number, b: number };
type Problem<Base, Key extends keyof Base> = Base;
type Ex1 = Problem<Problem<Test1, "a">, "b"> // No Error
type Ex2<Test2 extends Test1> = Problem<Problem<Test2, "a">, "b"> // No Error
...which suggests to me that even if this is expected behavior, it's still a misplaced error; If the error occurs while trying to satisfy the signature of Omit
as opposed to the signature of Problem
, then that means there are some types that can be passed as valid arguments to Problem
that are not necessarily valid to Omit
. Therefore, the type definition for Problem
itself should throw an error that its generic type parameters aren't constrained strictly enough to satisfy it's value, in the same way that:
type Problem<Base, Key> = Omit<Base, Key>
...throws an error.
Unsatisfying Fix
One solution is to replace the signature for Problem
with
type Problem<Base, Key extends keyof any> = Omit<Base, Key>;
Which now functions as expected, and neither of the two lines error:
type Ex1 = Problem<Problem<Test1, "a">, "b"> // No Error
type Ex2<Test2 extends Test1> = Problem<Problem<Test2, "a">, "b"> // No Error
However, this comes with the drawback that the second parameter isn't enforced to be a key of the first, i.e., the following throws no error:
type Problem<Base, Key extends keyof any> = Omit<Base, Key>;
type Ex1 = Problem<Test1, "invalid key"> // No Error (this is a bad thing)
This is not desirable; It should be enforceable that Key
is a keyof Base
.
π Actual behavior
Using the type Problem
with a generic type parameter passed down gives an error.
π Expected behavior
I think this is unexpected behavior, and that this shouldn't cause an error. I can't think of a type argument that can be passed to Test2
that doesn't satisfy the constraints needed.
If this is somehow expected behavior, then I think the error is in the wrong place. There's no reason why changing the value of the type Problem
without changing its signature should add/remove errors in uses of the type, when the type definition itself has no errors in either case.
Additional information about the issue
No response