GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — Look for usable type nodes in associated expressions for declaration emit by weswigham · Pull Request #57772 · microsoft/TypeScript Conversation
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (sym) { | ||
| if (isSymbolAccessible(sym, context.enclosingDeclaration, SymbolFlags.All, /*shouldComputeAliasesToMakeVisible*/ false).accessibility !== SymbolAccessibility.Accessible) { | ||
| introducesError = true; | ||
| if (!isDeclarationName(node)) { |
There was a problem 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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| } | ||
| 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); |
There was a problem 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).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| } | ||
|
|
||
| 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)) { |
There was a problem 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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| // 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 |
There was a problem 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).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| : !!(declaration as HasInitializer).initializer | ||
| ? (declaration as HasInitializer & typeof declaration).initializer | ||
| : isParameter(declaration) && isSetAccessor(declaration.parent) | ||
| ? getSingleReturnExpression(getAllAccessorDeclarations(getSymbolOfDeclaration(declaration.parent)?.declarations, declaration.parent).getAccessor) |
There was a problem 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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| * 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) { |
There was a problem 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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
TypeScript Bot (@typescript-bot) pack this |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Starting jobs; this comment will be updated as builds start and complete.
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Hey Daniel Rosenwasser (@DanielRosenwasser), I've packed this into an installable tgz. You can install it for testing by referencing it in your and then running There is also a playground for this build and an npm module you can use via |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Daniel Rosenwasser (@DanielRosenwasser) Here are the results of running the user test suite comparing Everything looks good! |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Daniel Rosenwasser (@DanielRosenwasser) Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
tsserverComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
startupComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Daniel Rosenwasser (@DanielRosenwasser) Here are the results of running the top-repos suite comparing Something interesting changed - please have a look. DetailsServer exited prematurely with code unknown and signal SIGABRTAffected reposcalcom/cal.comRaw 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
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| * 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) { |
There was a problem 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?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Other than my comments (and the current merge conflict) I think this is good. Any other thoughts Ryan Cavanaugh (@RyanCavanaugh) Jake Bailey (@jakebailey) ? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
I think I still feel uneasy about the behavior of parameter properties. Take export class Foo { constructor(public bar?: string) { } } Our JS emit is: var Foo = /** @class */ (function () { function Foo(bar) { this.bar = bar; } return Foo; }());
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 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. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Jake Bailey (@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 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
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. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
(I am giving this PR one last pass now.) |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| /** @param {number} req */ | ||
| methodWithRequiredDefault(p = /** @type {P} */(something), req) {} | ||
|
|
||
| constructor(ctorField = /** @type {P} */(something)) {} |
There was a problem 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?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
(And there's no jsdoc equivalent)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Oh, whoops, didn't notice that.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Unfortunately, we issue |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
[ID-Prep] Preserve type nodes from type assertions in declarations
There was an error while loading. Please reload this page.