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

Fixed an issue with apparent mapped type keys #57838

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

Andarist
Copy link
Contributor

fixes #57827

@typescript-bot typescript-bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Mar 18, 2024
@@ -22486,7 +22486,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
/*stringsOnly*/ false,
t => void mappedKeys.push(instantiateType(nameType, appendTypeMapping(targetType.mapper, getTypeParameterFromMappedType(targetType), t))),
);
return getUnionType(mappedKeys);
return mappedKeys.length ? getUnionType(mappedKeys) : stringNumberSymbolType;
Copy link
Contributor Author

@Andarist Andarist Mar 18, 2024

Choose a reason for hiding this comment

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

The problem here is that when modifiersType is unknown (or {}) then keyof modifiersType is never. Since never is assignable to everything, the checker assumed that keyof { [P in keyof T as T[P] extends string ? P: never]: any } is assignable to string.

Then based on that information it managed to simplify the instantiated substitution type of Extract to just keyof { [P in keyof T as T[P] extends string ? P: never]: any } (its newBaseType). Then in turn getNarrowableTypeForReference concluded that it should substitute constraints. Those were substituted with stringNumberSymbolType (for the keyof ...) and that's not assignable to string.

Before #56742 , it didn't mistakenly assume that keyof { [P in keyof T as T[P] extends string ? P: never]: any } is assignable to string so the substitution type was instantiated as (keyof { [P in keyof T as T[P] extends string ? P: never]: any }) & string and that is assignable to a string.

@LukeAbby
Copy link

Hi! I appreciate that reviewers can be very busy but I discovered this PR when bisecting my own issue with a bit of an absurd reproduction that was erroring with a much more opaque error "Type instantiation is excessively deep and possibly infinite." so I was unable to find any workarounds before the process of bisecting.

I'd really like to see this PR merged because when I built it locally it makes my code work! I wouldn't want to ask this of anyone else without being willing to do it myself so I did also review it and I can't find anything objectionable, though of course my opinion doesn't weigh as much given I'm not a maintainer.

@gabritto
Copy link
Member

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 30, 2024

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
pack this ✅ Started ✅ Results

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 30, 2024

Hey @gabritto, 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/164242/artifacts?artifactName=tgz&fileId=63DB465238EF48184C9FE24AA2B166C04B5AAA3CA7F9AFB4F3F63BF467E24E8402&fileName=/typescript-5.8.0-insiders.20241130.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/[email protected]".;

@gabritto
Copy link
Member

@typescript-bot test it

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 30, 2024

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
test top400 ✅ Started ✅ Results
user test this ✅ Started ✅ Results
run dt ✅ Started ✅ Results
perf test this faster ✅ Started 👀 Results

@typescript-bot
Copy link
Collaborator

@gabritto Here are the results of running the user tests with tsc comparing main and refs/pull/57838/merge:

Everything looks good!

@typescript-bot
Copy link
Collaborator

Hey @gabritto, the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-bot
Copy link
Collaborator

@gabritto
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
Compiler-Unions - node (v18.15.0, x64)
Errors 34 34 ~ ~ ~ p=1.000 n=6
Symbols 62,363 62,363 ~ ~ ~ p=1.000 n=6
Types 50,395 50,395 ~ ~ ~ p=1.000 n=6
Memory used 194,857k (± 1.02%) 194,229k (± 0.95%) ~ 193,006k 196,645k p=0.173 n=6
Parse Time 1.30s (± 0.75%) 1.31s (± 0.89%) ~ 1.29s 1.32s p=0.109 n=6
Bind Time 0.72s 0.72s ~ ~ ~ p=1.000 n=6
Check Time 9.81s (± 0.28%) 9.81s (± 0.51%) ~ 9.71s 9.85s p=0.809 n=6
Emit Time 2.74s (± 0.64%) 2.72s (± 0.23%) ~ 2.71s 2.73s p=0.070 n=6
Total Time 14.57s (± 0.11%) 14.56s (± 0.38%) ~ 14.45s 14.60s p=1.000 n=6
angular-1 - node (v18.15.0, x64)
Errors 37 37 ~ ~ ~ p=1.000 n=6
Symbols 947,936 947,936 ~ ~ ~ p=1.000 n=6
Types 410,955 410,955 ~ ~ ~ p=1.000 n=6
Memory used 1,226,005k (± 0.00%) 1,226,007k (± 0.00%) ~ 1,225,969k 1,226,073k p=0.936 n=6
Parse Time 6.65s (± 0.64%) 6.65s (± 0.46%) ~ 6.61s 6.70s p=1.000 n=6
Bind Time 1.89s (± 0.33%) 1.89s (± 0.27%) ~ 1.88s 1.89s p=0.386 n=6
Check Time 31.96s (± 0.24%) 31.95s (± 0.39%) ~ 31.73s 32.06s p=0.810 n=6
Emit Time 15.22s (± 0.51%) 15.20s (± 0.51%) ~ 15.11s 15.33s p=0.688 n=6
Total Time 55.72s (± 0.14%) 55.69s (± 0.22%) ~ 55.55s 55.84s p=0.688 n=6
mui-docs - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 2,501,464 2,501,464 ~ ~ ~ p=1.000 n=6
Types 909,569 909,569 ~ ~ ~ p=1.000 n=6
Memory used 2,318,592k (± 0.00%) 2,318,551k (± 0.00%) ~ 2,318,489k 2,318,648k p=0.230 n=6
Parse Time 9.29s (± 0.32%) 9.28s (± 0.24%) ~ 9.25s 9.32s p=0.681 n=6
Bind Time 2.18s (± 0.69%) 2.16s (± 0.63%) -0.02s (- 0.92%) 2.14s 2.18s p=0.040 n=6
Check Time 74.81s (± 0.54%) 75.28s (± 0.61%) ~ 74.90s 76.10s p=0.173 n=6
Emit Time 0.29s (± 3.60%) 0.29s (± 2.61%) ~ 0.28s 0.30s p=0.931 n=6
Total Time 86.56s (± 0.46%) 87.01s (± 0.53%) ~ 86.63s 87.84s p=0.229 n=6
self-build-src - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,225,251 1,225,251 ~ ~ ~ p=1.000 n=6
Types 266,546 266,546 ~ ~ ~ p=1.000 n=6
Memory used 2,354,384k (± 0.01%) 2,414,937k (± 6.12%) ~ 2,353,863k 2,717,160k p=1.000 n=6
Parse Time 5.26s (± 0.79%) 5.21s (± 1.14%) ~ 5.16s 5.33s p=0.066 n=6
Bind Time 1.80s (± 0.58%) 1.78s (± 1.23%) ~ 1.74s 1.80s p=0.210 n=6
Check Time 35.20s (± 0.20%) 35.16s (± 0.41%) ~ 34.96s 35.36s p=0.575 n=6
Emit Time 2.98s (± 1.57%) 2.97s (± 2.65%) ~ 2.82s 3.04s p=1.000 n=6
Total Time 45.25s (± 0.24%) 45.14s (± 0.38%) ~ 44.87s 45.31s p=0.296 n=6
self-build-src-public-api - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,225,251 1,225,251 ~ ~ ~ p=1.000 n=6
Types 266,546 266,546 ~ ~ ~ p=1.000 n=6
Memory used 2,786,942k (±14.25%) 2,907,388k (±12.89%) ~ 2,422,757k 3,149,947k p=0.936 n=6
Parse Time 6.91s (± 2.11%) 6.99s (± 1.69%) ~ 6.82s 7.11s p=0.261 n=6
Bind Time 2.18s (± 1.12%) 2.15s (± 2.23%) ~ 2.10s 2.22s p=0.470 n=6
Check Time 42.86s (± 0.64%) 42.81s (± 0.45%) ~ 42.46s 43.03s p=0.378 n=6
Emit Time 3.51s (± 4.08%) 3.54s (± 2.34%) ~ 3.45s 3.66s p=0.810 n=6
Total Time 55.46s (± 0.67%) 55.50s (± 0.29%) ~ 55.18s 55.61s p=0.810 n=6
self-compiler - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 262,223 262,223 ~ ~ ~ p=1.000 n=6
Types 106,607 106,607 ~ ~ ~ p=1.000 n=6
Memory used 439,759k (± 0.01%) 439,832k (± 0.02%) ~ 439,755k 439,988k p=0.092 n=6
Parse Time 3.56s (± 0.92%) 3.57s (± 0.62%) ~ 3.54s 3.60s p=0.418 n=6
Bind Time 1.32s (± 1.27%) 1.32s (± 1.24%) ~ 1.30s 1.34s p=0.742 n=6
Check Time 18.96s (± 0.42%) 18.98s (± 0.35%) ~ 18.88s 19.07s p=0.810 n=6
Emit Time 1.53s (± 0.99%) 1.54s (± 0.76%) ~ 1.53s 1.56s p=0.071 n=6
Total Time 25.36s (± 0.32%) 25.42s (± 0.34%) ~ 25.32s 25.54s p=0.260 n=6
ts-pre-modules - node (v18.15.0, x64)
Errors 70 70 ~ ~ ~ p=1.000 n=6
Symbols 226,062 226,062 ~ ~ ~ p=1.000 n=6
Types 94,488 94,488 ~ ~ ~ p=1.000 n=6
Memory used 371,703k (± 0.07%) 371,545k (± 0.01%) -158k (- 0.04%) 371,497k 371,567k p=0.005 n=6
Parse Time 2.90s (± 1.38%) 2.87s (± 1.48%) ~ 2.81s 2.93s p=0.258 n=6
Bind Time 1.58s (± 1.31%) 1.59s (± 1.08%) ~ 1.57s 1.62s p=0.288 n=6
Check Time 16.46s (± 0.46%) 16.46s (± 0.24%) ~ 16.41s 16.50s p=0.936 n=6
Emit Time 0.00s 0.00s (±244.70%) ~ 0.00s 0.01s p=0.405 n=6
Total Time 20.93s (± 0.52%) 20.92s (± 0.30%) ~ 20.82s 20.97s p=0.936 n=6
vscode - node (v18.15.0, x64)
Errors 4 4 ~ ~ ~ p=1.000 n=6
Symbols 3,188,226 3,188,226 ~ ~ ~ p=1.000 n=6
Types 1,096,566 1,096,566 ~ ~ ~ p=1.000 n=6
Memory used 3,262,606k (± 0.02%) 3,262,564k (± 0.01%) ~ 3,262,003k 3,263,113k p=0.936 n=6
Parse Time 14.12s (± 0.49%) 14.16s (± 0.58%) ~ 14.08s 14.30s p=0.226 n=6
Bind Time 4.56s (± 2.12%) 4.57s (± 1.87%) ~ 4.52s 4.74s p=0.462 n=6
Check Time 87.52s (± 1.53%) 87.41s (± 2.21%) ~ 85.83s 89.91s p=0.575 n=6
Emit Time 28.05s (± 3.03%) 27.31s (± 7.94%) ~ 23.07s 28.89s p=0.936 n=6
Total Time 134.24s (± 0.83%) 133.44s (± 1.09%) ~ 131.81s 135.89s p=0.230 n=6
webpack - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 288,733 288,733 ~ ~ ~ p=1.000 n=6
Types 117,152 117,152 ~ ~ ~ p=1.000 n=6
Memory used 441,046k (± 0.03%) 440,980k (± 0.03%) ~ 440,825k 441,235k p=0.471 n=6
Parse Time 4.08s (± 0.59%) 4.09s (± 0.51%) ~ 4.06s 4.12s p=0.375 n=6
Bind Time 1.73s (± 0.87%) 1.75s (± 1.02%) ~ 1.73s 1.78s p=0.164 n=6
Check Time 18.86s (± 0.33%) 18.88s (± 0.58%) ~ 18.75s 19.01s p=0.936 n=6
Emit Time 0.00s (±244.70%) 0.00s ~ ~ ~ p=0.405 n=6
Total Time 24.68s (± 0.34%) 24.73s (± 0.39%) ~ 24.60s 24.84s p=0.261 n=6
xstate-main - node (v18.15.0, x64)
Errors 5 5 ~ ~ ~ p=1.000 n=6
Symbols 552,390 552,390 ~ ~ ~ p=1.000 n=6
Types 185,096 185,096 ~ ~ ~ p=1.000 n=6
Memory used 492,477k (± 0.02%) 492,467k (± 0.01%) ~ 492,390k 492,517k p=0.575 n=6
Parse Time 3.41s (± 1.28%) 3.41s (± 0.79%) ~ 3.37s 3.45s p=0.746 n=6
Bind Time 1.17s (± 1.00%) 1.18s (± 0.93%) ~ 1.17s 1.20s p=0.383 n=6
Check Time 19.59s (± 1.48%) 19.43s (± 0.40%) ~ 19.33s 19.56s p=0.296 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 24.18s (± 1.32%) 24.03s (± 0.36%) ~ 23.93s 24.16s p=0.376 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Compiler-Unions - node (v18.15.0, x64)
  • angular-1 - node (v18.15.0, x64)
  • mui-docs - node (v18.15.0, x64)
  • self-build-src - node (v18.15.0, x64)
  • self-build-src-public-api - node (v18.15.0, x64)
  • self-compiler - node (v18.15.0, x64)
  • ts-pre-modules - node (v18.15.0, x64)
  • vscode - node (v18.15.0, x64)
  • webpack - node (v18.15.0, x64)
  • xstate-main - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks


function h<T>(z: StringKeys2<T>) {
z = "foo"; // error
Copy link
Member

Choose a reason for hiding this comment

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

Maybe add a const f: string = z; here too to show that it will still error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Shouldn't this be OK though? Only string keys are extracted by StringKeys2 so z has a string constraint~ and is assignable to string. This is how it works today (TS playground) and this PR doesn't change that

let sourceMappedKeys: Type;
if (nameType && isMappedTypeWithKeyofConstraintDeclaration(mappedType)) {
sourceMappedKeys = getApparentMappedTypeKeys(nameType, mappedType);
if (sourceMappedKeys.flags & TypeFlags.Never) {
Copy link
Member

Choose a reason for hiding this comment

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

I find it a bit weird that:

  • keyof unknown is undefined.
  • the fix is looking at the result of getApparentMappedTypeKeys. I'd expect it to be inside getApparentMappedTypeKeys. Isn't there a case where getApparentMappedTypeKeys is never and sourceMappedKeys should stay as never?

This doesn't look wrong to me, but I can't yet form a good mental model of what's happening, so I don't know what to think. 😅
I'll revisit in a few days and maybe I'll have a better idea of what this all means.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

keyof unknown is undefined.

If we think about keyof T as a set of types that can be safely used to access properties of T in T[keyof T] then never makes sense here. never is the only type that can be safely used as keyof T after we instantiate T as unknown.

the fix is looking at the result of getApparentMappedTypeKeys. I'd expect it to be inside getApparentMappedTypeKeys. Isn't there a case where getApparentMappedTypeKeys is never and sourceMappedKeys should stay as never?

That's what I started with but mapping never to PropertyKey would break the other call site where getApparentMappedTypeKeys is used. In there, it's used to get the target keys. So this would remove the error below:

type StringKeys2<T> = keyof {
  [P in keyof T as T[P] extends string ? P : never]: any;
};
function h<T>(z: StringKeys2<T>) {
  z = "foo"; // error
}

@typescript-bot
Copy link
Collaborator

@gabritto Here are the results of running the top 400 repos with tsc comparing main and refs/pull/57838/merge:

Everything looks good!

@Andarist Andarist force-pushed the fix/apparent-mapped-type-keys-with-unknown-modifier branch from 2f2b500 to cad532a Compare November 30, 2024 15:45
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
Status: Waiting on reviewers
Development

Successfully merging this pull request may close these issues.

Extract not narrowing type union in mapped type passed as generic
5 participants