Skip to content

Generic type constraint doesn't seem to propagate to conditional type #56045

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Bubblyworld opened this issue Oct 9, 2023 · 5 comments
Closed
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@Bubblyworld
Copy link

Bubblyworld commented Oct 9, 2023

πŸ”Ž Search Terms

"generic constraint conditional type"

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about conditional types and generics

⏯ Playground Link

https://www.typescriptlang.org/play?#code/C4TwDgpgBAkgzgeQEYCsIGNgB4AqA+KAXihyggA9gIA7AEzigHtUNgoB+EqALimogBuEAE4BuAFAAzAK7VMAS0bUok6rjKUa9Ji0x4AFMF44AlFADe4qFHRK4bcr3jI0mXAWLAJAXyA

πŸ’» Code

type IsObject<T> = T extends object ? T : never;
function fn<T extends object>(t: T) {
  const x: IsObject<T> = t; // error 2322
}

πŸ™ Actual behavior

  1. Type 'T' is not assignable to type 'IsObject'.
    Type 'object' is not assignable to type 'IsObject'. [2322]

πŸ™‚ Expected behavior

Since the type parameter T is constrained to extend object, I expected tsc to infer that T is a subtype of IsObject<T>.

I'm sure I'm misunderstanding how constraints or conditional types work, but on the face of it this is very surprising to me. In the real world version of this I'm trying to to construct generic queries with MikroORM, which does an IsObject<T> check recursively in its query DSL type.

Additional information about the issue

No response

@MartinJohns
Copy link
Contributor

MartinJohns commented Oct 9, 2023

Sounds like a duplicate of #51523.

I'm sure I'm misunderstanding how constraints or conditional types work

Simple answer: constraints have no effect on the resolving of conditional types.

But I believe this open PR would change this: #56004

@Bubblyworld
Copy link
Author

Thank you, I see, that does look like a duplicate. Awesome to hear that there's a PR in the works! πŸ™

@whzx5byb
Copy link

whzx5byb commented Oct 9, 2023

But I believe this open PR would change this: #56004

I don't think so. Here is the playground for the PR and I don't see any change with OP's code.

@RyanCavanaugh
Copy link
Member

The only time this assignment is actually safe is when you could have written the target as an intersection instead:

type IsObject<T> = T & object;

which works as expected and is generally clearer all around.

The problem of resolving to the truthy branch of a conditional type based on the constraint is that it then appears that a constraint not matching the extends check should go to the falsy branch, but this isn't actually true. Experimentally, this also ends up creating circularities that didn't exist before since we do more assignability checks than before.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Oct 10, 2023
@Bubblyworld
Copy link
Author

Thanks for the tip - I can work around my original issue by patching MikroORM's IsObject<T> to use an intersection instead, so that's great πŸ‘ .

For the more general issue of resolving conditionals based on constraints...

type Cond<T> = T extends A ? Truthy : Falsy;
function fn<T extends B>(t: T) {
  const x: Cond<T> = t;
}

...if I understand right, you're saying that B extends A would imply the Truthy branch, but B not extends A actually doesn't tell you anything. Because in that case A and B may have non-trivial overlap, and you can't resolve the conditional either way.

But in that case, is something like this possible? Test if constraint B is assignable to the conditional check A: if it is, resolve to Truthy, otherwise leave it unresolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

4 participants