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

Possible regression of intersection type inference in 5.7.2 compared to 5.6.3 #60864

Open
spion opened this issue Dec 27, 2024 · 2 comments Β· May be fixed by #60876
Open

Possible regression of intersection type inference in 5.7.2 compared to 5.6.3 #60864

spion opened this issue Dec 27, 2024 · 2 comments Β· May be fixed by #60876
Labels
Help Wanted You can do this Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases
Milestone

Comments

@spion
Copy link

spion commented Dec 27, 2024

πŸ”Ž Search Terms

inference generic types

πŸ•— Version & Regression Information

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.7.2#code/MYGwhgzhAEBqYCcCWYBGICmAeAqgGmgGVoMAPAFwwDsATGCc5KgcwD5oBvAKGmmAHsqDBAFdg5fggAUABxHokwaBABcRAJSdoAXx7QRAQjU4A3F11cAZiKrikg6AFsA1gHFqGZMFwFiZSrT0jEgsrFKqGpx6CBjkIghU0FQYAO5wiCjo2PhEYRDq5lxc5ACeMhjQAKKkYOIAggjMAHJgjhgQWO7JXgAqZRgEAMKCwmAh5OwAvNBdnop95dAAZFG8ANoA0tAh0M4YJfyWMx69-QC6alv+1HS7+4fQw0KMY1Tk0AD8x93z-Ztn0DUyQAbp5CsV+tA6iAQPwUvBkGhMB0elN0oislgegQwFQSuwAD5aTbbRJ7A5HHoXdGZTBY-44vGE6A9f46IqlRY9drkBrMGDTbi8CD8Np8oEiRyoMEWTkVbkMPkQADqSHIAAsEQKoTC4Qjae0sTylawitZbOR7IlyGA9hBZl4sLASBQbjBobD4RkkYaFbzGhBWGEwGpqrV-c1WobYAQ-Sb1GpgfwkDQohZMO9gdBpja7Q7FFIOCKxY01C588ApAAiUhV9TadRmIA

πŸ’» Code

class Variable<U, S extends string> {
  constructor(public s: S) { }
  u!: U;
}

function mkGeneric<U, S extends string>(s: S) {
  return new Variable<U, S>(s)
}

type ExactArgNames<GenericType, Constraint> = GenericType & {
  [K in keyof GenericType]: K extends keyof Constraint ? GenericType[K] : never
}

type AllowVariables<T> = Variable<T, any> | { [K in keyof T]: Variable<T[K], any> | T[K] }

type TestArgs = {
  someArg: number
}

type TestArgsWithVars = AllowVariables<TestArgs>

function takesGeneric<V extends AllowVariables<TestArgs>>(a: ExactArgNames<V, TestArgs>): void {
}

let v = takesGeneric({someArg: mkGeneric("x")});

πŸ™ Actual behavior

VVersion 5.7.2 of the compiler infers the variable type as Variable<uknown, "x"> and subsequently generates an error

πŸ™‚ Expected behavior

VVersion 5.6.3 of the compiler infers the variable type correctly as Variable<number, "x">

Additional information about the issue

This is breaking most code that uses typed-graphql-builder typed-graphql-builder/typed-graphql-builder#85

@Andarist
Copy link
Contributor

I opened a PR with an experiment that could address this: #60876 but, like I mentioned there, I'm not really that confident in the chosen solution.

Your use case is not quite what TS usually allows. Exact types are not a thing, and you are trying to emulate them here. I feel like this is usually "there might be dragons" territory ;p

In general, nested inferences are quite tricky on their own. Just take a look at this comment from the source code:

        // When a call to a generic function is an argument to an outer call to a generic function for which
        // inference is in process, we have a choice to make. If the inner call relies on inferences made from
        // its contextual type to its return type, deferring the inner call processing allows the best possible
        // contextual type to accumulate. But if the outer call relies on inferences made from the return type of
        // the inner call, the inner call should be processed early. There's no sure way to know which choice is
        // right (only a full unification algorithm can determine that), so we resort to the following heuristic:
        // If no type arguments are specified in the inner call and at least one call signature is generic and
        // returns a function type, we choose to defer processing. This narrowly permits function composition
        // operators to flow inferences through return types, but otherwise processes calls right away. We
        // use the resolvingSignature singleton to indicate that we deferred processing. This result will be
        // propagated out and eventually turned into silentNeverType (a type that is assignable to anything and
        // from which we never make inferences).

I hit this myself many times already. In some way, you just lucked out before on some of the contextual types being accidentally ignored and that's why the return type inference was able to flow into your inner inference when the outer one was still in progress.

That being said... this alternative to your types work: TS playground

@spion
Copy link
Author

spion commented Jan 1, 2025

Thanks. I did work around this by using a dummy conditional type but using Exclude for filtering keys seems cleaner.

@RyanCavanaugh RyanCavanaugh added Help Wanted You can do this Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases labels Jan 2, 2025
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Jan 2, 2025
@spion spion changed the title Possible regression in 5.7.2 compared to 5.6.3 Possible regression of intersection types in 5.7.2 compared to 5.6.3 Jan 4, 2025
@spion spion changed the title Possible regression of intersection types in 5.7.2 compared to 5.6.3 Possible regression of intersection type inference in 5.7.2 compared to 5.6.3 Jan 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Help Wanted You can do this Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants