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

Pr 50377 redo filter boolean overload #56357

Conversation

craigphicks
Copy link

@craigphicks craigphicks commented Nov 9, 2023

It turns out a problem with this idea is that BooleanConstructor is not distinguishable from (x:any)=>boolean. Meaning TypeScript doesn't check the interface prototype when the prototype is a TypeScript Symbol for a user variable. I.e., in this case Boolean.

Oddly, it does check the interface prototype when the prototype is a unique ES Symbol.

If that gets sorted out, it might be worth considering.


Fixes #50377 - when Boolean is used a predicate to Array.filter falsie types are filtered in the result.
(However, this version doesn't use an overload in lib/es5.d.ts)

Source Files changed:

  • src/compiler/checker.ts

Added Test Files

  • tests/baselines/reference/unionOfArraysBooleanFilterCall.js
  • tests/cases/compiler/arrayFilterBooleanOverload.ts

Related issues

filter(Boolean)
#50377 Explore adding the Boolean to filter to narrow out null/undefined in array#filter (closed pull)
#50387 Add BooleanConstructor as an overload to .filter to allow for easy type predicate filtering (inDiscussion)
#30621 narrow array type with .filter(Boolean) (marked as duplicate of #16655, although #16655 is more broad than #30621)
#56013 Adding an overload to .filter breaks specific inference with nested functions (related)
#55777

Boolean as falsey check in general
#16655 Boolean() cannot be used to perform a null check (broader than filter(Boolean))
#29955 Allow Boolean() to be used to perform a null check (accepted pull, may have reverted)

#53489 Add fallback logic for generating signatures for unions of array members

Source code changes

  • checker.ts
    In chooseOverload, after result is selected, when the argument is of type BooleanConstructor and the result signature is Array.filter, the result signature is cloned, and its resolvedReturnType property is set.

Regressions

None found with runtests

Discussion

In this revision, es5.d.ts is not modified at all. Instead, the return type is changed dynamically in chooseOverload in checker.ts.
Therefore, the user won't see a completion suggesting Boolean, unlike the last revision. Obviously that is a disadvantage.

The advantage is that there are less changes overall, so the pull is easier to review. (Previously there were ~60 files changes, and that's a lot to review).

@typescript-bot typescript-bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Nov 9, 2023
@craigphicks craigphicks force-pushed the PR-50377-redo-filterBooleanOverload branch from 6fb5fd3 to f56e8f0 Compare November 9, 2023 22:31
@craigphicks craigphicks force-pushed the PR-50377-redo-filterBooleanOverload branch from f56e8f0 to e1a72ff Compare November 9, 2023 22:47
@fatcerberus
Copy link

Adding special magic for this in the compiler internals for what is otherwise a userland method call (that isn’t reflected in the method signature) feels like it opens a can of worms that the TS maintainers may not want to touch…

@craigphicks
Copy link
Author

craigphicks commented Nov 9, 2023

Adding special magic for this in the compiler internals for what is otherwise a userland method call (that isn’t reflected in the method signature) feels like it opens a can of worms that the TS maintainers may not want to touch…

I hadn't initially thought of the js standard lib as a userland method. The compiler type processing internals do include tests for the js Array and Readonly array symbols, but not for the methods within those objects - so this would indeed be an outlier there.

@fatcerberus
Copy link

Yeah, the methods are declared in userland - I don’t think the built-in .d.ts files are treated any differently from user-authored ones in that regard.

I’m not a maintainer, so take what I say with a grain of salt, but this does feel like the kind of thing there’d be significant resistance to, based on past experience (I’ve been lurking around here for many years).

@craigphicks
Copy link
Author

I believe you.

BTW - here is TypeScript compiler code where Array and Tuple methods are treated specially and rewritten:

            let memberName: __String;
            if (everyType(type, t => !!t.symbol?.parent && isArrayOrTupleSymbol(t.symbol.parent) && (!memberName ? (memberName = t.symbol.escapedName, true) : memberName === t.symbol.escapedName))) {
                // Transform the type from `(A[] | B[])["member"]` to `(A | B)[]["member"]` (since we pretend array is covariant anyway)
                const arrayArg = mapType(type, t => getMappedType((isReadonlyArraySymbol(t.symbol.parent) ? globalReadonlyArrayType : globalArrayType).typeParameters![0], (t as AnonymousType).mapper!));
                const arrayType = createArrayType(arrayArg, someType(type, t => isReadonlyArraySymbol(t.symbol.parent)));
                return (type as UnionType).arrayFallbackSignatures = getSignaturesOfType(getTypeOfPropertyOfType(arrayType, memberName!)!, kind);
            }
            (type as UnionType).arrayFallbackSignatures = result;

It is an example (and I think probably the only example) of the built-in .d.ts files being treated differently from user-authored ones. The result of being forced into a corner, I guess.

@jakebailey
Copy link
Member

Yes, that was explicitly added in #53489.

@typescript-bot test top200
@typescript-bot user test this
@typescript-bot run dt

@typescript-bot perf test this
@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 10, 2023

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 10, 2023

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 10, 2023

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 10, 2023

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

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 10, 2023

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 10, 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/158543/artifacts?artifactName=tgz&fileId=E14CF5EE3A7D5B0DFF2D7B26E5C27FC04EBE738879CE4B753E3735E9CC233E5502&fileName=/typescript-5.4.0-insiders.20231110.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.4.0-pr-56357-10".;

@typescript-bot
Copy link
Collaborator

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

There were infrastructure failures potentially unrelated to your change:

  • 3 instances of "Package install failed"

Otherwise...

Something interesting changed - please have a look.

Details

webpack

tsconfig.types.json

  • [MISSING] error TS2345: Argument of type 'Comparator<Module>' is not assignable to parameter of type '(a: Module | null, b: Module | null) => number'.
  • [MISSING] error TS18047: 'module' is possibly 'null'.
  • [MISSING] error TS2345: Argument of type 'Module | null' is not assignable to parameter of type 'Module'.

@typescript-bot
Copy link
Collaborator

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

Here they are:

Compiler

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Angular - node (v18.15.0, x64)
Memory used 295,173k (± 0.01%) 295,124k (± 0.01%) -49k (- 0.02%) 295,092k 295,164k p=0.037 n=6
Parse Time 2.63s (± 0.39%) 2.62s (± 0.45%) ~ 2.60s 2.63s p=0.056 n=6
Bind Time 0.84s (± 1.23%) 0.83s (± 1.00%) ~ 0.83s 0.85s p=0.923 n=6
Check Time 8.04s (± 0.32%) 8.04s (± 0.19%) ~ 8.03s 8.07s p=0.808 n=6
Emit Time 7.09s (± 0.48%) 7.07s (± 0.23%) ~ 7.06s 7.10s p=0.624 n=6
Total Time 18.60s (± 0.24%) 18.57s (± 0.12%) ~ 18.55s 18.61s p=0.332 n=6
Compiler-Unions - node (v18.15.0, x64)
Memory used 192,094k (± 1.24%) 192,676k (± 1.58%) ~ 190,666k 196,709k p=0.378 n=6
Parse Time 1.36s (± 1.46%) 1.35s (± 0.76%) ~ 1.34s 1.37s p=0.561 n=6
Bind Time 0.73s (± 0.00%) 0.73s (± 0.56%) ~ 0.73s 0.74s p=0.405 n=6
Check Time 9.15s (± 0.23%) 9.16s (± 0.24%) ~ 9.13s 9.19s p=0.627 n=6
Emit Time 2.62s (± 0.62%) 2.63s (± 0.57%) ~ 2.61s 2.65s p=0.366 n=6
Total Time 13.86s (± 0.20%) 13.88s (± 0.15%) ~ 13.86s 13.91s p=0.168 n=6
Monaco - node (v18.15.0, x64)
Memory used 347,354k (± 0.01%) 347,363k (± 0.00%) ~ 347,355k 347,378k p=0.688 n=6
Parse Time 2.45s (± 0.36%) 2.46s (± 0.31%) ~ 2.45s 2.47s p=0.053 n=6
Bind Time 0.94s (± 0.00%) 0.94s (± 0.43%) ~ 0.94s 0.95s p=0.405 n=6
Check Time 6.93s (± 0.67%) 6.94s (± 0.38%) ~ 6.91s 6.98s p=0.629 n=6
Emit Time 4.05s (± 0.53%) 4.05s (± 0.46%) ~ 4.03s 4.08s p=0.513 n=6
Total Time 14.38s (± 0.35%) 14.40s (± 0.22%) ~ 14.35s 14.45s p=0.288 n=6
TFS - node (v18.15.0, x64)
Memory used 302,608k (± 0.01%) 302,607k (± 0.01%) ~ 302,579k 302,674k p=0.688 n=6
Parse Time 2.01s (± 0.80%) 1.99s (± 0.38%) ~ 1.98s 2.00s p=0.071 n=6
Bind Time 1.01s (± 0.88%) 1.01s (± 0.97%) ~ 1.00s 1.02s p=0.798 n=6
Check Time 6.25s (± 0.30%) 6.26s (± 0.48%) ~ 6.22s 6.29s p=0.747 n=6
Emit Time 3.59s (± 0.49%) 3.59s (± 0.50%) ~ 3.57s 3.62s p=0.806 n=6
Total Time 12.86s (± 0.36%) 12.86s (± 0.23%) ~ 12.82s 12.91s p=0.294 n=6
material-ui - node (v18.15.0, x64)
Memory used 470,573k (± 0.01%) 470,593k (± 0.02%) ~ 470,527k 470,750k p=0.936 n=6
Parse Time 2.57s (± 0.20%) 2.57s (± 0.41%) ~ 2.55s 2.58s p=0.794 n=6
Bind Time 0.99s (± 0.76%) 1.00s (± 1.04%) ~ 0.98s 1.01s p=0.351 n=6
Check Time 16.70s (± 0.41%) 16.65s (± 0.59%) ~ 16.54s 16.79s p=0.230 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 20.26s (± 0.34%) 20.21s (± 0.52%) ~ 20.11s 20.36s p=0.298 n=6
xstate - node (v18.15.0, x64)
Memory used 512,822k (± 0.01%) 512,819k (± 0.01%) ~ 512,753k 512,896k p=0.936 n=6
Parse Time 3.28s (± 0.17%) 3.28s (± 0.17%) ~ 3.27s 3.28s p=1.000 n=6
Bind Time 1.54s (± 0.68%) 1.55s (± 0.54%) ~ 1.53s 1.55s p=1.000 n=6
Check Time 2.85s (± 0.34%) 2.88s (± 0.14%) +0.03s (+ 1.05%) 2.87s 2.88s p=0.003 n=6
Emit Time 0.08s (± 0.00%) 0.07s (± 5.69%) 🟩-0.01s (-10.42%) 0.07s 0.08s p=0.007 n=6
Total Time 7.75s (± 0.13%) 7.77s (± 0.15%) +0.02s (+ 0.32%) 7.76s 7.79s p=0.007 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Angular - node (v18.15.0, x64)
  • Compiler-Unions - node (v18.15.0, x64)
  • Monaco - node (v18.15.0, x64)
  • TFS - node (v18.15.0, x64)
  • material-ui - node (v18.15.0, x64)
  • xstate - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

tsserver

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-UnionsTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,399ms (± 0.75%) 2,390ms (± 0.79%) ~ 2,366ms 2,412ms p=0.378 n=6
Req 2 - geterr 5,349ms (± 1.13%) 5,366ms (± 1.38%) ~ 5,301ms 5,478ms p=0.936 n=6
Req 3 - references 326ms (± 1.44%) 326ms (± 0.93%) ~ 322ms 329ms p=1.000 n=6
Req 4 - navto 279ms (± 0.98%) 277ms (± 1.24%) ~ 273ms 281ms p=0.448 n=6
Req 5 - completionInfo count 1,356 (± 0.00%) 1,356 (± 0.00%) ~ 1,356 1,356 p=1.000 n=6
Req 5 - completionInfo 82ms (± 4.58%) 83ms (± 7.10%) ~ 75ms 90ms p=0.787 n=6
CompilerTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,501ms (± 0.54%) 2,495ms (± 1.14%) ~ 2,462ms 2,525ms p=1.000 n=6
Req 2 - geterr 4,082ms (± 2.13%) 4,078ms (± 1.84%) ~ 4,020ms 4,178ms p=0.936 n=6
Req 3 - references 341ms (± 1.69%) 340ms (± 1.76%) ~ 333ms 347ms p=0.686 n=6
Req 4 - navto 282ms (± 0.27%) 282ms (± 0.39%) ~ 280ms 283ms p=1.000 n=6
Req 5 - completionInfo count 1,518 (± 0.00%) 1,518 (± 0.00%) ~ 1,518 1,518 p=1.000 n=6
Req 5 - completionInfo 86ms (± 7.20%) 86ms (± 6.77%) ~ 78ms 90ms p=0.928 n=6
xstateTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,606ms (± 0.56%) 2,602ms (± 0.39%) ~ 2,592ms 2,618ms p=0.688 n=6
Req 2 - geterr 1,733ms (± 2.08%) 1,707ms (± 1.71%) ~ 1,673ms 1,742ms p=0.128 n=6
Req 3 - references 107ms (± 6.22%) 112ms (± 9.94%) ~ 102ms 123ms p=0.686 n=6
Req 4 - navto 365ms (± 0.97%) 366ms (± 0.75%) ~ 363ms 370ms p=0.936 n=6
Req 5 - completionInfo count 2,073 (± 0.00%) 2,073 (± 0.00%) ~ 2,073 2,073 p=1.000 n=6
Req 5 - completionInfo 313ms (± 1.98%) 306ms (± 1.93%) ~ 301ms 316ms p=0.123 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • CompilerTSServer - node (v18.15.0, x64)
  • Compiler-UnionsTSServer - node (v18.15.0, x64)
  • xstateTSServer - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Startup

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
tsc-startup - node (v18.15.0, x64)
Execution time 152.84ms (± 0.18%) 152.77ms (± 0.17%) -0.06ms (- 0.04%) 151.65ms 154.60ms p=0.042 n=600
tsserver-startup - node (v18.15.0, x64)
Execution time 228.13ms (± 0.15%) 228.03ms (± 0.16%) -0.10ms (- 0.04%) 226.94ms 233.16ms p=0.000 n=600
tsserverlibrary-startup - node (v18.15.0, x64)
Execution time 229.50ms (± 0.17%) 229.51ms (± 0.17%) ~ 228.05ms 234.84ms p=0.636 n=600
typescript-startup - node (v18.15.0, x64)
Execution time 228.77ms (± 0.17%) 228.82ms (± 0.17%) ~ 227.52ms 235.08ms p=0.255 n=600
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • tsc-startup - node (v18.15.0, x64)
  • tsserver-startup - node (v18.15.0, x64)
  • tsserverlibrary-startup - node (v18.15.0, x64)
  • typescript-startup - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

@fatcerberus
Copy link

Emit time improved 10+% from this? That has to be noise.

@jakebailey
Copy link
Member

It is, note the time granularity is only 0.01s and that test has enough problems that it doesn't emit and so runs in 0.08.

@typescript-bot
Copy link
Collaborator

Hey @jakebailey, the results of running the DT tests are ready.
Everything looks the same!
You can check the log here.

@typescript-bot
Copy link
Collaborator

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

Everything looks good!

@craigphicks craigphicks marked this pull request as draft November 11, 2023 16:03
@craigphicks
Copy link
Author

It turns out a problem with this idea is that BooleanConstructor is not distinguishable from (x:any)=>boolean. Meaning TypeScript doesn't check the interface prototype when the prototype is a TypeScript Symbol for a user variable. I.e., in this case Boolean.

Oddly, it does check the interface prototype when the prototype is a unique ES Symbol.

If that gets sorted out, it might be worth considering.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants