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

Add optionality to mapped type indexed access substitutions #57549

Merged
merged 5 commits into from
Mar 4, 2024
Merged

Conversation

ahejlsberg
Copy link
Member

Fixes #57487.

@ahejlsberg
Copy link
Member Author

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

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 26, 2024

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 26, 2024

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 26, 2024

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

Update: The results are in!

@typescript-bot
Copy link
Collaborator

typescript-bot commented Feb 26, 2024

Heya @ahejlsberg, I've started to run the diff-based user code test suite on this PR at ab3184b. 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/57549/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,642k (± 0.00%) 295,638k (± 0.01%) ~ 295,619k 295,665k p=0.873 n=6
Parse Time 2.66s (± 0.31%) 2.66s (± 0.39%) ~ 2.65s 2.68s p=0.932 n=6
Bind Time 0.83s (± 0.90%) 0.83s (± 0.90%) ~ 0.82s 0.84s p=1.000 n=6
Check Time 8.27s (± 0.25%) 8.22s (± 0.31%) -0.04s (- 0.54%) 8.19s 8.26s p=0.018 n=6
Emit Time 7.10s (± 0.37%) 7.11s (± 0.20%) ~ 7.09s 7.13s p=0.567 n=6
Total Time 18.87s (± 0.08%) 18.83s (± 0.20%) ~ 18.80s 18.90s p=0.052 n=6
Compiler-Unions - node (v18.15.0, x64)
Memory used 192,582k (± 1.23%) 194,569k (± 1.68%) ~ 191,561k 197,655k p=0.689 n=6
Parse Time 1.36s (± 1.08%) 1.37s (± 1.13%) ~ 1.35s 1.39s p=0.220 n=6
Bind Time 0.72s (± 0.00%) 0.72s (± 0.57%) ~ 0.72s 0.73s p=0.405 n=6
Check Time 9.33s (± 0.31%) 9.36s (± 0.46%) ~ 9.29s 9.41s p=0.261 n=6
Emit Time 2.61s (± 0.40%) 2.63s (± 1.05%) ~ 2.60s 2.67s p=0.183 n=6
Total Time 14.02s (± 0.21%) 14.08s (± 0.47%) ~ 13.98s 14.16s p=0.108 n=6
Monaco - node (v18.15.0, x64)
Memory used 347,467k (± 0.00%) 347,480k (± 0.00%) ~ 347,461k 347,492k p=0.228 n=6
Parse Time 2.48s (± 0.78%) 2.48s (± 0.71%) ~ 2.46s 2.50s p=0.869 n=6
Bind Time 0.93s (± 0.44%) 0.93s (± 0.56%) ~ 0.92s 0.93s p=0.595 n=6
Check Time 6.94s (± 0.27%) 6.96s (± 0.75%) ~ 6.91s 7.03s p=0.872 n=6
Emit Time 4.05s (± 0.48%) 4.07s (± 0.30%) ~ 4.05s 4.08s p=0.254 n=6
Total Time 14.41s (± 0.29%) 14.43s (± 0.34%) ~ 14.38s 14.50s p=0.295 n=6
TFS - node (v18.15.0, x64)
Memory used 302,876k (± 0.01%) 302,864k (± 0.01%) ~ 302,850k 302,892k p=0.298 n=6
Parse Time 2.02s (± 0.70%) 2.01s (± 0.58%) ~ 2.00s 2.03s p=0.161 n=6
Bind Time 1.00s (± 1.03%) 1.00s (± 0.83%) ~ 1.00s 1.02s p=0.788 n=6
Check Time 6.36s (± 0.29%) 6.35s (± 0.18%) ~ 6.33s 6.36s p=0.367 n=6
Emit Time 3.59s (± 0.21%) 3.60s (± 0.27%) ~ 3.59s 3.61s p=0.102 n=6
Total Time 12.97s (± 0.23%) 12.96s (± 0.18%) ~ 12.92s 12.99s p=1.000 n=6
material-ui - node (v18.15.0, x64)
Memory used 511,279k (± 0.01%) 511,286k (± 0.00%) ~ 511,259k 511,314k p=0.471 n=6
Parse Time 2.65s (± 0.67%) 2.67s (± 0.64%) ~ 2.64s 2.69s p=0.104 n=6
Bind Time 1.00s (± 1.05%) 0.99s (± 0.82%) ~ 0.99s 1.01s p=0.720 n=6
Check Time 17.28s (± 0.38%) 17.28s (± 0.59%) ~ 17.14s 17.41s p=1.000 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 20.92s (± 0.37%) 20.94s (± 0.51%) ~ 20.80s 21.08s p=1.000 n=6
mui-docs - node (v18.15.0, x64)
Memory used 2,294,952k (± 0.00%) 2,294,958k (± 0.00%) ~ 2,294,894k 2,294,998k p=0.810 n=6
Parse Time 11.93s (± 0.66%) 11.99s (± 0.44%) ~ 11.93s 12.06s p=0.199 n=6
Bind Time 2.65s (± 0.37%) 2.65s (± 0.60%) ~ 2.62s 2.66s p=0.388 n=6
Check Time 101.92s (± 0.76%) 101.57s (± 0.78%) ~ 100.76s 102.77s p=0.470 n=6
Emit Time 0.32s (± 0.00%) 0.32s (± 0.00%) ~ 0.32s 0.32s p=1.000 n=6
Total Time 116.82s (± 0.63%) 116.53s (± 0.69%) ~ 115.67s 117.70s p=0.688 n=6
self-build-src - node (v18.15.0, x64)
Memory used 2,406,997k (± 0.02%) 2,406,178k (± 0.02%) -820k (- 0.03%) 2,405,356k 2,406,554k p=0.020 n=6
Parse Time 5.09s (± 0.79%) 5.07s (± 1.21%) ~ 5.01s 5.17s p=0.471 n=6
Bind Time 1.87s (± 0.63%) 1.88s (± 0.34%) ~ 1.87s 1.89s p=0.070 n=6
Check Time 33.67s (± 0.23%) 33.63s (± 0.35%) ~ 33.49s 33.77s p=0.470 n=6
Emit Time 2.64s (± 1.88%) 2.70s (± 2.14%) ~ 2.60s 2.76s p=0.109 n=6
Total Time 43.28s (± 0.21%) 43.30s (± 0.29%) ~ 43.10s 43.44s p=0.810 n=6
self-compiler - node (v18.15.0, x64)
Memory used 419,200k (± 0.01%) 419,176k (± 0.01%) ~ 419,124k 419,236k p=0.230 n=6
Parse Time 2.82s (± 0.45%) 2.83s (± 0.88%) ~ 2.81s 2.88s p=0.459 n=6
Bind Time 1.08s (± 0.51%) 1.07s (± 0.38%) ~ 1.07s 1.08s p=0.282 n=6
Check Time 15.25s (± 0.21%) 15.21s (± 0.45%) ~ 15.13s 15.32s p=0.199 n=6
Emit Time 1.14s (± 1.80%) 1.14s (± 1.08%) ~ 1.12s 1.15s p=0.454 n=6
Total Time 20.29s (± 0.27%) 20.24s (± 0.25%) ~ 20.20s 20.33s p=0.295 n=6
vscode - node (v18.15.0, x64)
Memory used 2,849,301k (± 0.00%) 2,849,285k (± 0.00%) ~ 2,849,178k 2,849,346k p=0.226 n=6
Parse Time 10.74s (± 0.21%) 10.77s (± 0.54%) ~ 10.68s 10.82s p=0.332 n=6
Bind Time 3.43s (± 0.24%) 3.44s (± 0.71%) ~ 3.41s 3.48s p=0.243 n=6
Check Time 60.73s (± 0.32%) 60.58s (± 0.14%) ~ 60.48s 60.69s p=0.126 n=6
Emit Time 16.85s (± 7.93%) 16.25s (± 0.19%) ~ 16.21s 16.28s p=0.090 n=6
Total Time 91.75s (± 1.58%) 91.03s (± 0.11%) ~ 90.92s 91.16s p=0.065 n=6
webpack - node (v18.15.0, x64)
Memory used 396,927k (± 0.03%) 396,930k (± 0.01%) ~ 396,875k 396,981k p=0.575 n=6
Parse Time 3.17s (± 1.24%) 3.16s (± 0.95%) ~ 3.13s 3.21s p=0.685 n=6
Bind Time 1.38s (± 1.18%) 1.39s (± 0.59%) ~ 1.38s 1.40s p=0.186 n=6
Check Time 14.06s (± 0.44%) 14.10s (± 0.44%) ~ 14.02s 14.20s p=0.199 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 18.61s (± 0.42%) 18.66s (± 0.44%) ~ 18.58s 18.81s p=0.518 n=6
xstate - node (v18.15.0, x64)
Memory used 513,437k (± 0.01%) 513,434k (± 0.01%) ~ 513,386k 513,514k p=0.689 n=6
Parse Time 3.28s (± 0.36%) 3.27s (± 0.12%) ~ 3.27s 3.28s p=0.087 n=6
Bind Time 1.55s (± 0.53%) 1.54s (± 0.76%) ~ 1.53s 1.56s p=0.401 n=6
Check Time 2.87s (± 0.87%) 2.86s (± 0.58%) ~ 2.84s 2.88s p=0.324 n=6
Emit Time 0.08s (± 0.00%) 0.08s (± 0.00%) ~ 0.08s 0.08s p=1.000 n=6
Total Time 7.78s (± 0.35%) 7.76s (± 0.20%) ~ 7.75s 7.79s p=0.184 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.
Everything looks the same!
You can check the log here.

getObjectFlags(type) & ObjectFlags.Mapped ? getMappedTypeOptionality(type as MappedType) :
0;
}

function getCombinedMappedTypeOptionality(type: MappedType): number {
Copy link
Contributor

Choose a reason for hiding this comment

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

If I'm not mistaken this currently falls short here:

type Obj = {
    a: 1,
    b: 2
};

type Identity<T> = { [K in keyof T]: T[K] };

const mapped: {
    [K in keyof Identity<Partial<Obj>>]: Obj[K]
} = {}

const resolveMapped = <K extends keyof typeof mapped>(key: K) => mapped[key].toString(); // should error but it doesnt

Some extra handling of homomorphic mapped types might have to be added here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, looks like we need to recurse in getCombinedMappedTypeOptionality.

@typescript-bot
Copy link
Collaborator

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

Everything looks good!

@@ -18517,7 +18521,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function substituteIndexedMappedType(objectType: MappedType, index: Type) {
const mapper = createTypeMapper([getTypeParameterFromMappedType(objectType)], [index]);
const templateMapper = combineTypeMappers(objectType.mapper, mapper);
return instantiateType(getTemplateTypeFromMappedType(objectType.target as MappedType || objectType), templateMapper);
const instantiatedTemplateType = instantiateType(getTemplateTypeFromMappedType(objectType.target as MappedType || objectType), templateMapper);
return addOptionality(instantiatedTemplateType, /*isProperty*/ true, getCombinedMappedTypeOptionality(objectType) > 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Taking known properties into consideration would fix this inconsistency that this PR creates:

type Obj = {
  a: string;
  b: number;
};

type Obj2 = {
  b: number;
  c: boolean;
};

declare const mapped: {
  [K in keyof (Partial<Obj> & Required<Obj2>)]: number;
};

// displays the same way as `resolved` below!
mapped;
// ^? const mapped: { a?: number | undefined; b: number; c: number; }

const accessMapped = <K extends keyof Obj2>(key: K) => mapped[key].toString();

declare const resolved: { a?: number | undefined; b: number; c: number };

const accessResolved = <K extends keyof Obj2>(key: K) => resolved[key].toString();

Copy link
Member Author

Choose a reason for hiding this comment

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

Right, when mapping over intersections constructed from multiple mapped types the PR makes the most conservative assumption about optionality. I think that's an acceptable compromise and I'm concerned with the potential cost of examining every member possibly selected by the key.

@@ -14040,10 +14040,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return modifiers & MappedTypeModifiers.ExcludeOptional ? -1 : modifiers & MappedTypeModifiers.IncludeOptional ? 1 : 0;
}

function getModifiersTypeOptionality(type: Type): number {
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand what this is computing. What does a result of 2 mean?

Copy link
Member Author

Choose a reason for hiding this comment

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

The result is always either -1, 0, or 1. -1 means optionality is stripped (i.e. -?), 0 means optionality is unchanged, 1 means optionality is added (i.e. +?). When a homomorphic mapped type doesn't modify optionality, we (recursively) consult the optionality of the type you're mapping over to see if it strips or adds optionality.

Copy link
Member

Choose a reason for hiding this comment

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

Should this be an enum? Then it'd be easier to tell what the numbers mean.

Copy link
Member Author

Choose a reason for hiding this comment

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

It could be an enum, but number feels more appropriate since we use the greater and less than operators to relate the combined values.

Copy link
Member

Choose a reason for hiding this comment

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

Would a comment about what each number represents work?

Copy link
Member

@RyanCavanaugh RyanCavanaugh left a comment

Choose a reason for hiding this comment

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

Behavior looks good

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

Successfully merging this pull request may close these issues.

Regression: Indexed Access of Partial Mapped Type with Union from Generic Function is missing undefined
5 participants