Skip to content

T satisfies a constraint that E extends T does not (specifically with Omit)Β #61870

Open
@vi013t

Description

@vi013t

πŸ”Ž 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

https://www.typescriptlang.org/play/?#code/C4TwDgpgBAKhDOwCMUC8UDeUCGAuKAdgK4C2ARhAE4A0UZ+x5VUAvgNwBQHokUACpQD2ZADYQSAHgBC2eBFoBpCCCgQAHsAgEAJvCgBrZYIBmUGXIB8aKAHkSAS2DTZ8qEpAXO3cNACialCgg4JDQsPQBYTFJSNFxCThEJFoAImwUi1SyDKgAelyoADlBKF9KIUpvXn8AJgSEYBrVDS1dWAakKwihOJie6PrEGtT0zKgU7Kt80vLBSumwxaXlldXFgD1NmYqoQQBjPaJKPQALKgggA

πŸ’» 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions