Skip to content
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

Type parameter not usable as type argument in identical type; union in constraint becomes intersection #60892

Open
jcalz opened this issue Dec 31, 2024 · 1 comment
Labels
Bug A bug in TypeScript Help Wanted You can do this
Milestone

Comments

@jcalz
Copy link
Contributor

jcalz commented Dec 31, 2024

πŸ”Ž Search Terms

indexed access, constraints, generic, unions, ts2344

πŸ•— Version & Regression Information

⏯ Playground Link

Playground link

πŸ’» Code

interface Foo<T extends { x: string }, U extends (T | { x: "a" })['x']> {
    z: Foo<T, U> // error!
    //        ~ Type 'U' does not satisfy the constraint 'T["x"] & "a"'. πŸ™ƒ
}

πŸ™ Actual behavior

The U type parameter is rejected as a type argument with a TS2344 error about how it doesn't satisfy the constraint. The constraint seems to have shifted from a union to an intersection, even though it comes from an identical place.

πŸ™‚ Expected behavior

The U type parameter should be accepted as a type argument.

Additional information about the issue

Distilled from SO question.

I expect this is a consequence of #30769 as per #31731 (comment), but it is at least somewhat surprising that this should happen when the types involved are identical. One could rewrite the constraint to U extends T["x"] | "a", of course, but this is distilled from the above SO question which is presumably distilled from some use case. What's happening here, exactly, and is it intended, a design limitation, or a bona fide bug?

@Andarist
Copy link
Contributor

FWIW, both come from distributeIndexOverObjectType - the union/intersection output is determined based on the writing option. When doing the relationship check the compiler checks if U is assignable to the constraint of the target type parameter - that's (T | { x: "a"; })["x"]. So far so good. But then it's normalized/simplified close to the top of isRelatedTo (using transitively mentioned distributeIndexOverObjectType) and the output of that is an intersection. At this stage, the source itself is still U - but later on its constraint goes through the same normalization... this time it becomes a union. But at this stage, the target is already heavily disassociated from its origin anyway. I suspect that if this indexed access on the target side would stay deferred then it would just work but implementing that might prove tricky.

@RyanCavanaugh RyanCavanaugh added Bug A bug in TypeScript Help Wanted You can do this labels Jan 2, 2025
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Jan 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Help Wanted You can do this
Projects
None yet
Development

No branches or pull requests

3 participants