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

Make any[] the effective rest type when any is spread #50034

Closed
wants to merge 5 commits into from

Conversation

Andarist
Copy link
Contributor

@Andarist Andarist commented Jul 25, 2022

I think this might be a prerequisite to fixing the issue #29919 fixes #29919

Why do I think this might be required?

  1. the problem with that issue is that getResolvedApparentTypeOfMappedType for MappedType<Parameters<FnType>> is not recognized as an array type
  2. but we know that it has to be - mapping over an array type (using a homomorphic mapped type) produces an array type and we know that Parameters<FnType> has to produce an array type
  3. with the current logic the computed constraint here is any and thus it doesn't pass isArrayOrTupleType check, the mapped type stays non-instantiated and later on can't be used as a type for the rest parameter
  4. therefore it, IMHO, makes sense to "propagate" the information from Parameters to the mapped type in the easiest way possible - by changing the final constraint of ...T to any[] when T extends any.

There are still some changes to be made to the code for this to be part of the fix. I'm still struggling to tweak the logic in getResolvedApparentTypeOfMappedType to instantiate the mapped type using this newly adjusted constraint but that's, sort of, a problem for another day. I'd like to check with the team first if I'm on the right path and if this change makes any sense. This has been done in #56727

@typescript-bot typescript-bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Jul 25, 2022
@typescript-bot
Copy link
Collaborator

This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise.

>x : string
>args : T

type U17<T extends any[]> = InstanceType<abstract new (x: string, ...args: T) => T[]>; // T[]
>U17 : T[]
>U17 : U17<T>
Copy link
Contributor Author

@Andarist Andarist Jul 25, 2022

Choose a reason for hiding this comment

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

Those 3 changes here are basically caused by the same issue.

Because target/source is now any[] and not any when we start comparing the signatures within the conditional type we don't pass the isTypeAssignable check here. Because of that the conditional type can't be "simplified" here to the trueType and thus the displayed type changed.

This check reaches the structuredTypeRelatedToWorker with types like:

source // any[]
target // [x: string, ...args: T]

I've expected this to be satisfied somewhere around this check but actually the target is not an array - it's a tuple and there is no logic supporting tuple targets here. The question is if this is deliberate or just an accidental omission?

I've played around with this more though and I've noticed 2 additional problems here though:

  1. any[] is not assignable to [string, ...any[]]. I understand why the error is reported for this case but in the presence of any in the type system it feels weird. Intuitively I've expected any[] to be assignable to all arrays and tuples, similarly to how anyFunctionType is always assignable, regardless of the parameters count etc, see the check here EDIT:// I know now that it's not how it's supposed to work, tuples might come with required elements and stuff and any[] doesn't guarantee that
  2. even if the previous point would get "fixed" then the base constraint for T here is unknown and not any[]. I don't understand why as I've expected the latter.

@@ -490,6 +490,6 @@ const result = invoker('test', true)({ test: (a: boolean) => 123 })
>123 : 123

type Foo2<A extends any[]> = ReturnType<(...args: A) => string>;
>Foo2 : string
>Foo2 : Foo2<A>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This change is caused by pretty similar stuff to the one described here.

The only~ difference here is that we get:

source // any[]
target // A

and there is not a lot of logic related to handling type params in the structuredTypeRelatedToWorker. So this target stays as A, its constraint is not really requested - but, again, the constraint for some reason is unknown, instead of any[]. Even though T's declaration points to T extends any[].

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'd like to check with the team first if I'm on the right path and if this change makes any sense.

Maybe? On its' own, I wouldn't really want to take this change. With some actual motivating test cases and alongside whatever other changes are needed to fix those? More likely. On the other hand, the original issue is pretty easy to work around. Toss in an extra Extract<whatever, any[]> and bam, constrained to an array type and now valid to spread. So if there are any adverse effects on existing code, it's pretty unlikely we'd take the change. Plus, why only any - either never would conceivably behave the same depending on if we're talking about the read or write constraint of the signature. For complexity's sake, it may just be better to expect the Extract<T, any[]> workaround.

But, to go back to the original issue, I honestly think a better fix would be in the constraint calculation for homomorphic mapped types, rather than this. Specifically, right now they don't have a constraint (they're just themselves), and since Parameters<GenericThing> is still... generic... when the mapped type is instantiated with it, you still get a mapped type out, rather than an array or tuple type. We could add a constraint to the mapped type, such that when the modifiers type of the mapped type is constrained to an array or tuple type, you get a (readonly, depending on constraint) array of the substituted template type as the constraint - basically removing the mapped-type-y-ness from the constraint calculation. Not sure if that'd be a perfect solution, but I think I'd be happier with that direction than a syntactic approach.

@Andarist
Copy link
Contributor Author

Specifically, right now they don't have a constraint (they're just themselves), and since Parameters is still... generic... when the mapped type is instantiated with it, you still get a mapped type out, rather than an array or tuple type.

This sounds nice (and kinda what I'm trying to achieve here) but the Parameters<GenericThing> is the modifiers type here and that doesn't have the array/tuple constraint. That is caused by the fact that we don't know its result came from ...P in T extends (...args: infer P) => any ? P : never. So it's impossible to propagate the array-ness of the modifiers here because that array-ness is not there in the first place (right now).

That's what this PR was meant to solve - it brought array-ness to this P. It has been a while since I opened it and I know much more about the compiler and all right now so I'll take a look at this with fresh eyes soon.

@typescript-bot
Copy link
Collaborator

The TypeScript team hasn't accepted the linked issue #29919. If you can get it accepted, this PR will have a better chance of being reviewed.

@Andarist
Copy link
Contributor Author

It's worth mentioning that a syntactic approach is already used by getInferredTypeParameterConstraint here:

// When an 'infer T' declaration is immediately contained in a rest parameter declaration, a rest type
// or a named rest tuple element, we infer an 'unknown[]' constraint.

But the problem here is that the infer P gets instantiated with any and thus it deoptimizes its inferred type parameter constraint and can't later be recognized as array-like.

It also turns out that in the meantime I have fixed the other part of the underlying issue (see #56727 ). So this PR now fixes #29919 . Of course, I understand that you might still want a different fix for this but I fail to see how to properly propagate this arrayness without relying on the syntax or without introducing some internal anyArrayLikeType (which perhaps could even leak to the user code here and that wouldn't be desirable).

@ahejlsberg
Copy link
Member

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

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jan 22, 2024

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jan 22, 2024

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jan 22, 2024

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Jan 22, 2024

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

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

There were infrastructure failures potentially unrelated to your change:

  • 1 instance of "Package install failed"

Otherwise...

Something interesting changed - please have a look.

Details

puppeteer

packages/browsers/test/src/tsconfig.json

@typescript-bot
Copy link
Collaborator

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

Here they are:

tsc

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Angular - node (v18.15.0, x64)
Memory used 295,650k (± 0.01%) 295,633k (± 0.00%) ~ 295,622k 295,645k p=0.470 n=6
Parse Time 2.67s (± 0.31%) 2.67s (± 0.46%) ~ 2.65s 2.68s p=0.730 n=6
Bind Time 0.83s (± 1.00%) 0.84s (± 1.43%) ~ 0.82s 0.85s p=0.175 n=6
Check Time 8.18s (± 0.24%) 8.18s (± 0.22%) ~ 8.16s 8.21s p=0.932 n=6
Emit Time 7.09s (± 0.34%) 7.09s (± 0.27%) ~ 7.08s 7.13s p=0.935 n=6
Total Time 18.77s (± 0.12%) 18.78s (± 0.04%) ~ 18.76s 18.78s p=0.265 n=6
Compiler-Unions - node (v18.15.0, x64)
Memory used 192,529k (± 1.26%) 191,504k (± 0.01%) -1,025k (- 0.53%) 191,465k 191,549k p=0.020 n=6
Parse Time 1.35s (± 1.24%) 1.36s (± 1.30%) ~ 1.33s 1.38s p=0.676 n=6
Bind Time 0.72s (± 0.00%) 0.72s (± 0.00%) ~ 0.72s 0.72s p=1.000 n=6
Check Time 9.36s (± 0.35%) 9.36s (± 0.50%) ~ 9.31s 9.44s p=0.810 n=6
Emit Time 2.61s (± 0.79%) 2.62s (± 0.62%) ~ 2.60s 2.64s p=0.808 n=6
Total Time 14.05s (± 0.18%) 14.06s (± 0.33%) ~ 13.99s 14.11s p=0.571 n=6
Monaco - node (v18.15.0, x64)
Memory used 347,462k (± 0.00%) 347,439k (± 0.00%) -23k (- 0.01%) 347,416k 347,457k p=0.045 n=6
Parse Time 2.48s (± 0.42%) 2.49s (± 0.22%) ~ 2.48s 2.49s p=1.000 n=6
Bind Time 0.93s (± 0.81%) 0.93s (± 0.68%) ~ 0.92s 0.94s p=0.718 n=6
Check Time 6.94s (± 0.59%) 6.93s (± 0.33%) ~ 6.90s 6.96s p=0.628 n=6
Emit Time 4.06s (± 0.20%) 4.06s (± 0.40%) ~ 4.04s 4.08s p=0.934 n=6
Total Time 14.41s (± 0.25%) 14.40s (± 0.20%) ~ 14.36s 14.43s p=0.746 n=6
TFS - node (v18.15.0, x64)
Memory used 302,835k (± 0.00%) 302,843k (± 0.01%) ~ 302,806k 302,884k p=0.336 n=6
Parse Time 2.01s (± 1.09%) 2.01s (± 0.44%) ~ 2.00s 2.02s p=0.934 n=6
Bind Time 1.00s (± 1.21%) 1.00s (± 0.98%) ~ 0.99s 1.02s p=0.867 n=6
Check Time 6.34s (± 0.33%) 6.33s (± 0.22%) ~ 6.31s 6.35s p=0.515 n=6
Emit Time 3.60s (± 0.64%) 3.59s (± 0.38%) ~ 3.58s 3.62s p=0.511 n=6
Total Time 12.96s (± 0.20%) 12.94s (± 0.06%) ~ 12.93s 12.95s p=0.088 n=6
material-ui - node (v18.15.0, x64)
Memory used 511,307k (± 0.00%) 511,286k (± 0.01%) ~ 511,213k 511,321k p=0.298 n=6
Parse Time 2.65s (± 0.37%) 2.66s (± 0.19%) ~ 2.65s 2.66s p=0.091 n=6
Bind Time 0.99s (± 1.05%) 1.00s (± 0.75%) ~ 0.99s 1.01s p=0.611 n=6
Check Time 17.24s (± 0.42%) 17.29s (± 0.55%) ~ 17.17s 17.45s p=0.629 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 20.89s (± 0.28%) 20.95s (± 0.47%) ~ 20.83s 21.12s p=0.260 n=6
mui-docs - node (v18.15.0, x64)
Memory used 1,696,089k (± 0.00%) 1,696,093k (± 0.00%) ~ 1,696,068k 1,696,113k p=0.689 n=6
Parse Time 6.53s (± 0.46%) 6.55s (± 0.83%) ~ 6.49s 6.65s p=0.935 n=6
Bind Time 2.35s (± 0.42%) 2.35s (± 0.64%) ~ 2.34s 2.38s p=0.865 n=6
Check Time 55.52s (± 0.43%) 55.58s (± 0.58%) ~ 55.23s 56.05s p=0.936 n=6
Emit Time 0.16s (± 0.00%) 0.16s (± 0.00%) ~ 0.16s 0.16s p=1.000 n=6
Total Time 64.56s (± 0.38%) 64.64s (± 0.48%) ~ 64.24s 65.11s p=0.689 n=6
self-build-src - node (v18.15.0, x64)
Memory used 2,413,313k (± 0.03%) 2,412,837k (± 0.02%) ~ 2,411,984k 2,413,256k p=0.173 n=6
Parse Time 4.91s (± 0.62%) 4.93s (± 0.62%) ~ 4.89s 4.97s p=0.422 n=6
Bind Time 1.88s (± 0.80%) 1.89s (± 0.86%) ~ 1.87s 1.91s p=0.805 n=6
Check Time 33.39s (± 0.37%) 33.40s (± 0.24%) ~ 33.29s 33.53s p=0.809 n=6
Emit Time 2.70s (± 1.09%) 2.70s (± 1.50%) ~ 2.64s 2.74s p=1.000 n=6
Total Time 42.91s (± 0.30%) 42.92s (± 0.20%) ~ 42.81s 43.01s p=0.630 n=6
self-compiler - node (v18.15.0, x64)
Memory used 419,694k (± 0.02%) 419,766k (± 0.02%) ~ 419,690k 419,899k p=0.378 n=6
Parse Time 2.77s (± 2.91%) 2.73s (± 3.14%) ~ 2.66s 2.84s p=0.466 n=6
Bind Time 1.12s (± 6.26%) 1.17s (± 6.46%) ~ 1.07s 1.24s p=0.254 n=6
Check Time 15.09s (± 0.31%) 15.19s (± 0.39%) +0.10s (+ 0.70%) 15.11s 15.29s p=0.013 n=6
Emit Time 1.16s (± 1.04%) 1.16s (± 0.91%) ~ 1.14s 1.17s p=0.282 n=6
Total Time 20.14s (± 0.30%) 20.25s (± 0.32%) +0.10s (+ 0.50%) 20.18s 20.37s p=0.025 n=6
vscode - node (v18.15.0, x64)
Memory used 2,807,197k (± 0.00%) 2,807,235k (± 0.00%) ~ 2,807,160k 2,807,323k p=0.298 n=6
Parse Time 10.63s (± 0.24%) 10.63s (± 0.35%) ~ 10.56s 10.67s p=0.517 n=6
Bind Time 3.38s (± 0.58%) 3.39s (± 0.80%) ~ 3.36s 3.43s p=0.568 n=6
Check Time 59.62s (± 0.67%) 59.57s (± 0.50%) ~ 59.25s 59.95s p=0.936 n=6
Emit Time 16.14s (± 0.40%) 16.21s (± 0.77%) ~ 16.09s 16.44s p=0.377 n=6
Total Time 89.78s (± 0.48%) 89.81s (± 0.42%) ~ 89.41s 90.29s p=0.810 n=6
webpack - node (v18.15.0, x64)
Memory used 392,442k (± 0.01%) 392,785k (± 0.02%) +343k (+ 0.09%) 392,697k 392,909k p=0.005 n=6
Parse Time 3.05s (± 0.95%) 3.05s (± 0.79%) ~ 3.02s 3.09s p=0.871 n=6
Bind Time 1.40s (± 0.37%) 1.40s (± 0.90%) ~ 1.39s 1.42s p=0.931 n=6
Check Time 13.95s (± 0.24%) 13.94s (± 0.29%) ~ 13.89s 14.01s p=0.687 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 18.40s (± 0.15%) 18.39s (± 0.33%) ~ 18.30s 18.46s p=0.809 n=6
xstate - node (v18.15.0, x64)
Memory used 513,378k (± 0.01%) 513,408k (± 0.01%) ~ 513,315k 513,454k p=0.298 n=6
Parse Time 3.29s (± 0.37%) 3.29s (± 0.37%) ~ 3.27s 3.30s p=0.789 n=6
Bind Time 1.54s (± 0.26%) 1.54s (± 0.33%) ~ 1.54s 1.55s p=0.595 n=6
Check Time 2.85s (± 0.72%) 2.85s (± 0.48%) ~ 2.83s 2.87s p=0.625 n=6
Emit Time 0.08s (± 0.00%) 0.08s (± 0.00%) ~ 0.08s 0.08s p=1.000 n=6
Total Time 7.75s (± 0.33%) 7.75s (± 0.19%) ~ 7.74s 7.78s p=1.000 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)
  • mui-docs - node (v18.15.0, x64)
  • self-build-src - node (v18.15.0, x64)
  • self-compiler - node (v18.15.0, x64)
  • vscode - node (v18.15.0, x64)
  • webpack - node (v18.15.0, x64)
  • xstate - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

@typescript-bot
Copy link
Collaborator

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

Branch only errors:

Package: lodash
Error:

Error: 
/home/vsts/work/1/DefinitelyTyped/types/lodash/common/function.d.ts
  1414:34  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect
  1420:39  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect

/home/vsts/work/1/DefinitelyTyped/types/lodash/common/util.d.ts
  313:71  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect
  317:67  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect
  321:63  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect
  325:59  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect
  329:55  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect
  333:56  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect
  343:71  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect
  347:67  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect
  351:63  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect
  355:59  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect
  359:55  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect
  363:56  error  TypeScript@local compile error: 
Type '"0"' cannot be used to index type 'Parameters<T>'  @definitelytyped/expect

✖ 14 problems (14 errors, 0 warnings)

    at testTypesVersion (/home/vsts/work/1/DefinitelyTyped/node_modules/.pnpm/@[email protected][email protected]/node_modules/@definitelytyped/dtslint/dist/index.js:166:15)
    at async runTests (/home/vsts/work/1/DefinitelyTyped/node_modules/.pnpm/@[email protected][email protected]/node_modules/@definitelytyped/dtslint/dist/index.js:125:9)

Package: es-abstract
Error:

Error: 
/home/vsts/work/1/DefinitelyTyped/types/es-abstract/test/es2015.test.ts
  66:1  error  TypeScript@local expected type to be:
  IteratorResult<number, boolean>
got:
  never  @definitelytyped/expect

✖ 1 problem (1 error, 0 warnings)

    at testTypesVersion (/home/vsts/work/1/DefinitelyTyped/node_modules/.pnpm/@[email protected][email protected]/node_modules/@definitelytyped/dtslint/dist/index.js:166:15)
    at async runTests (/home/vsts/work/1/DefinitelyTyped/node_modules/.pnpm/@[email protected][email protected]/node_modules/@definitelytyped/dtslint/dist/index.js:125:9)

You can check the log here.

@typescript-bot
Copy link
Collaborator

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

Something interesting changed - please have a look.

Details

chakra-ui/chakra-ui

4 of 28 projects failed to build with the old tsc and were ignored

packages/components/tsconfig.build.json

  • error TS5056: Cannot write file '/mnt/ts_downloads/chakra-ui/packages/components/dist/types/menu/menu.stories.d.ts' because it would be overwritten by multiple input files.
    • Project Scope

ReactiveX/rxjs

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

packages/rxjs/src/tsconfig.types.json

vercel/hyper

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

tsconfig.json

  • error TS2536: Type '"0"' cannot be used to index type 'Parameters<T>'.
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/function.d.ts#L1414 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/function.d.ts#L1420 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/util.d.ts#L313 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/util.d.ts#L317 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/util.d.ts#L321 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/util.d.ts#L325 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/util.d.ts#L329 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/util.d.ts#L333 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/util.d.ts#L343 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/util.d.ts#L347 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/util.d.ts#L351 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/util.d.ts#L355 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/util.d.ts#L359 in app/tsconfig.json
    • file:///mnt/ts_downloads/hyper/node_modules/@types/lodash/common/util.d.ts#L363 in app/tsconfig.json

@jakebailey
Copy link
Member

All of the above failures are lodash.

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/lodash/common/util.d.ts#L313

Code like:

flowRight<A extends any[]>(f1: (...args: A) => Parameters<T>["0"]): Function<(...args: A) => ReturnType<T>>;

@Andarist
Copy link
Contributor Author

Andarist commented Jan 22, 2024

I’ll investigate this as soon as I can 😉

declare module "lodash" {
  interface Function<T extends (...args: any) => any> {
    iteratee(): Function<T>;
  }
  interface Function<T> {
    flowRight<A extends any[], R1>(
      f2: (a: R1) => Parameters<T>["0"],
      f1: (...args: A) => R1,
    ): void;
  }
}

@Andarist
Copy link
Contributor Author

The minimal-minimal repro case for this is:

type Test<T extends any[]> = T["0"] // Type '"0"' cannot be used to index type 'T'.(2536)

I'd like to confirm before fixing this - this is meant to be valid, just like #56878 , since #48837 , right?

@ahejlsberg
Copy link
Member

I'd like to confirm before fixing this - this is meant to be valid...

Hmm, I don't think so. There are instantiations of T for which T["0"] is an error, for example [], so wouldn't be right to permit it.

@Andarist
Copy link
Contributor Author

Andarist commented Jan 22, 2024

This one doesn't error though:

type A<T extends any[]> = T[0]

Should they both be errors then? 😉 If they should error then I'd say that the lodash failure above is correct and shouldn't get fixed.

@ahejlsberg
Copy link
Member

Well, that's nice and inconsistent. It appears T[0] has always been permitted, and with that in mind, T["0"] ought to be valid too. So I guess that trumps the fact that it would be an error for some instantiations.

@Andarist
Copy link
Contributor Author

Andarist commented Jan 23, 2024

So I got this working with this patch:

`getLiteralTypeFromProperties` patch
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index dd24f9be31..29abbfa96a 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -17856,7 +17856,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
         const propertyTypes = map(getPropertiesOfType(type), prop => getLiteralTypeFromProperty(prop, include));
         const indexKeyTypes = map(getIndexInfosOfType(type), info =>
             info !== enumNumberIndexInfo && isKeyTypeIncluded(info.keyType, include) ?
-                info.keyType === stringType && include & TypeFlags.Number ? stringOrNumberType : info.keyType : neverType);
+                info.keyType === stringType && include & TypeFlags.Number ? stringOrNumberType : info.keyType === numberType ? getUnionType([numberType, numericStringType]) : info.keyType : neverType);
         return getUnionType(concatenate(propertyTypes, indexKeyTypes), UnionReduction.Literal, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, origin);
     }

That broke some things:

Failed tests
diff --git a/tests/baselines/reference/conditionalTypes2.errors.txt b/tests/baselines/reference/conditionalTypes2.errors.txt
index de78fe2d37..d5c6945d1e 100644
--- a/tests/baselines/reference/conditionalTypes2.errors.txt
+++ b/tests/baselines/reference/conditionalTypes2.errors.txt
@@ -13,10 +13,10 @@ conditionalTypes2.ts(24,5): error TS2322: Type 'Invariant<B>' is not assignable
             Type 'keyof B' is not assignable to type 'keyof A'.
               Type 'string | number | symbol' is not assignable to type 'keyof A'.
                 Type 'string' is not assignable to type 'keyof A'.
-                  Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
-                    Type 'keyof B' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
-                      Type 'string | number | symbol' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
-                        Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+                  Type 'string' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+                    Type 'keyof B' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+                      Type 'string | number | symbol' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+                        Type 'string' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
 conditionalTypes2.ts(25,5): error TS2322: Type 'Invariant<A>' is not assignable to type 'Invariant<B>'.
   Types of property 'foo' are incompatible.
     Type 'A extends string ? keyof A : A' is not assignable to type 'B extends string ? keyof B : B'.
@@ -79,10 +79,10 @@ conditionalTypes2.ts(75,12): error TS2345: Argument of type 'Extract2<T, Foo, Ba
 !!! error TS2322:             Type 'keyof B' is not assignable to type 'keyof A'.
 !!! error TS2322:               Type 'string | number | symbol' is not assignable to type 'keyof A'.
 !!! error TS2322:                 Type 'string' is not assignable to type 'keyof A'.
-!!! error TS2322:                   Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
-!!! error TS2322:                     Type 'keyof B' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
-!!! error TS2322:                       Type 'string | number | symbol' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
-!!! error TS2322:                         Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+!!! error TS2322:                   Type 'string' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+!!! error TS2322:                     Type 'keyof B' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+!!! error TS2322:                       Type 'string | number | symbol' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+!!! error TS2322:                         Type 'string' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
         b = a;  // Error
         ~
 !!! error TS2322: Type 'Invariant<A>' is not assignable to type 'Invariant<B>'.
diff --git a/tests/baselines/reference/deferredLookupTypeResolution2.types b/tests/baselines/reference/deferredLookupTypeResolution2.types
index b67a3bc92c..e638c63a2c 100644
--- a/tests/baselines/reference/deferredLookupTypeResolution2.types
+++ b/tests/baselines/reference/deferredLookupTypeResolution2.types
@@ -17,7 +17,7 @@ type B = ObjectHasKey<[string, number], '1'>;  // "true"
 >B : "true"
 
 type C = ObjectHasKey<[string, number], '2'>;  // "false"
->C : "false"
+>C : "true"
 
 type D = A<[string]>;  // "true"
 >D : "true"
diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types
index d1aca03181..345381f895 100644
--- a/tests/baselines/reference/keyofAndIndexedAccess.types
+++ b/tests/baselines/reference/keyofAndIndexedAccess.types
@@ -60,7 +60,7 @@ type K00 = keyof any;  // string
 >K00 : string | number | symbol
 
 type K01 = keyof string;  // "toString" | "charAt" | ...
->K01 : number | "length" | "toString" | "concat" | "slice" | "indexOf" | "lastIndexOf" | "charAt" | "charCodeAt" | "localeCompare" | "match" | "replace" | "search" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "valueOf"
+>K01 : number | `${number}` | "length" | "toString" | "concat" | "slice" | "indexOf" | "lastIndexOf" | "charAt" | "charCodeAt" | "localeCompare" | "match" | "replace" | "search" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "valueOf"
 
 type K02 = keyof number;  // "toString" | "toFixed" | "toExponential" | ...
 >K02 : "toString" | "toLocaleString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf"
@@ -111,7 +111,7 @@ type K18 = keyof (Shape & Item);  // "name" | "width" | "height" | "visible" | "
 >K18 : "name" | "width" | "height" | "visible" | "price"
 
 type K19 = keyof NumericallyIndexed<Shape> // never
->K19 : number
+>K19 : number | `${number}`
 
 type KeyOf<T> = keyof T;
 >KeyOf : keyof T
diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.types b/tests/baselines/reference/keyofAndIndexedAccessErrors.types
index 204e358211..2ba22589e9 100644
--- a/tests/baselines/reference/keyofAndIndexedAccessErrors.types
+++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.types
@@ -28,19 +28,19 @@ type T01 = keyof Object;
 >T01 : keyof Object
 
 type T02 = keyof keyof Object;
->T02 : number | "length" | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "valueOf"
+>T02 : number | `${number}` | "length" | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "valueOf"
 
 type T03 = keyof keyof keyof Object;
 >T03 : "toString" | "valueOf"
 
 type T04 = keyof keyof keyof keyof Object;
->T04 : number | "length" | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "valueOf"
+>T04 : number | `${number}` | "length" | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "valueOf"
 
 type T05 = keyof keyof keyof keyof keyof Object;
 >T05 : "toString" | "valueOf"
 
 type T06 = keyof keyof keyof keyof keyof keyof Object;
->T06 : number | "length" | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "valueOf"
+>T06 : number | `${number}` | "length" | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "valueOf"
 
 type T10 = Shape["name"];
 >T10 : string
diff --git a/tests/baselines/reference/mappedTypeAsClauses.types b/tests/baselines/reference/mappedTypeAsClauses.types
index 168e8bd6ea..0f1c89f7f8 100644
--- a/tests/baselines/reference/mappedTypeAsClauses.types
+++ b/tests/baselines/reference/mappedTypeAsClauses.types
@@ -35,10 +35,10 @@ type TP1 = TypeFromDefs<{ name: 'a', type: string } | { name: 'b', type: number
 // No array or tuple type mapping when 'as N' clause present
 
 type TA1 = Getters<string[]>;
->TA1 : { getConcat: () => { (...items: ConcatArray<string>[]): string[]; (...items: (string | ConcatArray<string>)[]): string[]; }; getIndexOf: () => (searchElement: string, fromIndex?: number | undefined) => number; getLastIndexOf: () => (searchElement: string, fromIndex?: number | undefined) => number; getSlice: () => (start?: number | undefined, end?: number | undefined) => string[]; getLength: () => number; getToLocaleString: () => () => string; getToString: () => () => string; getPop: () => () => string | undefined; getPush: () => (...items: string[]) => number; getJoin: () => (separator?: string | undefined) => string; getReverse: () => () => string[]; getShift: () => () => string | undefined; getSort: () => (compareFn?: ((a: string, b: string) => number) | undefined) => string[]; getSplice: () => { (start: number, deleteCount?: number | undefined): string[]; (start: number, deleteCount: number, ...items: string[]): string[]; }; getUnshift: () => (...items: string[]) => number; getEvery: () => { <S extends string>(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean; }; getSome: () => (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any) => boolean; getForEach: () => (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void; getMap: () => <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]; getFilter: () => { <S_1 extends string>(predicate: (value: string, index: number, array: string[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): string[]; }; getReduce: () => { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; <U_1>(callbackfn: (previousValue: U_1, currentValue: string, currentIndex: number, array: string[]) => U_1, initialValue: U_1): U_1; }; getReduceRight: () => { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; <U_2>(callbackfn: (previousValue: U_2, currentValue: string, currentIndex: number, array: string[]) => U_2, initialValue: U_2): U_2; }; }
+>TA1 : { [x: `get${Capitalize<`${number}`>}`]: () => string; getConcat: () => { (...items: ConcatArray<string>[]): string[]; (...items: (string | ConcatArray<string>)[]): string[]; }; getIndexOf: () => (searchElement: string, fromIndex?: number | undefined) => number; getLastIndexOf: () => (searchElement: string, fromIndex?: number | undefined) => number; getSlice: () => (start?: number | undefined, end?: number | undefined) => string[]; getLength: () => number; getToLocaleString: () => () => string; getToString: () => () => string; getPop: () => () => string | undefined; getPush: () => (...items: string[]) => number; getJoin: () => (separator?: string | undefined) => string; getReverse: () => () => string[]; getShift: () => () => string | undefined; getSort: () => (compareFn?: ((a: string, b: string) => number) | undefined) => string[]; getSplice: () => { (start: number, deleteCount?: number | undefined): string[]; (start: number, deleteCount: number, ...items: string[]): string[]; }; getUnshift: () => (...items: string[]) => number; getEvery: () => { <S extends string>(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean; }; getSome: () => (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any) => boolean; getForEach: () => (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void; getMap: () => <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]; getFilter: () => { <S_1 extends string>(predicate: (value: string, index: number, array: string[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): string[]; }; getReduce: () => { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; <U_1>(callbackfn: (previousValue: U_1, currentValue: string, currentIndex: number, array: string[]) => U_1, initialValue: U_1): U_1; }; getReduceRight: () => { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; <U_2>(callbackfn: (previousValue: U_2, currentValue: string, currentIndex: number, array: string[]) => U_2, initialValue: U_2): U_2; }; }
 
 type TA2 = Getters<[number, boolean]>;
->TA2 : { getConcat: () => { (...items: ConcatArray<number | boolean>[]): (number | boolean)[]; (...items: (number | boolean | ConcatArray<number | boolean>)[]): (number | boolean)[]; }; getIndexOf: () => (searchElement: number | boolean, fromIndex?: number | undefined) => number; getLastIndexOf: () => (searchElement: number | boolean, fromIndex?: number | undefined) => number; getSlice: () => (start?: number | undefined, end?: number | undefined) => (number | boolean)[]; getLength: () => 2; getToLocaleString: () => () => string; getToString: () => () => string; getPop: () => () => number | boolean | undefined; getPush: () => (...items: (number | boolean)[]) => number; getJoin: () => (separator?: string | undefined) => string; getReverse: () => () => (number | boolean)[]; getShift: () => () => number | boolean | undefined; getSort: () => (compareFn?: ((a: number | boolean, b: number | boolean) => number) | undefined) => [number, boolean]; getSplice: () => { (start: number, deleteCount?: number | undefined): (number | boolean)[]; (start: number, deleteCount: number, ...items: (number | boolean)[]): (number | boolean)[]; }; getUnshift: () => (...items: (number | boolean)[]) => number; getEvery: () => { <S extends number | boolean>(predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => unknown, thisArg?: any): boolean; }; getSome: () => (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => unknown, thisArg?: any) => boolean; getForEach: () => (callbackfn: (value: number | boolean, index: number, array: (number | boolean)[]) => void, thisArg?: any) => void; getMap: () => <U>(callbackfn: (value: number | boolean, index: number, array: (number | boolean)[]) => U, thisArg?: any) => U[]; getFilter: () => { <S_1 extends number | boolean>(predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => unknown, thisArg?: any): (number | boolean)[]; }; getReduce: () => { (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean): number | boolean; (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean, initialValue: number | boolean): number | boolean; <U_1>(callbackfn: (previousValue: U_1, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => U_1, initialValue: U_1): U_1; }; getReduceRight: () => { (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean): number | boolean; (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean, initialValue: number | boolean): number | boolean; <U_2>(callbackfn: (previousValue: U_2, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => U_2, initialValue: U_2): U_2; }; get0: () => number; get1: () => boolean; }
+>TA2 : { [x: `get${Capitalize<`${number}`>}`]: () => number | boolean; getConcat: () => { (...items: ConcatArray<number | boolean>[]): (number | boolean)[]; (...items: (number | boolean | ConcatArray<number | boolean>)[]): (number | boolean)[]; }; getIndexOf: () => (searchElement: number | boolean, fromIndex?: number | undefined) => number; getLastIndexOf: () => (searchElement: number | boolean, fromIndex?: number | undefined) => number; getSlice: () => (start?: number | undefined, end?: number | undefined) => (number | boolean)[]; getLength: () => 2; getToLocaleString: () => () => string; getToString: () => () => string; getPop: () => () => number | boolean | undefined; getPush: () => (...items: (number | boolean)[]) => number; getJoin: () => (separator?: string | undefined) => string; getReverse: () => () => (number | boolean)[]; getShift: () => () => number | boolean | undefined; getSort: () => (compareFn?: ((a: number | boolean, b: number | boolean) => number) | undefined) => [number, boolean]; getSplice: () => { (start: number, deleteCount?: number | undefined): (number | boolean)[]; (start: number, deleteCount: number, ...items: (number | boolean)[]): (number | boolean)[]; }; getUnshift: () => (...items: (number | boolean)[]) => number; getEvery: () => { <S extends number | boolean>(predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => unknown, thisArg?: any): boolean; }; getSome: () => (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => unknown, thisArg?: any) => boolean; getForEach: () => (callbackfn: (value: number | boolean, index: number, array: (number | boolean)[]) => void, thisArg?: any) => void; getMap: () => <U>(callbackfn: (value: number | boolean, index: number, array: (number | boolean)[]) => U, thisArg?: any) => U[]; getFilter: () => { <S_1 extends number | boolean>(predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: number | boolean, index: number, array: (number | boolean)[]) => unknown, thisArg?: any): (number | boolean)[]; }; getReduce: () => { (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean): number | boolean; (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean, initialValue: number | boolean): number | boolean; <U_1>(callbackfn: (previousValue: U_1, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => U_1, initialValue: U_1): U_1; }; getReduceRight: () => { (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean): number | boolean; (callbackfn: (previousValue: number | boolean, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => number | boolean, initialValue: number | boolean): number | boolean; <U_2>(callbackfn: (previousValue: U_2, currentValue: number | boolean, currentIndex: number, array: (number | boolean)[]) => U_2, initialValue: U_2): U_2; }; }
 
 // Filtering using 'as N' clause
 
diff --git a/tests/baselines/reference/templateLiteralTypes4.errors.txt b/tests/baselines/reference/templateLiteralTypes4.errors.txt
index 42bf2664af..ce37b8df59 100644
--- a/tests/baselines/reference/templateLiteralTypes4.errors.txt
+++ b/tests/baselines/reference/templateLiteralTypes4.errors.txt
@@ -1,8 +1,7 @@
-templateLiteralTypes4.ts(285,12): error TS2345: Argument of type '2' is not assignable to parameter of type '0 | 1'.
-templateLiteralTypes4.ts(289,12): error TS2345: Argument of type '2' is not assignable to parameter of type '0 | 1'.
+templateLiteralTypes4.ts(289,15): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'.
 
 
-==== templateLiteralTypes4.ts (2 errors) ====
+==== templateLiteralTypes4.ts (1 errors) ====
     // infer from number
     type TNumber0 = "100" extends `${infer N extends number}` ? N : never; // 100
     type TNumber1 = "-100" extends `${infer N extends number}` ? N : never; // -100
@@ -288,14 +287,12 @@ templateLiteralTypes4.ts(289,12): error TS2345: Argument of type '2' is not assi
     p.getIndex(0); // ok, 0 is a valid index
     p.getIndex(1); // ok, 1 is a valid index
     p.getIndex(2); // error, 2 is not a valid index
-               ~
-!!! error TS2345: Argument of type '2' is not assignable to parameter of type '0 | 1'.
     
     p.setIndex(0, 0); // ok, 0 is a valid index
     p.setIndex(1, 0); // ok, 1 is a valid index
     p.setIndex(2, 3); // error, 2 is not a valid index
-               ~
-!!! error TS2345: Argument of type '2' is not assignable to parameter of type '0 | 1'.
+                  ~
+!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'.
     
     // function inference
     declare function f1<T extends string | number>(s: `**${T}**`): T;
diff --git a/tests/baselines/reference/templateLiteralTypes4.types b/tests/baselines/reference/templateLiteralTypes4.types
index e139cd975a..d561764a0e 100644
--- a/tests/baselines/reference/templateLiteralTypes4.types
+++ b/tests/baselines/reference/templateLiteralTypes4.types
@@ -515,46 +515,46 @@ declare const p: Point;
 
 p.getIndex(0); // ok, 0 is a valid index
 >p.getIndex(0) : number
->p.getIndex : <I extends 0 | 1>(index: I) => FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>
+>p.getIndex : <I extends number>(index: I) => FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>
 >p : Point
->getIndex : <I extends 0 | 1>(index: I) => FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>
+>getIndex : <I extends number>(index: I) => FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>
 >0 : 0
 
 p.getIndex(1); // ok, 1 is a valid index
 >p.getIndex(1) : number
->p.getIndex : <I extends 0 | 1>(index: I) => FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>
+>p.getIndex : <I extends number>(index: I) => FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>
 >p : Point
->getIndex : <I extends 0 | 1>(index: I) => FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>
+>getIndex : <I extends number>(index: I) => FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>
 >1 : 1
 
 p.getIndex(2); // error, 2 is not a valid index
->p.getIndex(2) : number
->p.getIndex : <I extends 0 | 1>(index: I) => FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>
+>p.getIndex(2) : never
+>p.getIndex : <I extends number>(index: I) => FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>
 >p : Point
->getIndex : <I extends 0 | 1>(index: I) => FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>
+>getIndex : <I extends number>(index: I) => FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>
 >2 : 2
 
 p.setIndex(0, 0); // ok, 0 is a valid index
 >p.setIndex(0, 0) : void
->p.setIndex : <I extends 0 | 1>(index: I, value: FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>) => void
+>p.setIndex : <I extends number>(index: I, value: FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>) => void
 >p : Point
->setIndex : <I extends 0 | 1>(index: I, value: FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>) => void
+>setIndex : <I extends number>(index: I, value: FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>) => void
 >0 : 0
 >0 : 0
 
 p.setIndex(1, 0); // ok, 1 is a valid index
 >p.setIndex(1, 0) : void
->p.setIndex : <I extends 0 | 1>(index: I, value: FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>) => void
+>p.setIndex : <I extends number>(index: I, value: FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>) => void
 >p : Point
->setIndex : <I extends 0 | 1>(index: I, value: FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>) => void
+>setIndex : <I extends number>(index: I, value: FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>) => void
 >1 : 1
 >0 : 0
 
 p.setIndex(2, 3); // error, 2 is not a valid index
 >p.setIndex(2, 3) : void
->p.setIndex : <I extends 0 | 1>(index: I, value: FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>) => void
+>p.setIndex : <I extends number>(index: I, value: FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>) => void
 >p : Point
->setIndex : <I extends 0 | 1>(index: I, value: FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>) => void
+>setIndex : <I extends number>(index: I, value: FieldType<Extract<[{ name: "x"; type: "f64"; }, { name: "y"; type: "f64"; }][I], FieldDefinition>["type"]>) => void
 >2 : 2
 >3 : 3
 
diff --git a/tests/baselines/reference/variadicTuples1.errors.txt b/tests/baselines/reference/variadicTuples1.errors.txt
index a272e38931..707cbe7077 100644
--- a/tests/baselines/reference/variadicTuples1.errors.txt
+++ b/tests/baselines/reference/variadicTuples1.errors.txt
@@ -33,8 +33,6 @@ variadicTuples1.ts(190,5): error TS2322: Type 'T' is not assignable to type '[..
 variadicTuples1.ts(191,5): error TS2322: Type '[...T]' is not assignable to type '[...U]'.
   Type 'T' is not assignable to type 'U'.
     'T' is assignable to the constraint of type 'U', but 'U' could be instantiated with a different subtype of constraint 'readonly string[]'.
-variadicTuples1.ts(203,5): error TS2322: Type 'string' is not assignable to type 'keyof [1, 2, ...T]'.
-  Type '"2"' is not assignable to type '"0" | "1" | keyof T[]'.
 variadicTuples1.ts(213,5): error TS2322: Type '[...T, ...T]' is not assignable to type '[unknown, unknown]'.
   Type '[] | [unknown] | [unknown, unknown]' is not assignable to type '[unknown, unknown]'.
     Type '[]' is not assignable to type '[unknown, unknown]'.
@@ -48,7 +46,7 @@ variadicTuples1.ts(411,7): error TS2322: Type '[boolean, false]' is not assignab
     Type 'boolean' is not assignable to type 'number'.
 
 
-==== variadicTuples1.ts (22 errors) ====
+==== variadicTuples1.ts (21 errors) ====
     // Variadics in tuple types
     
     type TV0<T extends unknown[]> = [string, ...T];
@@ -305,9 +303,6 @@ variadicTuples1.ts(411,7): error TS2322: Type '[boolean, false]' is not assignab
         k3 = '0';
         k3 = '1';
         k3 = '2';  // Error
-        ~~
-!!! error TS2322: Type 'string' is not assignable to type 'keyof [1, 2, ...T]'.
-!!! error TS2322:   Type '"2"' is not assignable to type '"0" | "1" | keyof T[]'.
     }
     
     // Constraints of variadic tuple types

All of those failures are related to the fact that keyof Arr includes now the numericStringType and thus indexed accesses that were not allowed became allowed now. This turned out to be more of a breaking change than I'd like it to be. However... it just kinda makes sense under some of the existing assumptions.

Like showcased above - array types are always indexable with concrete numbers (despite the fact that some instantiations might not have those elements):

type Test1<A extends unknown[]> = A[0]

This actually isn't even an array problem, it's just an index signature thing:

type Test2<O extends { [k: string]: number }> = O["foo"] // ok
type Res2 = Test2<{ bar: 10 }> // unknown

We can read the comment in isApplicableIndexType to learn that:

        // A 'string' index signature applies to types assignable to 'string' or 'number', and a 'number' index
        // signature applies to types assignable to 'number', `${number}` and numeric string literal types.

To me, this means that number index signature is special because - at runtime - numbers used to index objects/arrays are actually coerced to strings. So this reflects exactly that and number index signature is, in fact, a specialcased type of a string index signature (that permits only numeric strings). In other words, the numericStringType (and numeric strings) should always be OK where the number and number literal types are allowed in the indexed access position.

But... that's not exactly how it works today and we can find some further examples of how things are not completely consistent:

type Arr = number[];

type Access1 = Arr[0]; // ok
type Access2 = Arr["0"]; // ok

type Check1 = 100 extends keyof Arr ? 1 : 0; // 1
type Check2 = "100" extends keyof Arr ? 1 : 0; // 0

Should check2 return 1? In my opinion, it should - based on the fact that it matches an applicable index type. I understand that keyof doesn't have to permit everything that an indexed access does (for example it won't include prototype methods etc) but it feels odd to ignore an index signature here.

That being said, getLiteralTypeFromProperties is used by getIndexType and that is used in multiple places that might not want to "normalize" an number index signature. So I quickly tried this patch:

scoped `getLiteralTypeFromProperties` patch
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index dd24f9be31..92714df2e7 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -17851,12 +17851,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
         return !!(keyType.flags & include || keyType.flags & TypeFlags.Intersection && some((keyType as IntersectionType).types, t => isKeyTypeIncluded(t, include)));
     }
 
-    function getLiteralTypeFromProperties(type: Type, include: TypeFlags, includeOrigin: boolean) {
+    function getLiteralTypeFromProperties(type: Type, include: TypeFlags, includeOrigin: boolean, includeNumericStringIndex = false) {
         const origin = includeOrigin && (getObjectFlags(type) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference) || type.aliasSymbol) ? createOriginIndexType(type) : undefined;
         const propertyTypes = map(getPropertiesOfType(type), prop => getLiteralTypeFromProperty(prop, include));
         const indexKeyTypes = map(getIndexInfosOfType(type), info =>
             info !== enumNumberIndexInfo && isKeyTypeIncluded(info.keyType, include) ?
-                info.keyType === stringType && include & TypeFlags.Number ? stringOrNumberType : info.keyType : neverType);
+                info.keyType === stringType && include & TypeFlags.Number ? stringOrNumberType : info.keyType === numberType && includeNumericStringIndex ? getUnionType([numberType, numericStringType]) : info.keyType : neverType);
         return getUnionType(concatenate(propertyTypes, indexKeyTypes), UnionReduction.Literal, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined, origin);
     }
 
@@ -17878,7 +17878,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
             type === wildcardType ? wildcardType :
             type.flags & TypeFlags.Unknown ? neverType :
             type.flags & (TypeFlags.Any | TypeFlags.Never) ? keyofConstraintType :
-            getLiteralTypeFromProperties(type, (indexFlags & IndexFlags.NoIndexSignatures ? TypeFlags.StringLiteral : TypeFlags.StringLike) | (indexFlags & IndexFlags.StringsOnly ? 0 : TypeFlags.NumberLike | TypeFlags.ESSymbolLike), indexFlags === defaultIndexFlags);
+            getLiteralTypeFromProperties(type, (indexFlags & IndexFlags.NoIndexSignatures ? TypeFlags.StringLiteral : TypeFlags.StringLike) | (indexFlags & IndexFlags.StringsOnly ? 0 : TypeFlags.NumberLike | TypeFlags.ESSymbolLike), indexFlags === defaultIndexFlags, !!(indexFlags & IndexFlags.IncludeNumericStringIndex));
     }
 
     function getExtractStringType(type: Type) {
@@ -22474,7 +22474,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
                         // false positives. For example, given 'T extends { [K in keyof T]: string }',
                         // 'keyof T' has itself as its constraint and produces a Ternary.Maybe when
                         // related to other types.
-                        if (isRelatedTo(source, getIndexType(constraint, (target as IndexType).indexFlags | IndexFlags.NoReducibleCheck), RecursionFlags.Target, reportErrors) === Ternary.True) {
+                        if (isRelatedTo(source, getIndexType(constraint, (target as IndexType).indexFlags | IndexFlags.IncludeNumericStringIndex | IndexFlags.NoReducibleCheck), RecursionFlags.Target, reportErrors) === Ternary.True) {
                             return Ternary.True;
                         }
                     }
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 48d8e2d01b..584076aac9 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -6670,6 +6670,7 @@ export const enum IndexFlags {
     StringsOnly = 1 << 0,
     NoIndexSignatures = 1 << 1,
     NoReducibleCheck = 1 << 2,
+    IncludeNumericStringIndex = 1 << 3,
 }
 
 // keyof T types (TypeFlags.Index)

That isn't as breaking - at least not when it comes to the tests contained in this repo. We only get one different error message:

changed error message baseline
diff --git a/tests/baselines/reference/conditionalTypes2.errors.txt b/tests/baselines/reference/conditionalTypes2.errors.txt
index de78fe2d37..d5c6945d1e 100644
--- a/tests/baselines/reference/conditionalTypes2.errors.txt
+++ b/tests/baselines/reference/conditionalTypes2.errors.txt
@@ -13,10 +13,10 @@ conditionalTypes2.ts(24,5): error TS2322: Type 'Invariant<B>' is not assignable
             Type 'keyof B' is not assignable to type 'keyof A'.
               Type 'string | number | symbol' is not assignable to type 'keyof A'.
                 Type 'string' is not assignable to type 'keyof A'.
-                  Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
-                    Type 'keyof B' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
-                      Type 'string | number | symbol' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
-                        Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+                  Type 'string' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+                    Type 'keyof B' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+                      Type 'string | number | symbol' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+                        Type 'string' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
 conditionalTypes2.ts(25,5): error TS2322: Type 'Invariant<A>' is not assignable to type 'Invariant<B>'.
   Types of property 'foo' are incompatible.
     Type 'A extends string ? keyof A : A' is not assignable to type 'B extends string ? keyof B : B'.
@@ -79,10 +79,10 @@ conditionalTypes2.ts(75,12): error TS2345: Argument of type 'Extract2<T, Foo, Ba
 !!! error TS2322:             Type 'keyof B' is not assignable to type 'keyof A'.
 !!! error TS2322:               Type 'string | number | symbol' is not assignable to type 'keyof A'.
 !!! error TS2322:                 Type 'string' is not assignable to type 'keyof A'.
-!!! error TS2322:                   Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
-!!! error TS2322:                     Type 'keyof B' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
-!!! error TS2322:                       Type 'string | number | symbol' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
-!!! error TS2322:                         Type 'string' is not assignable to type 'number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+!!! error TS2322:                   Type 'string' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+!!! error TS2322:                     Type 'keyof B' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+!!! error TS2322:                       Type 'string | number | symbol' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
+!!! error TS2322:                         Type 'string' is not assignable to type 'number | `${number}` | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "valueOf"'.
         b = a;  // Error
         ~
 !!! error TS2322: Type 'Invariant<A>' is not assignable to type 'Invariant<B>'.

Let me know what you think - which direction is worth exploring here (I don't expect the patch posted above to be final). And where the fix for this should go. It doesn't quite feel like part of this PR here - I'd probably extend #56878 with this since it feels highly related to this problem/inconsistency.

@Andarist
Copy link
Contributor Author

I'm closing this as this change became part of #57122

@Andarist Andarist closed this Feb 18, 2024
@Andarist Andarist deleted the any-array-effective-rest branch February 19, 2024 09:49
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
Archived in project
Development

Successfully merging this pull request may close these issues.

When trying to use mapped tuples as rest parameters error 'A rest parameter must be of an array type' given
5 participants