Skip to content

Defer types like keyof (T & {}) - #49696

Merged
Anders Hejlsberg (ahejlsberg) merged 7 commits into
mainfrom
fix49681
Jul 7, 2022
Merged

Defer types like keyof (T & {})#49696
Anders Hejlsberg (ahejlsberg) merged 7 commits into
mainfrom
fix49681

Conversation

@ahejlsberg

@ahejlsbergAnders Hejlsberg (ahejlsberg) commented Jun 27, 2022

Copy link
Copy Markdown
Member

Ordinarily, types of the form keyof (A & B) are normalized to keyof A | keyof B. However, for types like keyof (T & {}), this becomes keyof T | keyof {}, which becomes just keyof T, losing the fact that undefined and null have been removed from T. With this PR we defer the normalization for intersection types that include {}.

Fixes#49681.

@ahejlsberg

Copy link
Copy Markdown
MemberAuthor

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 27, 2022

Copy link
Copy Markdown
Contributor

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

Update: The results are in!

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 27, 2022

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the extended test suite on this PR at ab04c57. You can monitor the build here.

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 27, 2022

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the parallelized Definitely Typed test suite on this PR at ab04c57. You can monitor the build here.

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 27, 2022

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the abridged perf test suite on this PR at ab04c57. You can monitor the build here.

Update: The results are in!

@typescript-bot

Copy link
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg)
The results of the perf run you requested are in!

Here they are:

Comparison Report - main..49696

Metricmain49696DeltaBestWorst
Angular - node (v14.15.1, x64)
Memory used335,271k (± 0.01%)335,222k (± 0.01%)-49k (- 0.01%)335,150k335,264k
Parse Time2.06s (± 0.62%)2.06s (± 0.63%)-0.00s (- 0.19%)2.04s2.09s
Bind Time0.90s (± 0.52%)0.90s (± 0.52%)-0.00s (- 0.00%)0.89s0.91s
Check Time5.74s (± 0.44%)5.75s (± 0.53%)+0.01s (+ 0.17%)5.68s5.83s
Emit Time6.38s (± 0.46%)6.37s (± 0.59%)-0.01s (- 0.19%)6.28s6.46s
Total Time15.08s (± 0.21%)15.07s (± 0.40%)-0.00s (- 0.02%)14.94s15.22s
Compiler-Unions - node (v14.15.1, x64)
Memory used192,683k (± 0.02%)192,671k (± 0.02%)-12k (- 0.01%)192,571k192,732k
Parse Time0.85s (± 0.70%)0.85s (± 0.56%)+0.01s (+ 0.83%)0.84s0.86s
Bind Time0.57s (± 0.78%)0.57s (± 0.70%)-0.00s (- 0.17%)0.56s0.58s
Check Time7.63s (± 0.65%)7.63s (± 0.58%)-0.00s (- 0.01%)7.55s7.72s
Emit Time2.50s (± 0.49%)2.51s (± 0.75%)+0.00s (+ 0.16%)2.45s2.54s
Total Time11.55s (± 0.51%)11.56s (± 0.50%)+0.01s (+ 0.05%)11.42s11.66s
Monaco - node (v14.15.1, x64)
Memory used325,624k (± 0.00%)325,621k (± 0.01%)-2k (- 0.00%)325,578k325,669k
Parse Time1.58s (± 0.60%)1.57s (± 0.49%)-0.01s (- 0.44%)1.56s1.59s
Bind Time0.79s (± 0.59%)0.79s (± 0.62%)+0.00s (+ 0.51%)0.78s0.80s
Check Time5.67s (± 0.48%)5.68s (± 0.40%)+0.01s (+ 0.12%)5.62s5.72s
Emit Time3.36s (± 0.84%)3.36s (± 0.33%)0.00s ( 0.00%)3.35s3.40s
Total Time11.40s (± 0.36%)11.40s (± 0.17%)+0.01s (+ 0.05%)11.37s11.45s
TFS - node (v14.15.1, x64)
Memory used288,764k (± 0.01%)288,757k (± 0.00%)-7k (- 0.00%)288,737k288,786k
Parse Time1.32s (± 1.69%)1.34s (± 1.30%)+0.02s (+ 1.37%)1.29s1.37s
Bind Time0.78s (± 4.28%)0.77s (± 3.88%)-0.02s (- 2.30%)0.74s0.85s
Check Time5.31s (± 0.48%)5.31s (± 0.56%)-0.01s (- 0.13%)5.27s5.39s
Emit Time3.59s (± 2.23%)3.49s (± 1.53%)-0.10s (- 2.87%)3.43s3.69s
Total Time11.00s (± 0.80%)10.89s (± 0.68%)-0.11s (- 1.00%)10.79s11.08s
material-ui - node (v14.15.1, x64)
Memory used446,364k (± 0.01%)446,357k (± 0.00%)-6k (- 0.00%)446,328k446,415k
Parse Time1.86s (± 0.39%)1.87s (± 0.58%)+0.01s (+ 0.54%)1.85s1.90s
Bind Time0.73s (± 0.00%)0.73s (± 1.11%)-0.00s (- 0.00%)0.71s0.75s
Check Time13.09s (± 0.78%)13.05s (± 0.64%)-0.04s (- 0.32%)12.89s13.31s
Emit Time0.00s (± 0.00%)0.00s (± 0.00%)0.00s ( NaN%)0.00s0.00s
Total Time15.68s (± 0.66%)15.65s (± 0.61%)-0.03s (- 0.21%)15.48s15.95s
xstate - node (v14.15.1, x64)
Memory used541,027k (± 0.01%)541,220k (± 0.00%)+193k (+ 0.04%)541,180k541,275k
Parse Time2.59s (± 0.23%)2.60s (± 0.62%)+0.00s (+ 0.15%)2.58s2.66s
Bind Time1.15s (± 1.42%)1.16s (± 1.00%)+0.00s (+ 0.17%)1.13s1.18s
Check Time1.53s (± 0.49%)1.54s (± 0.73%)+0.01s (+ 0.46%)1.52s1.57s
Emit Time0.07s (± 4.66%)0.07s (± 3.14%)-0.00s (- 2.74%)0.07s0.08s
Total Time5.35s (± 0.28%)5.37s (± 0.55%)+0.02s (+ 0.43%)5.33s5.47s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-210-generic
Architecturex64
Available Memory16 GB
Available Memory15 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v14.15.1, x64)
Scenarios
  • Angular - node (v14.15.1, x64)
  • Compiler-Unions - node (v14.15.1, x64)
  • Monaco - node (v14.15.1, x64)
  • TFS - node (v14.15.1, x64)
  • material-ui - node (v14.15.1, x64)
  • xstate - node (v14.15.1, x64)
BenchmarkNameIterations
Current4969610
Baselinemain10

Developer Information:

Download Benchmark

@typescript-bot

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@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
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg)
Great news! no new errors were found between main..refs/pull/49696/merge

Comment on lines +33 to +35
type K04 = keyof void; // never
type K05 = keyof undefined; // never
type K06 = keyof null; // never
type K05 = keyof undefined; // string | number | symbol
type K06 = keyof null; // string | number | symbol

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For me, as a user - this doesn't quite make any sense. It's super hard for me to reason about what kind of runtime behavior this is supposed to represent. I've also checked it with this PR and this creates an unsoundness and might lead to runtime errors like here:

functiontest<T,KextendskeyofT>(t: T,k: K){returnt[k];// oops, runtime error if we allow the call below}test(null,'foo')// OK with this patch, error with 4.7

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Given the new 4.8 behavior, test itself should have an error in its implementation due to not having a { } constraint.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

First experiment wasn't optimal, now deferring types like keyof (T & {}) such that effects of removing undefined and null are properly reflected.

@ahejlsbergAnders Hejlsberg (ahejlsberg) changed the title keyof undefined and keyof null same as keyof neverDefer types like keyof (T & {})Jun 28, 2022
@ahejlsberg

Copy link
Copy Markdown
MemberAuthor

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 28, 2022

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the parallelized Definitely Typed test suite on this PR at b457868. You can monitor the build here.

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 28, 2022

Copy link
Copy Markdown
Contributor

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

Update: The results are in!

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 28, 2022

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the abridged perf test suite on this PR at b457868. You can monitor the build here.

Update: The results are in!

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 28, 2022

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the extended test suite on this PR at b457868. You can monitor the build here.

@typescript-bot

Copy link
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg)
Great news! no new errors were found between main..refs/pull/49696/merge

@typescript-bot

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@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
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg)
The results of the perf run you requested are in!

Here they are:

Comparison Report - main..49696

Metricmain49696DeltaBestWorst
Angular - node (v14.15.1, x64)
Memory used335,277k (± 0.01%)335,256k (± 0.01%)-21k (- 0.01%)335,136k335,293k
Parse Time2.07s (± 0.49%)2.06s (± 0.59%)-0.00s (- 0.19%)2.04s2.10s
Bind Time0.90s (± 0.84%)0.91s (± 0.54%)+0.00s (+ 0.22%)0.90s0.92s
Check Time5.75s (± 0.61%)5.76s (± 0.31%)+0.01s (+ 0.19%)5.73s5.81s
Emit Time6.41s (± 0.86%)6.40s (± 0.58%)-0.01s (- 0.11%)6.32s6.50s
Total Time15.13s (± 0.54%)15.13s (± 0.27%)-0.00s (- 0.01%)15.04s15.23s
Compiler-Unions - node (v14.15.1, x64)
Memory used192,558k (± 0.13%)192,681k (± 0.02%)+123k (+ 0.06%)192,622k192,777k
Parse Time0.85s (± 1.23%)0.85s (± 0.85%)-0.00s (- 0.12%)0.84s0.87s
Bind Time0.58s (± 1.21%)0.57s (± 0.97%)-0.01s (- 1.21%)0.56s0.59s
Check Time7.68s (± 0.63%)7.69s (± 0.74%)+0.01s (+ 0.16%)7.53s7.79s
Emit Time2.51s (± 0.94%)2.50s (± 0.69%)-0.01s (- 0.40%)2.46s2.55s
Total Time11.63s (± 0.58%)11.62s (± 0.61%)-0.01s (- 0.08%)11.40s11.73s
Monaco - node (v14.15.1, x64)
Memory used325,618k (± 0.01%)325,612k (± 0.00%)-5k (- 0.00%)325,576k325,636k
Parse Time1.59s (± 1.10%)1.59s (± 0.90%)0.00s ( 0.00%)1.57s1.63s
Bind Time0.80s (± 0.81%)0.80s (± 0.81%)-0.00s (- 0.00%)0.79s0.82s
Check Time5.69s (± 0.72%)5.68s (± 0.53%)-0.01s (- 0.26%)5.59s5.73s
Emit Time3.38s (± 0.75%)3.36s (± 0.37%)-0.02s (- 0.59%)3.33s3.39s
Total Time11.46s (± 0.56%)11.43s (± 0.32%)-0.03s (- 0.30%)11.34s11.49s
TFS - node (v14.15.1, x64)
Memory used288,784k (± 0.01%)288,792k (± 0.01%)+9k (+ 0.00%)288,752k288,836k
Parse Time1.33s (± 1.71%)1.33s (± 2.12%)-0.00s (- 0.15%)1.29s1.40s
Bind Time0.75s (± 2.25%)0.78s (± 4.70%)+0.03s (+ 3.98%)0.74s0.86s
Check Time5.32s (± 0.46%)5.33s (± 0.46%)+0.01s (+ 0.23%)5.26s5.38s
Emit Time3.53s (± 2.45%)3.61s (± 1.83%)+0.07s (+ 2.09%)3.47s3.71s
Total Time10.94s (± 0.94%)11.05s (± 0.48%)+0.11s (+ 1.00%)10.91s11.14s
material-ui - node (v14.15.1, x64)
Memory used446,260k (± 0.04%)446,377k (± 0.00%)+118k (+ 0.03%)446,340k446,443k
Parse Time1.88s (± 0.46%)1.88s (± 0.66%)-0.00s (- 0.21%)1.85s1.90s
Bind Time0.73s (± 0.96%)0.73s (± 1.46%)-0.01s (- 0.68%)0.71s0.76s
Check Time13.09s (± 0.52%)13.15s (± 0.73%)+0.06s (+ 0.45%)12.93s13.33s
Emit Time0.00s (± 0.00%)0.00s (± 0.00%)0.00s ( NaN%)0.00s0.00s
Total Time15.70s (± 0.43%)15.75s (± 0.65%)+0.05s (+ 0.32%)15.50s15.96s
xstate - node (v14.15.1, x64)
Memory used541,030k (± 0.00%)541,039k (± 0.00%)+9k (+ 0.00%)540,994k541,077k
Parse Time2.60s (± 0.51%)2.60s (± 0.51%)-0.00s (- 0.08%)2.58s2.63s
Bind Time1.17s (± 1.30%)1.15s (± 1.62%)-0.02s (- 2.13%)1.12s1.21s
Check Time1.54s (± 0.63%)1.54s (± 0.58%)+0.00s (+ 0.32%)1.52s1.56s
Emit Time0.07s (± 4.92%)0.07s (± 4.66%)-0.00s (- 1.35%)0.07s0.08s
Total Time5.39s (± 0.62%)5.37s (± 0.41%)-0.02s (- 0.35%)5.33s5.44s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-210-generic
Architecturex64
Available Memory16 GB
Available Memory15 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v14.15.1, x64)
Scenarios
  • Angular - node (v14.15.1, x64)
  • Compiler-Unions - node (v14.15.1, x64)
  • Monaco - node (v14.15.1, x64)
  • TFS - node (v14.15.1, x64)
  • material-ui - node (v14.15.1, x64)
  • xstate - node (v14.15.1, x64)
BenchmarkNameIterations
Current4969610
Baselinemain10

Developer Information:

Download Benchmark

@Andarist

Copy link
Copy Markdown
Contributor

I've noticed that this PR didn't introduce the reported issue from #49681 as a test case. Is that alright? Or perhaps just an accidental omission?

Comment on lines +273 to +275
// We allow an unconstrained object of a generic type `T` to be indexed by a key of type `keyof T`
// without a check that the object is non-undefined and non-null. This is safe because `keyof T`
// is `never` (meaning no possible keys) for any `T` that includes `undefined` or `null`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Isn't this sort of an unusual thing to "defer" the error reporting to the call site? I'm not totally sure what I think about this yet but my first thought was that it's a special case in the algorithm that might be hard to explain to people.

@AndaristMateusz Burzyński (Andarist)Jun 29, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I guess this build on top of the fact that T[never] is always OK and never is always assignable to everything so perhaps this fits the existing rules just OK. I'll take consolation in the fact Ryan Cavanaugh (@RyanCavanaugh)'s first instinct was that those should raise errors too 😅

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I added the test and the comment to capture the behavior for future reference. It's nothing new actually. We have the same behavior in 4.7 with the old NonNullable<T> based on a conditional type. This PR ensures we carry it forward in 4.8 with NonNullable<T> based on T & {}.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sure - but I'm not thinking here from the PoV of NonNullable at all. My confusion/concern was solely about unconstrained generics and your changes from #49119, I've assumed that given those changes this indexed access would error nowadays. Since the base constraint of unconstrained T is unknown it feels off that we can use indexed access on it without refining the type somehow first

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It may seem surprising at first, but it is actually quite meaningful because we can permit t[k]without requiring local proof that t is non-undefined and non-null. Such proof would add unnecessary overhead for instantiations where the type argument for T is already known not to be undefined or null.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Author: TeamFor Milestone BugPRs that fix a bug with a specific milestone

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

"cannot be used to index type" error when indexing NonNullable of a generic type in typescript 4.8

5 participants

@ahejlsberg@typescript-bot@Andarist@RyanCavanaugh@sandersn