Skip to content

[experiment] Don't relate primitives to their wrappers #52947

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
wants to merge 3 commits into from

Conversation

jakebailey
Copy link
Member

@jakebailey jakebailey commented Feb 24, 2023

Basically, I was trying to understand why we allow const x: Number = 1 when the latter is a wrapper object. e.g. 1 === new Number(1) will yield false and typeof new Number(1) === "object", not number.

@DanielRosenwasser said this might be an interesting experiment. Might as well try.

@typescript-bot typescript-bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Feb 24, 2023
@jakebailey
Copy link
Member Author

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

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 24, 2023

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 24, 2023

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

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 24, 2023

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

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 24, 2023

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 24, 2023

Heya @jakebailey, I've started to run the tarball bundle task on this PR at 5211178. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 24, 2023

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

Update: The results are in!

@typescript-bot

This comment was marked as outdated.

@typescript-bot
Copy link
Collaborator

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

Something interesting changed - please have a look.

Details

fp-ts

tsconfig.json

@typescript-bot
Copy link
Collaborator

Heya @jakebailey, 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

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

Here they are:

Comparison Report - main..52947

Metric main 52947 Delta Best Worst p-value
Angular - node (v16.17.1, x64)
Memory used 359,035k (± 0.01%) 359,044k (± 0.00%) ~ 359,016k 359,056k p=0.521 n=6
Parse Time 3.73s (± 0.80%) 3.71s (± 0.46%) ~ 3.69s 3.73s p=0.252 n=6
Bind Time 1.19s (± 0.63%) 1.19s (± 0.43%) ~ 1.19s 1.20s p=0.784 n=6
Check Time 9.45s (± 0.64%) 9.43s (± 0.52%) ~ 9.35s 9.50s p=0.333 n=6
Emit Time 7.93s (± 0.62%) 8.08s (± 0.52%) +0.15s (+ 1.89%) 8.02s 8.13s p=0.006 n=6
Total Time 22.31s (± 0.52%) 22.41s (± 0.27%) ~ 22.30s 22.48s p=0.125 n=6
Compiler-Unions - node (v16.17.1, x64)
Memory used 191,374k (± 0.04%) 191,358k (± 0.02%) ~ 191,296k 191,424k p=0.630 n=6
Parse Time 1.59s (± 1.31%) 1.56s (± 0.66%) -0.03s (- 1.79%) 1.54s 1.57s p=0.018 n=6
Bind Time 0.83s (± 0.49%) 0.82s (± 0.77%) -0.01s (- 1.01%) 0.81s 0.83s p=0.033 n=6
Check Time 10.09s (± 0.27%) 9.98s (± 0.53%) -0.11s (- 1.12%) 9.92s 10.03s p=0.005 n=6
Emit Time 3.04s (± 0.77%) 3.01s (± 0.33%) -0.02s (- 0.82%) 3.00s 3.03s p=0.037 n=6
Total Time 15.54s (± 0.24%) 15.36s (± 0.47%) -0.17s (- 1.12%) 15.27s 15.44s p=0.005 n=6
Monaco - node (v16.17.1, x64)
Memory used 343,078k (± 0.00%) 343,126k (± 0.01%) +48k (+ 0.01%) 343,102k 343,169k p=0.005 n=6
Parse Time 2.81s (± 0.52%) 2.80s (± 0.69%) ~ 2.78s 2.83s p=0.372 n=6
Bind Time 1.09s (± 0.58%) 1.08s (± 0.38%) -0.01s (- 0.76%) 1.08s 1.09s p=0.033 n=6
Check Time 7.69s (± 0.67%) 7.67s (± 0.51%) ~ 7.63s 7.73s p=0.744 n=6
Emit Time 4.45s (± 1.41%) 4.44s (± 0.87%) ~ 4.39s 4.50s p=0.936 n=6
Total Time 16.04s (± 0.63%) 15.99s (± 0.37%) ~ 15.92s 16.08s p=0.466 n=6
TFS - node (v16.17.1, x64)
Memory used 299,217k (± 0.01%) 299,214k (± 0.01%) ~ 299,179k 299,241k p=1.000 n=6
Parse Time 2.20s (± 0.85%) 2.19s (± 0.71%) ~ 2.18s 2.22s p=0.451 n=6
Bind Time 1.24s (± 1.10%) 1.24s (± 1.10%) ~ 1.22s 1.25s p=1.000 n=6
Check Time 7.17s (± 0.58%) 7.14s (± 0.36%) ~ 7.10s 7.18s p=0.126 n=6
Emit Time 4.34s (± 0.70%) 4.34s (± 0.54%) ~ 4.31s 4.38s p=1.000 n=6
Total Time 14.94s (± 0.52%) 14.90s (± 0.28%) ~ 14.83s 14.95s p=0.520 n=6
material-ui - node (v16.17.1, x64)
Memory used 475,712k (± 0.01%) 475,718k (± 0.01%) ~ 475,667k 475,848k p=1.000 n=6
Parse Time 3.33s (± 0.41%) 3.32s (± 0.19%) ~ 3.31s 3.33s p=0.070 n=6
Bind Time 0.96s (± 0.54%) 0.96s (± 0.54%) ~ 0.95s 0.96s p=0.069 n=6
Check Time 18.11s (± 0.47%) 18.01s (± 0.29%) -0.10s (- 0.55%) 17.94s 18.08s p=0.030 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 22.41s (± 0.37%) 22.29s (± 0.23%) -0.12s (- 0.54%) 22.23s 22.36s p=0.020 n=6
xstate - node (v16.17.1, x64)
Memory used 545,722k (± 0.01%) 545,785k (± 0.02%) ~ 545,624k 545,960k p=0.575 n=6
Parse Time 4.27s (± 0.21%) 4.25s (± 0.30%) -0.02s (- 0.47%) 4.24s 4.27s p=0.027 n=6
Bind Time 1.75s (± 0.72%) 1.74s (± 0.67%) ~ 1.73s 1.76s p=0.278 n=6
Check Time 2.99s (± 0.51%) 2.98s (± 0.27%) -0.02s (- 0.61%) 2.97s 2.99s p=0.026 n=6
Emit Time 0.09s (± 5.53%) 0.09s (± 4.45%) ~ 0.09s 0.10s p=0.595 n=6
Total Time 9.11s (± 0.28%) 9.05s (± 0.18%) -0.06s (- 0.62%) 9.04s 9.08s p=0.005 n=6
System
Machine Namets-ci-ubuntu
Platformlinux 5.4.0-135-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 52947 6
Baseline main 6

Developer Information:

Download Benchmark

@DanielRosenwasser
Copy link
Member

why we allow const x: number = new Number(1)

It's the opposite - number is assignable (and comparable) to Number; Number is not assignable (nor comparable) to number.

const a: number = new Number(0); // error

const b: Number = 0; // allowed - structurally compatible

@jakebailey
Copy link
Member Author

why we allow const x: number = new Number(1)

It's the opposite - number is assignable (and comparable) to Number; Number is not assignable (nor comparable) to number.

const a: number = new Number(0); // error

const b: Number = 0; // allowed - structurally compatible

Yep, sorry. Flipped it when writing!

Comment on lines +21581 to +21583
if (source !== originalSource && originalFlags & TypeFlags.HasWrapper && !(sourceFlags & TypeFlags.HasWrapper) && source === target) {
return Ternary.False;
}
Copy link
Member

Choose a reason for hiding this comment

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

I actually am not sure if you want to disallow primitives from being assignable and comparable to wrapper objects. I think there's at least some rationale that you actually want to allow 1 as Number even if that is a bit silly - but I can't imagine why.

Given that, I think the right place for this is an ad-hoc check in === and !==, and possibly == and != too. But this is fine for prototyping.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's true, I think I misinterpreted your suggestion.

Copy link
Member

Choose a reason for hiding this comment

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

It's not the craziest idea - we highly discourage using the wrapper objects anyway.

@typescript-bot
Copy link
Collaborator

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

Something interesting changed - please have a look.

Details

akveo/ngx-admin

src/tsconfig.app.json

tsconfig.json

angular/angular-cli

7 of 18 projects failed to build with the old tsc and were ignored

tests/legacy-cli/tsconfig.json

conwnet/github1s

2 of 5 projects failed to build with the old tsc and were ignored

extensions/github1s/tsconfig.json

electron-react-boilerplate/electron-react-boilerplate

tsconfig.json

Eugeny/tabby

19 of 29 projects failed to build with the old tsc and were ignored

app/tsconfig.json

app/tsconfig.main.json

excalidraw/excalidraw

2 of 5 projects failed to build with the old tsc and were ignored

tsconfig-types.json

tsconfig.json

lyswhut/lx-music-desktop

1 of 6 projects failed to build with the old tsc and were ignored

src/main/tsconfig.json

microsoft/playwright

3 of 14 projects failed to build with the old tsc and were ignored

packages/trace-viewer/tsconfig.json

microsoft/vscode

5 of 53 projects failed to build with the old tsc and were ignored

extensions/npm/tsconfig.json

src/tsconfig.tsec.json

mobxjs/mobx

7 of 9 projects failed to build with the old tsc and were ignored

packages/mobx-undecorate/tsconfig.json

socketio/socket.io

5 of 11 projects failed to build with the old tsc and were ignored

examples/angular-todomvc/tsconfig.app.json

typeorm/typeorm

tsconfig.json

vercel/hyper

2 of 3 projects failed to build with the old tsc and were ignored

tsconfig.json

@Andarist
Copy link
Contributor

driveby q: what does "comparable" really mean?

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Feb 24, 2023

Comparability is a relaxed form of assignability. It aims to answer "is this plausibly of the same type" instead of "is this pretty much substitutable". The biggest difference used to just be that we check if any member of a union type source is related to a target, instead of all members of a union type source. The second biggest difference was around optional properties.

It's used for

  • comparison operators
  • switch/case
  • type assertions

See #5517 for the original PR. The biggest motivator was #6167. See #17214 for an oldish list of differences between comparability/assignability.

@jakebailey
Copy link
Member Author

jakebailey commented Feb 24, 2023

Fwiw I really like the result here. I almost think we should re-allow comparing number and Number before 5.0 goes out, and then do this PR instead for 5.1 along with the valueOf change, because comparison is really the only safe thing you can do with wrapper types.

@jakebailey

This comment was marked as outdated.

@typescript-bot

This comment was marked as outdated.

@typescript-bot

This comment was marked as outdated.

@RyanCavanaugh
Copy link
Member

The cure might worse than the disease here. I'm nervous that this introduces subtyping commutativity violations, and does so nominally.

{ length: number } <-- String <-/- string
{ length: number } <-- string

string would be assignable to e.g. Pick<String, keyof String> but not String ?

@jakebailey
Copy link
Member Author

I should disclaim that this is definitely not the final iteration of this PR, but something I threw together late at night.

Probably we could just do the conservative thing of not allowing string === String like we do number === NaN. That, or some other thing to make this better.

@jakebailey

This comment was marked as outdated.

@typescript-bot

This comment was marked as outdated.

@typescript-bot

This comment was marked as outdated.

@jakebailey
Copy link
Member Author

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 24, 2023

Heya @jakebailey, I've started to run the tarball bundle task on this PR at 17bda87. You can monitor the build here.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 24, 2023

Hey @jakebailey, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
    "devDependencies": {
        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/147441/artifacts?artifactName=tgz&fileId=B539571D0370740ECDE637EEDEC3A40BE7914E295D5B42E893AAB269C43464B802&fileName=/typescript-5.0.0-insiders.20230224.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@5.0.0-pr-52947-27".;

@fatcerberus
Copy link

I always thought the reason number is assignable to/comparable with Number was structural — the same reason primitives are assignable to {} and more to the point, e.g. { toString(): string }.

@jakebailey
Copy link
Member Author

Yep, that's right (what Ryan was saying above). In this case, we get the "apparent" type which is the wrapper and has all of the properties and methods, and that's structurally compared.

@jakebailey
Copy link
Member Author

I'm going to close this as this isn't the kind of approach we'd be taking. It also didn't seem to catch any bugs, just people using Number as a type annotation, which is of course not my favorite, but not actually bug-causing it seems.

I might come back to doing this like NaN and try to error when Number is compared with number, but that would be a different PR entirely.

@jakebailey jakebailey closed this Mar 10, 2023
@jakebailey jakebailey deleted the no-primitive-to-wrapper branch March 28, 2024 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants