Skip to content
New issue

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

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

Already on GitHub? # to your account

Improve inference between types with multiple signatures #54448

Merged
merged 4 commits into from
Aug 23, 2023
Merged

Conversation

ahejlsberg
Copy link
Member

Implements what I describe here.

Fixes #28867.

@typescript-bot typescript-bot added Author: Team For Milestone Bug PRs that fix a bug with a specific milestone labels May 30, 2023
@ahejlsberg
Copy link
Member Author

@typescript-bot test this
@typescript-bot user test this inline
@typescript-bot run dt
@typescript-bot perf test faster
@typescript-bot test top100

@typescript-bot
Copy link
Collaborator

typescript-bot commented May 30, 2023

Heya @ahejlsberg, I've started to run the diff-based user code test suite on this PR at 4027c38. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented May 30, 2023

Heya @ahejlsberg, I've started to run the abridged perf test suite on this PR at 4027c38. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented May 30, 2023

Heya @ahejlsberg, I've started to run the parallelized Definitely Typed test suite on this PR at 4027c38. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented May 30, 2023

Heya @ahejlsberg, I've started to run the diff-based top-repos suite on this PR at 4027c38. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented May 30, 2023

Heya @ahejlsberg, I've started to run the extended test suite on this PR at 4027c38. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

@ahejlsberg
The results of the perf run you requested are in!

Here they are:

Comparison Report - main..54448

Metric main 54448 Delta Best Worst p-value
Angular - node (v16.17.1, x64)
Memory used 365,216k (± 0.01%) 365,235k (± 0.02%) ~ 365,154k 365,313k p=0.575 n=6
Parse Time 3.56s (± 0.31%) 3.55s (± 0.33%) ~ 3.53s 3.56s p=0.170 n=6
Bind Time 1.18s (± 0.35%) 1.18s (± 0.69%) ~ 1.17s 1.19s p=0.584 n=6
Check Time 9.56s (± 0.61%) 9.55s (± 0.18%) ~ 9.52s 9.57s p=0.936 n=6
Emit Time 7.88s (± 0.44%) 7.94s (± 1.13%) ~ 7.85s 8.10s p=0.126 n=6
Total Time 22.18s (± 0.41%) 22.22s (± 0.36%) ~ 22.17s 22.38s p=0.630 n=6
Compiler-Unions - node (v16.17.1, x64)
Memory used 192,918k (± 0.04%) 192,922k (± 0.03%) ~ 192,837k 193,015k p=0.936 n=6
Parse Time 1.60s (± 0.92%) 1.58s (± 1.92%) ~ 1.54s 1.62s p=0.226 n=6
Bind Time 0.83s (± 0.76%) 0.82s (± 0.66%) ~ 0.82s 0.83s p=0.201 n=6
Check Time 10.24s (± 0.51%) 10.12s (± 0.62%) -0.12s (- 1.16%) 10.02s 10.18s p=0.013 n=6
Emit Time 3.02s (± 1.21%) 3.00s (± 1.20%) ~ 2.93s 3.03s p=0.570 n=6
Total Time 15.69s (± 0.41%) 15.53s (± 0.49%) -0.16s (- 1.03%) 15.40s 15.61s p=0.013 n=6
Monaco - node (v16.17.1, x64)
Memory used 345,885k (± 0.00%) 345,885k (± 0.00%) ~ 345,874k 345,905k p=0.936 n=6
Parse Time 2.74s (± 0.87%) 2.73s (± 0.55%) ~ 2.72s 2.76s p=0.512 n=6
Bind Time 1.09s (± 0.69%) 1.08s (± 0.38%) ~ 1.08s 1.09s p=0.100 n=6
Check Time 7.86s (± 0.33%) 7.83s (± 0.60%) ~ 7.75s 7.87s p=0.198 n=6
Emit Time 4.46s (± 0.64%) 4.46s (± 0.39%) ~ 4.45s 4.49s p=1.000 n=6
Total Time 16.15s (± 0.31%) 16.10s (± 0.32%) ~ 16.04s 16.15s p=0.124 n=6
TFS - node (v16.17.1, x64)
Memory used 299,953k (± 0.01%) 299,950k (± 0.01%) ~ 299,930k 299,980k p=0.810 n=6
Parse Time 2.17s (± 0.38%) 2.17s (± 0.68%) ~ 2.15s 2.19s p=0.508 n=6
Bind Time 1.24s (± 0.94%) 1.24s (± 0.79%) ~ 1.23s 1.25s p=0.673 n=6
Check Time 7.27s (± 0.30%) 7.24s (± 0.36%) ~ 7.22s 7.28s p=0.147 n=6
Emit Time 4.38s (± 1.08%) 4.34s (± 0.80%) ~ 4.30s 4.40s p=0.228 n=6
Total Time 15.06s (± 0.28%) 14.99s (± 0.32%) -0.07s (- 0.46%) 14.94s 15.07s p=0.029 n=6
material-ui - node (v16.17.1, x64)
Memory used 480,988k (± 0.01%) 481,024k (± 0.01%) +37k (+ 0.01%) 480,994k 481,081k p=0.031 n=6
Parse Time 3.25s (± 0.41%) 3.25s (± 0.32%) ~ 3.23s 3.26s p=1.000 n=6
Bind Time 0.94s (± 0.43%) 0.94s (± 0.95%) ~ 0.93s 0.95s p=0.787 n=6
Check Time 17.96s (± 0.54%) 17.68s (± 0.52%) -0.28s (- 1.58%) 17.60s 17.85s p=0.006 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 22.15s (± 0.45%) 21.87s (± 0.39%) -0.28s (- 1.29%) 21.80s 22.03s p=0.005 n=6
xstate - node (v16.17.1, x64)
Memory used 560,586k (± 0.01%) 560,643k (± 0.02%) ~ 560,508k 560,737k p=0.298 n=6
Parse Time 4.02s (± 0.47%) 3.99s (± 0.37%) -0.03s (- 0.70%) 3.97s 4.01s p=0.024 n=6
Bind Time 1.77s (± 0.77%) 1.76s (± 0.43%) -0.02s (- 0.85%) 1.75s 1.77s p=0.027 n=6
Check Time 3.06s (± 0.74%) 3.05s (± 0.78%) ~ 3.02s 3.09s p=0.332 n=6
Emit Time 0.09s (± 4.45%) 0.09s (± 0.00%) ~ 0.09s 0.09s p=0.405 n=6
Total Time 8.94s (± 0.21%) 8.89s (± 0.22%) -0.06s (- 0.63%) 8.86s 8.92s p=0.008 n=6
System
Machine Namets-ci-ubuntu
Platformlinux 5.4.0-148-generic
Architecturex64
Available Memory16 GB
Available Memory15 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v16.17.1, x64)
Scenarios
  • Angular - node (v16.17.1, x64)
  • Compiler-Unions - node (v16.17.1, x64)
  • Monaco - node (v16.17.1, x64)
  • TFS - node (v16.17.1, x64)
  • material-ui - node (v16.17.1, x64)
  • xstate - node (v16.17.1, x64)
Benchmark Name Iterations
Current 54448 6
Baseline main 6

Developer Information:

Download Benchmark

@typescript-bot
Copy link
Collaborator

Heya @ahejlsberg, I've run the RWC suite on this PR - assuming you're on the TS core team, you can view the resulting diff here.

@typescript-bot
Copy link
Collaborator

@ahejlsberg Here are the results of running the user test suite comparing main and refs/pull/54448/merge:

There were infrastructure failures potentially unrelated to your change:

  • 1 instance of "Unknown failure"
  • 1 instance of "Package install failed"

Otherwise...

Everything looks good!

@typescript-bot
Copy link
Collaborator

@ahejlsberg Here are the results of running the top-repos suite comparing main and refs/pull/54448/merge:

Everything looks good!

@typescript-bot
Copy link
Collaborator

Hey @ahejlsberg, the results of running the DT tests are ready.
There were interesting changes:

Branch only errors:

Package: jquery
Error:

Error: /home/vsts/work/1/s/DefinitelyTyped-tools/packages/dtslint-runner/DefinitelyTyped/types/jquery/jquery-tests.ts:7169:21
ERROR: 7169:21  expect  TypeScript@local expected type to be:
  never
got:
  any
ERROR: 7297:21  expect  TypeScript@local expected type to be:
  J3
got:
  any
ERROR: 7320:21  expect  TypeScript@local expected type to be:
  J2
got:
  any
ERROR: 7346:13  expect  TypeScript@local expected type to be:
  PromiseBase<J1, never, never, never, never, never, never, never, never, never, never, never>
got:
  PromiseBase<J1, any, never, never, never, never, never, never, never, never, never, never>
ERROR: 7347:19  expect  TypeScript@local compile error: 
Type 'PromiseBase<J1, any, never, never, never, never, never, never, never, never, never, never>' is not assignable to type 'Promise3<J1, never, never, never, never, never, never, never, never>'.
  Types of property 'pipe' are incompatible.
    Type '{ <ARD = never, AJD = never, AND = never, BRD = never, BJD = never, BND = never, CRD = never, CJD = never, CND = never, RRD = never, RJD = never, RND = never, ARF = never, AJF = never, ANF = never, BRF = never, BJF = never, BNF = never, CRF = never, CJF = never, CNF = never, RRF = never, RJF = never, RNF = never, AR...' is not assignable to type '{ <ARD = never, AJD = never, AND = never, BRD = never, BJD = never, BND = never, CRD = never, CJD = never, CND = never, RRD = never, RJD = never, RND = never, ARF = never, AJF = never, ANF = never, BRF = never, BJF = never, BNF = never, CRF = never, CJF = never, CNF = never, RRF = never, RJF = never, RNF = never, AR...'. Two different types with this name exist, but they are unrelated.
      Types of parameters 'failFilter' and 'failFilter' are incompatible.
        Types of parameters 't' and 't' are incompatible.
          Type 'any' is not assignable to type 'never'.

    at testTypesVersion (/home/vsts/work/1/s/DefinitelyTyped-tools/packages/dtslint-runner/node_modules/@definitelytyped/dtslint/dist/index.js:194:15)
    at async runTests (/home/vsts/work/1/s/DefinitelyTyped-tools/packages/dtslint-runner/node_modules/@definitelytyped/dtslint/dist/index.js:151:9)

You can check the log here.

@Andarist
Copy link
Contributor

@ahejlsberg This has an effect on the code reported in #53508

type Overloads<T> = T extends {
    (...args: infer A1): infer R1;
    (...args: infer A2): infer R2;
  } 
  ? [(...args: A1) => R1, (...args: A2) => R2]
  : T extends {
      (...args: infer A1): infer R1;
    } 
    ? [(...args: A1) => R1]
    : any;

interface A {
  hello(str: string): string
}

// was: [(str: string) => string]
// is:  [(str: string) => string, (str: string) => string]
type HelloOverloadsOfA = Overloads<A['hello']>;

interface B {
  hello(): number
  hello(str: string): string
}

// was: [() => number, (str: string) => string]
// is:  [() => number, (str: string) => string]
type HelloOverloadsOfB = Overloads<B['hello']>;

interface C {
  hello(): number
}

// was:             [(...args: unknown[]) => unknown, () => number]
// is:              [() => number, () => number]
// requested by OP: [() => number]
type HelloOverloadsOfC = Overloads<C['hello']>;

@ahejlsberg
Copy link
Member Author

This has an effect on the code reported in #53508

The current state of affairs of inference between types with multiple signatures is confusing to say the least. When the source has fewer signatures than the target, we make no inferences to the excess target signatures, leaving type variables in those signatures to assume their defaults (typically unknown). In those cases, inference may produce target types with which the source type isn't compatible (because unknown in a parameter position is very restrictive). The net effect is that you sometimes can measure overload arity, so the strategy in #53508 of having a ladder with decreasing numbers of overloads is never going to produce consistent results.

With the changes in this PR, our behavior is much more consistent: We'll always make inferences to each signature in the target, ensuring that every type variable in the target is covered. It doesn't give you the ability to measure overload arity with a ladder (because the first step will always succeed), but that ability wasn't really there to begin with.

@Andarist
Copy link
Contributor

Just a note of clarification - I intentionally said that this has an effect on that code and not that it introduces a regression. After analyzing this a little bit better, I think this is a fine change.

Just wanted to double-check by mentioning that issue since I distinctly remember that it existed and that it related to this PR. Maybe worth adding HelloOverloadsOfA and HelloOverloadsOfC cases to tests since there are no tests like them in this PR and it changes behavior for them.

Copy link
Member

@RyanCavanaugh RyanCavanaugh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Seems like our test coverage in this area is not great...

@DanielRosenwasser
Copy link
Member

Let's wait until Monday for TS 5.3.

@RyanCavanaugh
Copy link
Member

Pushing a merge commit

Copy link
Member

@weswigham weswigham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is fine if the extended suites are OK with it - no worse than the existing inference logic, anyway.

What gets me is I know, since this logic differs from the logic in signaturesRelatedTo (which has way more special cases), we can infer things from a different order of signature pairs (with differing levels of instantiation) than we end up relating, so we can ultimately choose an inference that ends up failing relationship checks (which makes it a poor inference candidate). Again, already possible, so this doesn't change anything in that regard, just makes the behavioral drift between relating types and inferring between signatures in types a bit bigger.

@ahejlsberg ahejlsberg merged commit 6d07d5f into main Aug 23, 2023
@ahejlsberg ahejlsberg deleted the fix28867 branch August 23, 2023 18:14
sandersn added a commit to DefinitelyTyped/DefinitelyTyped that referenced this pull request Sep 14, 2023
sandersn added a commit to DefinitelyTyped/DefinitelyTyped that referenced this pull request Sep 15, 2023
snovader pushed a commit to EG-A-S/TypeScript that referenced this pull request Sep 23, 2023
…4448)

Co-authored-by: Ryan Cavanaugh <RyanCavanaugh@users.noreply.github.com>
Co-authored-by: TypeScript Bot <typescriptbot@microsoft.com>
amikheychik added a commit to perfective/ts.common that referenced this pull request Nov 24, 2023
Fix TypeScript 5.3 compatibility.

@perfective/common build fails since the TypeScript v5.3.0-dev.20230824. Type inference for the `nothing()` and `nil()` function has changed (probably caused by microsoft/TypeScript#54448).

The code like:

```
const maybe: Maybe<number> = nothing();
```

started to fail, as the `nothing()` return type is inferenced as `number | null | undefined` instead of just `number`.

One solution is to explicitly set the type:

```
const maybe: Maybe<number> = nothing<number>();
```

This solution may require significant amount of code updates across users' codebase.

The applied alternative solution was to change the `nothing()` and `nil()` return types to `Nothing<Present<T>>`.

In this case, the calls of `nothing()`/`nil()` remain as is, but `Maybe` and `Match` had to use additional type casts internally.

This approach is chosen for the v0.10.0, so the code is compatible with TypeScript v5.3. It may be reconsidered in v0.11.0 if the root cause of the different type inference will be located.
amikheychik added a commit to perfective/ts.common that referenced this pull request Nov 24, 2023
Fix TypeScript 5.3 compatibility.

@perfective/common build fails since the TypeScript v5.3.0-dev.20230824. Type inference for the `nothing()` and `nil()` function has changed (probably caused by microsoft/TypeScript#54448).

The code like:

```
const maybe: Maybe<number> = nothing();
```

started to fail, as the `nothing()` return type is inferenced as `number | null | undefined` instead of just `number`.

One solution is to explicitly set the type:

```
const maybe: Maybe<number> = nothing<number>();
```

This solution may require significant amount of code updates across users' codebase.

The applied alternative solution was to change the `nothing()` and `nil()` return types to `Nothing<Present<T>>`.

In this case, the calls of `nothing()`/`nil()` remain as is, but `Maybe` and `Match` had to use additional type casts internally.

This approach is chosen for the v0.10.0, so the code is compatible with TypeScript v5.3. It may be reconsidered in v0.11.0 if the root cause of the different type inference will be located.
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Author: Team For Milestone Bug PRs that fix a bug with a specific milestone
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Inconsistent inferred overloads in conditional types for no-arg signatures with strictFunctionTypes
6 participants