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

Look for usable type nodes in associated expressions for declaration emit #57772

Merged

Conversation

weswigham
Copy link
Member

@weswigham weswigham commented Mar 14, 2024

Per our design meeting notes (#57756), to expedite things a bit, this is a version of #57589 that (re)uses the machinery we already have for node reuse within the NodeBuilder in the checker.

Honestly, a worthwhile endeavor, since the test cases from the other PR let me find a bug in the existing node reuse code where we weren't reusing any nodes with a parameter or property in them (oops). The quick fix for that is responsible for most of the test whitespace changes in this PR (reused nodes don't get EmitFlags.SingleLine set, new nodes always do - and if that's a good thing or not is another discussion) - but I could split that short change out, if that was important for comparison (but it'd make two of the examples in tests\cases\compiler\declarationEmitCastReusesTypeNode5.ts not be copied - that's what made me notice the issue).

I've also normalized the new tests a bit, so all the similar ones test all the same variations, in addition to a couple extra variations. Notable differences from the original PR include this working for JS declaration emit, for set accessors, and for annotated required initialized parameters. I also took the opportunity to normalize all the callers of serializeExistingTypeNode (since I was adding another one or two) to perform the same sanity checking on the type (so all callers ensure the type node we're preserving actually resolves to the type we're looking at, and that if we're looking at a type reference, we don't try and reuse a type reference with less than the required number of type arguments (an error that is kind-of allowed in JS, but definitely results in an error if you copy it unmodified into TS)).

Fixes #57587

Copy link
Member Author

@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.

Neglected to post this last night, but I have some explanations for some of the smaller sub-changes here, that are a bit apart from the core change.

@@ -8525,7 +8589,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const sym = resolveEntityName(leftmost, SymbolFlags.All, /*ignoreErrors*/ true, /*dontResolveAlias*/ true);
if (sym) {
if (isSymbolAccessible(sym, context.enclosingDeclaration, SymbolFlags.All, /*shouldComputeAliasesToMakeVisible*/ false).accessibility !== SymbolAccessibility.Accessible) {
introducesError = true;
if (!isDeclarationName(node)) {
Copy link
Member Author

Choose a reason for hiding this comment

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

This single line is the bugfix I mention in the OP - this is sufficient for most cases, but not complete. A (x: whatever) => typeof x node we try to reuse will no longer be rejected on the first x in the parameter list, so we'll use the parameters and signature, but the typeof x will still be rejected, since x isn't actually "in scope" in the scope we're considering. This could be fixed by invoking the same "fake scope" logic we already use in signatureToSignatureDeclarationHelper for wholly synthetic signatures to stack up the signature scopes we're trying to preserve as we descend, but since that doesn't seem to be relevant for any tests here, I've left that for future work for now, since it's a slight refactor to extract the "fake scope" helper functions into the outer scope.

@@ -8664,6 +8734,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
node.isTypeOf,
);
}
if (isParameter(node)) {
if (!node.type && !node.initializer) {
return factory.updateParameterDeclaration(node, /*modifiers*/ undefined, node.dotDotDotToken, visitEachChild(node.name, visitExistingNodeTreeSymbols, /*context*/ undefined), node.questionToken, factory.createKeywordTypeNode(SyntaxKind.AnyKeyword), /*initializer*/ undefined);
Copy link
Member Author

Choose a reason for hiding this comment

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

This was kinda of just a drive-by fix - when looking at the whitespace changes, I noticed that some retained nodes in types baselines were missing : any because the input didn't have them. This probably never comes up in real code, since declaration emit doesn't trigger if the input code has errors, and basically everyone (our tests included) has noImplicitAny on. Still, .types baselines are made (and error messages!) - so this should (and does) help those cases look more complete (not that type portability across compiler settings matters for them).

@@ -8673,7 +8753,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}

if (file && isTupleTypeNode(node) && (getLineAndCharacterOfPosition(file, node.pos).line === getLineAndCharacterOfPosition(file, node.end).line)) {
if (file && isTupleTypeNode(node) && !nodeIsSynthesized(node) && (getLineAndCharacterOfPosition(file, node.pos).line === getLineAndCharacterOfPosition(file, node.end).line)) {
Copy link
Member Author

Choose a reason for hiding this comment

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

I toyed with expanding this make-tuples-single-line-if-the-input-was-single-line to all object types, which works, but affects a lot of baselines. And it just made me notice the other cases where it wasn't retained even more, because the declaration emitter, when it keeps around an existing node, also doesn't set SingleLine. In the end, I decided messing with the whitespace was a bit too far out of scope, so left that for another day. But what I kept was this nodeIsSynthesized check - getLineAndCharacterOfPosition asserts if it gets a -1 for a position. When I was doing it for more nodes, I found this can crash, because the nodes may already have been transformed. I could put together a test for said crash, now that I know it's there, but I'm not sure what passes in a node with synthetic positions into this function - maybe something via a quickfix? The fix is just this, at least.

@@ -9956,6 +10036,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// whose input is not type annotated (if the input symbol has an annotation we can reuse, we should prefer it)
const ctxSrc = getSourceFileOfNode(context.enclosingDeclaration);
return getObjectFlags(typeToSerialize) & (ObjectFlags.Anonymous | ObjectFlags.Mapped) &&
!some(typeToSerialize.symbol?.declarations, isTypeNode) && // If the type comes straight from a type node, we shouldn't try to break it up
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 JS declaration emit example is, apparently, the first time we've printed multiple variables of the same type where that type is defined in the current file in js declaration emit tests. It revealed that we tried to print them all as a namespace, but all the namespaces after the first would have no members, since they already got printed. That's silly. The namespace output mode for JS declaration emit is really supposed to be for things merged together across a lot of definitions - we can be pretty sure that if the type comes from a type node (as opposed to, say a binary expression or property access expression), we shouldn't convert it to a namespace (since, like, just writing a type node should be able to perfectly capture a type from a type node).

: !!(declaration as HasInitializer).initializer
? (declaration as HasInitializer & typeof declaration).initializer
: isParameter(declaration) && isSetAccessor(declaration.parent)
? getSingleReturnExpression(getAllAccessorDeclarations(getSymbolOfDeclaration(declaration.parent)?.declarations, declaration.parent).getAccessor)
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 accessor case in the original PR not working made me sad, so I added this line and made it work. We're smart and can look at multiple places for the potential annotation, after all.

@@ -8460,17 +8525,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
* Unlike `typeToTypeNodeHelper`, this handles setting up the `AllowUniqueESSymbolType` flag
* so a `unique symbol` is returned when appropriate for the input symbol, rather than `typeof sym`
*/
function serializeTypeForDeclaration(context: NodeBuilderContext, type: Type, symbol: Symbol, enclosingDeclaration: Node | undefined, includePrivateSymbol?: (s: Symbol) => void, bundled?: boolean) {
function serializeTypeForDeclaration(context: NodeBuilderContext, type: Type, symbol: Symbol, enclosingDeclaration: Node | undefined, includePrivateSymbol?: (s: Symbol) => void, bundled?: boolean, addUndefined?: boolean) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Honestly, we can probably delete the addUndefined parameter with a bit more work - this function can rederive the same thing with the arguments it's given. Given that, since these functions do attempt top-level node reuse now, it'd also be nice to just ditch requiresAddingImplicitUndefined from the EmitResolver entirely, and let the resolver's internals figure it out when you call serializeTypeForDeclaration via createTypeOfDeclaration. I actually tried this for a second, and it seems like the only major blocker is that createReturnTypeOfSignatureDeclaration actually isn't what we want - we need it to return type predicate nodes, too! You can't notice the issue today, since all predicates have to be annotated, so they get preserved in the declaration emitter, but #57465 will change this! It's very likely the structure of that code will change a bit in response to that PR, and then maybe removing addUndefined and requiresAddingImplicitUndefined will feel more easily done.

Copy link
Member

Choose a reason for hiding this comment

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

but #57465 will change this

Would it be good to proactively add a test case for that?

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 adds its' own tests for it and since you can't cast into a type predicate like null! as x is number, I don't think this PR has much to add on top of that. But it should still make the refactor I mentioned above easier to do - though I don't really want to do it here, since it'll probably require fixing some more discrepancies between the declaration emitter's existing type node reuse logic and the node builder's inner type node reuse logic, as while the later is generalized to apply at the top level (using serializeTypeForDeclaration on the top-level type node if present), I've not yet done a comparison to see if the behaviors are identical (though it's already in use for JS declarations, ofc).

tests/baselines/reference/optionalMethods.js Outdated Show resolved Hide resolved
@DanielRosenwasser
Copy link
Member

@typescript-bot pack this
@typescript-bot user test tsserver
@typescript-bot test tsserver top200
@typescript-bot perf test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 14, 2024

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

Command Status Results
pack this ✅ Started ✅ Results
user test tsserver ✅ Started ✅ Results
test tsserver top200 ✅ Started 👀 Results
perf test this ✅ Started 👀 Results

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 14, 2024

Hey @DanielRosenwasser, 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/160375/artifacts?artifactName=tgz&fileId=8DC261A07C381925E09DAFD3EC14E5AB5D52A38F5F18CFF20E5CB0CBE9A6ABA202&fileName=/typescript-5.5.0-insiders.20240314.tgz"
    }
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@5.5.0-pr-57772-2".;

@typescript-bot
Copy link
Collaborator

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

Everything looks good!

@typescript-bot
Copy link
Collaborator

@DanielRosenwasser
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,636k (± 0.01%) 295,656k (± 0.01%) ~ 295,632k 295,703k p=0.149 n=6
Parse Time 2.66s (± 0.28%) 2.66s (± 0.44%) ~ 2.64s 2.67s p=0.933 n=6
Bind Time 0.82s (± 0.66%) 0.83s (± 0.62%) ~ 0.82s 0.83s p=0.640 n=6
Check Time 8.21s (± 0.38%) 8.21s (± 0.34%) ~ 8.17s 8.24s p=0.622 n=6
Emit Time 7.14s (± 0.41%) 7.17s (± 0.42%) ~ 7.13s 7.21s p=0.225 n=6
Total Time 18.83s (± 0.29%) 18.86s (± 0.18%) ~ 18.81s 18.91s p=0.171 n=6
Compiler-Unions - node (v18.15.0, x64)
Memory used 193,298k (± 0.94%) 192,676k (± 0.93%) ~ 191,378k 195,045k p=0.575 n=6
Parse Time 1.36s (± 1.95%) 1.35s (± 0.60%) ~ 1.34s 1.36s p=0.324 n=6
Bind Time 0.72s (± 0.00%) 0.72s (± 0.00%) ~ 0.72s 0.72s p=1.000 n=6
Check Time 9.29s (± 0.34%) 9.30s (± 0.37%) ~ 9.25s 9.34s p=0.809 n=6
Emit Time 2.59s (± 0.49%) 2.60s (± 0.47%) ~ 2.59s 2.62s p=0.103 n=6
Total Time 13.97s (± 0.30%) 13.97s (± 0.23%) ~ 13.94s 14.02s p=1.000 n=6
Monaco - node (v18.15.0, x64)
Memory used 347,368k (± 0.01%) 347,367k (± 0.01%) ~ 347,314k 347,391k p=1.000 n=6
Parse Time 2.48s (± 0.42%) 2.47s (± 0.65%) ~ 2.44s 2.48s p=0.082 n=6
Bind Time 0.93s (± 0.56%) 0.92s (± 0.56%) ~ 0.92s 0.93s p=0.311 n=6
Check Time 6.95s (± 0.26%) 6.93s (± 0.30%) ~ 6.90s 6.96s p=0.168 n=6
Emit Time 4.08s (± 0.35%) 4.07s (± 0.43%) ~ 4.04s 4.09s p=0.677 n=6
Total Time 14.44s (± 0.17%) 14.40s (± 0.34%) ~ 14.31s 14.45s p=0.120 n=6
TFS - node (v18.15.0, x64)
Memory used 302,797k (± 0.01%) 302,805k (± 0.01%) ~ 302,779k 302,856k p=0.336 n=6
Parse Time 2.00s (± 0.77%) 2.00s (± 1.12%) ~ 1.98s 2.04s p=0.807 n=6
Bind Time 1.01s (± 0.63%) 1.01s (± 1.32%) ~ 0.99s 1.02s p=1.000 n=6
Check Time 6.31s (± 0.33%) 6.34s (± 0.41%) ~ 6.32s 6.39s p=0.054 n=6
Emit Time 3.60s (± 0.38%) 3.61s (± 0.34%) ~ 3.60s 3.63s p=0.155 n=6
Total Time 12.93s (± 0.21%) 12.97s (± 0.39%) ~ 12.92s 13.06s p=0.108 n=6
material-ui - node (v18.15.0, x64)
Memory used 511,261k (± 0.00%) 511,251k (± 0.01%) ~ 511,223k 511,284k p=0.688 n=6
Parse Time 2.65s (± 0.46%) 2.65s (± 0.48%) ~ 2.63s 2.66s p=0.654 n=6
Bind Time 0.98s (± 1.00%) 0.98s (± 0.85%) ~ 0.97s 0.99s p=0.588 n=6
Check Time 17.30s (± 0.18%) 17.32s (± 0.31%) ~ 17.27s 17.40s p=0.574 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 20.94s (± 0.19%) 20.96s (± 0.20%) ~ 20.91s 21.01s p=0.419 n=6
mui-docs - node (v18.15.0, x64)
Memory used 1,797,827k (± 0.00%) 1,797,831k (± 0.00%) ~ 1,797,806k 1,797,860k p=0.873 n=6
Parse Time 6.62s (± 0.35%) 6.61s (± 0.38%) ~ 6.59s 6.66s p=0.517 n=6
Bind Time 2.38s (± 0.59%) 2.38s (± 0.32%) ~ 2.37s 2.39s p=0.867 n=6
Check Time 59.14s (± 0.54%) 59.07s (± 0.41%) ~ 58.75s 59.46s p=0.873 n=6
Emit Time 0.16s (± 2.52%) 0.17s (± 3.32%) ~ 0.16s 0.17s p=0.282 n=6
Total Time 68.30s (± 0.47%) 68.23s (± 0.34%) ~ 67.93s 68.62s p=0.810 n=6
self-build-src - node (v18.15.0, x64)
Memory used 2,395,458k (± 0.04%) 2,396,659k (± 0.02%) +1,201k (+ 0.05%) 2,396,132k 2,397,329k p=0.045 n=6
Parse Time 5.01s (± 1.05%) 5.02s (± 0.78%) ~ 4.97s 5.08s p=0.810 n=6
Bind Time 1.90s (± 1.14%) 1.90s (± 0.87%) ~ 1.88s 1.92s p=1.000 n=6
Check Time 33.61s (± 0.34%) 33.61s (± 0.45%) ~ 33.38s 33.83s p=1.000 n=6
Emit Time 2.68s (± 1.43%) 2.70s (± 1.21%) ~ 2.66s 2.74s p=0.422 n=6
Total Time 43.23s (± 0.36%) 43.25s (± 0.42%) ~ 43.00s 43.54s p=0.689 n=6
self-compiler - node (v18.15.0, x64)
Memory used 414,789k (± 0.01%) 415,550k (± 0.01%) +761k (+ 0.18%) 415,489k 415,597k p=0.005 n=6
Parse Time 2.82s (± 1.13%) 2.81s (± 0.82%) ~ 2.77s 2.84s p=0.511 n=6
Bind Time 1.06s (± 0.84%) 1.07s (± 0.38%) ~ 1.06s 1.07s p=0.086 n=6
Check Time 15.17s (± 0.45%) 15.21s (± 0.37%) ~ 15.13s 15.26s p=0.376 n=6
Emit Time 1.10s (± 1.60%) 1.11s (± 0.95%) ~ 1.09s 1.12s p=0.413 n=6
Total Time 20.14s (± 0.38%) 20.19s (± 0.26%) ~ 20.12s 20.24s p=0.334 n=6
vscode - node (v18.15.0, x64)
Memory used 2,876,397k (± 0.00%) 2,876,397k (± 0.00%) ~ 2,876,202k 2,876,489k p=1.000 n=6
Parse Time 10.78s (± 0.23%) 10.78s (± 0.14%) ~ 10.76s 10.80s p=0.935 n=6
Bind Time 3.45s (± 0.35%) 3.45s (± 0.59%) ~ 3.42s 3.48s p=0.505 n=6
Check Time 61.19s (± 0.35%) 61.13s (± 0.40%) ~ 60.78s 61.51s p=0.471 n=6
Emit Time 16.45s (± 0.28%) 17.10s (± 8.08%) ~ 16.43s 19.92s p=0.090 n=6
Total Time 91.87s (± 0.23%) 92.46s (± 1.71%) ~ 91.47s 95.67s p=0.936 n=6
webpack - node (v18.15.0, x64)
Memory used 406,144k (± 0.01%) 406,154k (± 0.01%) ~ 406,075k 406,225k p=0.575 n=6
Parse Time 3.24s (± 1.49%) 3.21s (± 1.29%) ~ 3.16s 3.26s p=0.171 n=6
Bind Time 1.40s (± 1.25%) 1.39s (± 1.34%) ~ 1.37s 1.42s p=0.461 n=6
Check Time 14.05s (± 0.22%) 14.12s (± 0.22%) +0.07s (+ 0.52%) 14.08s 14.15s p=0.010 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 18.70s (± 0.41%) 18.72s (± 0.36%) ~ 18.62s 18.80s p=0.689 n=6
xstate - node (v18.15.0, x64)
Memory used 513,185k (± 0.01%) 513,239k (± 0.01%) ~ 513,194k 513,291k p=0.128 n=6
Parse Time 3.27s (± 0.23%) 3.27s (± 0.19%) ~ 3.26s 3.28s p=0.718 n=6
Bind Time 1.54s (± 0.67%) 1.54s (± 0.34%) ~ 1.53s 1.54s p=0.242 n=6
Check Time 2.85s (± 0.36%) 2.86s (± 0.83%) ~ 2.84s 2.89s p=1.000 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.28%) 7.75s (± 0.39%) ~ 7.72s 7.79s 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

tsserver

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-UnionsTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,349ms (± 1.00%) 2,354ms (± 0.66%) ~ 2,335ms 2,372ms p=0.574 n=6
Req 2 - geterr 5,546ms (± 1.53%) 5,459ms (± 0.45%) ~ 5,427ms 5,487ms p=0.054 n=6
Req 3 - references 324ms (± 0.57%) 322ms (± 0.87%) ~ 319ms 326ms p=0.212 n=6
Req 4 - navto 275ms (± 0.82%) 277ms (± 0.62%) +3ms (+ 0.97%) 275ms 279ms p=0.035 n=6
Req 5 - completionInfo count 1,357 (± 0.00%) 1,357 (± 0.00%) ~ 1,357 1,357 p=1.000 n=6
Req 5 - completionInfo 89ms (± 6.00%) 82ms (± 3.60%) 🟩-7ms (- 7.72%) 79ms 85ms p=0.033 n=6
CompilerTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,509ms (± 0.71%) 2,503ms (± 1.10%) ~ 2,458ms 2,530ms p=0.689 n=6
Req 2 - geterr 4,125ms (± 0.34%) 4,111ms (± 0.42%) ~ 4,087ms 4,128ms p=0.377 n=6
Req 3 - references 337ms (± 1.28%) 335ms (± 0.98%) ~ 332ms 341ms p=0.241 n=6
Req 4 - navto 286ms (± 0.18%) 286ms (± 0.26%) ~ 285ms 287ms p=0.241 n=6
Req 5 - completionInfo count 1,519 (± 0.00%) 1,519 (± 0.00%) ~ 1,519 1,519 p=1.000 n=6
Req 5 - completionInfo 89ms (± 0.84%) 89ms (± 0.00%) ~ 89ms 89ms p=0.598 n=6
xstateTSServer - node (v18.15.0, x64)
Req 1 - updateOpen 2,605ms (± 0.38%) 2,595ms (± 0.89%) ~ 2,559ms 2,617ms p=0.575 n=6
Req 2 - geterr 1,755ms (± 2.61%) 1,719ms (± 2.36%) ~ 1,668ms 1,779ms p=0.230 n=6
Req 3 - references 117ms (± 9.89%) 120ms (± 8.12%) ~ 107ms 128ms p=0.629 n=6
Req 4 - navto 369ms (± 0.68%) 369ms (± 1.17%) ~ 362ms 375ms p=0.870 n=6
Req 5 - completionInfo count 2,079 (± 0.00%) 2,079 (± 0.00%) ~ 2,079 2,079 p=1.000 n=6
Req 5 - completionInfo 307ms (± 1.53%) 309ms (± 1.23%) ~ 306ms 315ms p=0.195 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • CompilerTSServer - node (v18.15.0, x64)
  • Compiler-UnionsTSServer - node (v18.15.0, x64)
  • xstateTSServer - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

startup

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
tsc-startup - node (v18.15.0, x64)
Execution time 153.18ms (± 0.15%) 153.35ms (± 0.18%) +0.18ms (+ 0.11%) 152.17ms 155.22ms p=0.000 n=600
tsserver-startup - node (v18.15.0, x64)
Execution time 230.16ms (± 0.15%) 230.23ms (± 0.15%) ~ 228.67ms 233.20ms p=0.065 n=600
tsserverlibrary-startup - node (v18.15.0, x64)
Execution time 220.65ms (± 0.17%) 220.81ms (± 0.49%) +0.16ms (+ 0.07%) 219.21ms 269.23ms p=0.002 n=600
typescript-startup - node (v18.15.0, x64)
Execution time 221.60ms (± 0.15%) 221.53ms (± 0.14%) ~ 220.00ms 225.37ms p=0.068 n=600
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • tsc-startup - node (v18.15.0, x64)
  • tsserver-startup - node (v18.15.0, x64)
  • tsserverlibrary-startup - node (v18.15.0, x64)
  • typescript-startup - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

@typescript-bot
Copy link
Collaborator

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

Something interesting changed - please have a look.

Details

Server exited prematurely with code unknown and signal SIGABRT

Server exited prematurely with code unknown and signal SIGABRT

Affected repos

calcom/cal.com Raw error text: RepoResults7/calcom.cal.com.rawError.txt in the artifact folder

Last few requests

{"seq":968,"type":"request","command":"getOutliningSpans","arguments":{"file":"@PROJECT_ROOT@/packages/app-store/utils.ts"}}
{"seq":969,"type":"request","command":"navtree","arguments":{"file":"@PROJECT_ROOT@/packages/app-store/utils.ts"}}
{"seq":970,"type":"request","command":"navbar","arguments":{"file":"@PROJECT_ROOT@/packages/app-store/utils.ts"}}
{"seq":971,"type":"request","command":"navto","arguments":{"searchValue":"a","maxResultCount":256}}

Repro steps

  1. git clone https://github.com/calcom/cal.com --recurse-submodules
  2. In dir cal.com, run git reset --hard 71b0dd2aa36095b19ee56c7c13589e21ed915ad7
  3. In dir cal.com, run yarn install --no-immutable --mode=skip-build
  4. Back in the initial folder, download RepoResults7/calcom.cal.com.replay.txt from the artifact folder
  5. npm install --no-save @typescript/server-replay
  6. npx tsreplay ./cal.com ./calcom.cal.com.replay.txt path/to/tsserver.js
  7. npx tsreplay --help to learn about helpful switches for debugging, logging, etc

Copy link
Member

@DanielRosenwasser DanielRosenwasser left a comment

Choose a reason for hiding this comment

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

Seems reasonable - the naming is a little interesting for expressionOrTypeToTypeNode, but we could rename in the future.

@@ -8460,17 +8525,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
* Unlike `typeToTypeNodeHelper`, this handles setting up the `AllowUniqueESSymbolType` flag
* so a `unique symbol` is returned when appropriate for the input symbol, rather than `typeof sym`
*/
function serializeTypeForDeclaration(context: NodeBuilderContext, type: Type, symbol: Symbol, enclosingDeclaration: Node | undefined, includePrivateSymbol?: (s: Symbol) => void, bundled?: boolean) {
function serializeTypeForDeclaration(context: NodeBuilderContext, type: Type, symbol: Symbol, enclosingDeclaration: Node | undefined, includePrivateSymbol?: (s: Symbol) => void, bundled?: boolean, addUndefined?: boolean) {
Copy link
Member

Choose a reason for hiding this comment

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

but #57465 will change this

Would it be good to proactively add a test case for that?

src/compiler/checker.ts Show resolved Hide resolved
src/compiler/checker.ts Outdated Show resolved Hide resolved
src/compiler/checker.ts Show resolved Hide resolved
@DanielRosenwasser
Copy link
Member

Other than my comments (and the current merge conflict) I think this is good. Any other thoughts @RyanCavanaugh @jakebailey ?

@jakebailey
Copy link
Member

jakebailey commented Mar 15, 2024

I think I still feel uneasy about the behavior of parameter properties. Take tests/cases/compiler/declarationEmitParameterProperty.ts, which has:

export class Foo {
  constructor(public bar?: string) {
  }
}

Our JS emit is:

var Foo = /** @class */ (function () {
    function Foo(bar) {
        this.bar = bar;
    }
    return Foo;
}());

bar is always assigned. Right now, the declaration file we emit is:

export declare class Foo {
    bar?: string | undefined;
    constructor(bar?: string | undefined);
}

But after this PR, it's:

export declare class Foo {
    bar?: string;
    constructor(bar?: string);
}

Consumers may read this as "the class will either have string, or the prop won't even exist".

I feel like maybe I'm not understanding #57772 (comment) or something, because this behavior really does look like a regression, and one of the ones that I pointed out in #53463.

@weswigham
Copy link
Member Author

@jakebailey I've brought back the old emit for optional parameter properties - we can look into changing it later. It feels like they don't really play well with exactOptionalPropertyTypes as they are today.

@jakebailey
Copy link
Member

@jakebailey I've brought back the old emit for optional parameter properties - we can look into changing it later. It feels like they don't really play well with exactOptionalPropertyTypes as they are today.

Thanks; I do wonder if we could simply preserve parameter properties in declaration emit, eliminating the need to rewrite the properties and making other emitters easier to write (just leave it as-is). But, not in this PR.

@jakebailey
Copy link
Member

(I am giving this PR one last pass now.)

/** @param {number} req */
methodWithRequiredDefault(p = /** @type {P} */(something), req) {}

constructor(ctorField = /** @type {P} */(something)) {}
Copy link
Member

Choose a reason for hiding this comment

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

This looks like a typo; should be public ctorField =..., right?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is javascript, so no - since parameter properties are a syntax error in JS files.

Copy link
Member Author

Choose a reason for hiding this comment

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

(And there's no jsdoc equivalent)

Copy link
Member

Choose a reason for hiding this comment

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

Oh, whoops, didn't notice that.

@weswigham
Copy link
Member Author

I do wonder if we could simply preserve parameter properties in declaration emit, eliminating the need to rewrite the properties and making other emitters easier to write (just leave it as-is).

Unfortunately, we issue A parameter property is only allowed in a constructor implementation. when we read a parameter property from a constructor in a declaration file. So such a change would be a backwards incompatible change to declaration emit (though we admittedly still check it correctly, despite the error, so maybe not the worst break in emit in the world).

@weswigham weswigham merged commit 66047f7 into microsoft:main Mar 18, 2024
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Backlog Bug PRs that fix a backlog bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[ID-Prep] Preserve type nodes from type assertions in declarations
4 participants