-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Preserve string delimiter in type printing. #60729
base: main
Are you sure you want to change the base?
Preserve string delimiter in type printing. #60729
Conversation
We looked at this in the design meeting; the general consensus is that this is good for declaration files, but the fact that error messages are changing quote styles seems to be wrong; we should try and make diagnostic type printing normalize to |
Looks like you're introducing a change to the public API surface area. If this includes breaking changes, please document them on our wiki's API Breaking Changes page. Also, please make sure @DanielRosenwasser and @RyanCavanaugh are aware of the changes, just as a heads up. |
@@ -8,7 +8,7 @@ declare namespace demoNS { | |||
>f : Symbol(f, Decl(demo.d.ts, 0, 26)) | |||
} | |||
declare module 'demoModule' { | |||
>'demoModule' : Symbol("demoModule", Decl(demo.d.ts, 2, 1)) | |||
>'demoModule' : Symbol('demoModule', Decl(demo.d.ts, 2, 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm surprised to see symbol baselines change; were we inconsistent with these before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Symbol printing is involved in type printing and error printing. I applied the 'preserve source code delimiters in everything except errors' to the symbol printing too. This does mean that the delimiters are now preserved in symbol baselines where they previously were not.
Were they always replaced before? Don't think so. Some error messages used symbol printing for the property names and we can see in error baselines changes that some of those kept the '
delimiter instead of having it replaced with "
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I tried doing this in typeWriter.ts
:
let symbolString = "Symbol(" + this.checker.symbolToString(symbol, node.parent, undefined, ts.SymbolFormatFlags.AllowAnyNodeKind | ts.SymbolFormatFlags.UseDoubleQuotesForStringLiteralType);
And it actually still changed a whole bunch of double quotes to single quotes.... Is there some place which is not respecting the flag?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the latest changes, the above seems to actually work, though I don't think I actually think it matters; preserving stuff seems reasonable enough.
tests/baselines/reference/assignmentCompatWithObjectMembersStringNumericNames.errors.txt
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I read through all of the baselines; mostly seems correct, with some odd escaping changes, and I would like someone familiar with those displayParts to weigh in on those changes.
tests/baselines/reference/objectLiteralGettersAndSetters.symbols
Outdated
Show resolved
Hide resolved
tests/baselines/reference/objectTypeWithStringNamedPropertyOfIllegalCharacters.symbols
Outdated
Show resolved
Hide resolved
tests/baselines/reference/stringLiteralPropertyNameWithLineContinuation1.symbols
Outdated
Show resolved
Hide resolved
This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise. |
@@ -8,7 +8,7 @@ declare namespace demoNS { | |||
>f : Symbol(f, Decl(demo.d.ts, 0, 26)) | |||
} | |||
declare module 'demoModule' { | |||
>'demoModule' : Symbol("demoModule", Decl(demo.d.ts, 2, 1)) | |||
>'demoModule' : Symbol('demoModule', Decl(demo.d.ts, 2, 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I tried doing this in typeWriter.ts
:
let symbolString = "Symbol(" + this.checker.symbolToString(symbol, node.parent, undefined, ts.SymbolFormatFlags.AllowAnyNodeKind | ts.SymbolFormatFlags.UseDoubleQuotesForStringLiteralType);
And it actually still changed a whole bunch of double quotes to single quotes.... Is there some place which is not respecting the flag?
The PR seems good to me, outside my comment above about symbol baselines and the weird quoting not changing where I expected it to. |
I did not see places where the delimiter was replaced, I did see places where the delimiters were preserved as in source code. This is because there are still cases where an identifier is created from whatever text was in the computed property. // in symbolToExpression
if (canUsePropertyAccess(symbolName, languageVersion) || (index === 0 && firstChar === CharacterCodes.openBracket)) {
const identifier = setEmitFlags(factory.createIdentifier(symbolName), EmitFlags.NoAsciiEscaping);
if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray<TypeNode | TypeParameterDeclaration>(typeParameterNodes));
identifier.symbol = symbol;
return index > 0 ? factory.createPropertyAccessExpression(createExpressionFromSymbolChain(chain, index - 1), identifier) : identifier;
} Because I am working on changing this, but it's proving a bit trickier than I expected. Will push tomorrow a version that does not just reuse text if |
@@ -79,7 +79,7 @@ export function decl9() {} | |||
|
|||
decl9["🤪"] = 0 | |||
>decl9 : Symbol(decl9, Decl(declarationEmitLateBoundAssignments2.ts, 30, 25), Decl(declarationEmitLateBoundAssignments2.ts, 32, 26)) | |||
>"🤪" : Symbol(decl9["\uD83E\uDD2A"], Decl(declarationEmitLateBoundAssignments2.ts, 32, 26)) | |||
>"🤪" : Symbol(decl9["🤪"], Decl(declarationEmitLateBoundAssignments2.ts, 32, 26)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤪
@@ -8,7 +8,7 @@ declare namespace demoNS { | |||
>f : Symbol(f, Decl(demo.d.ts, 0, 26)) | |||
} | |||
declare module 'demoModule' { | |||
>'demoModule' : Symbol("demoModule", Decl(demo.d.ts, 2, 1)) | |||
>'demoModule' : Symbol('demoModule', Decl(demo.d.ts, 2, 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the latest changes, the above seems to actually work, though I don't think I actually think it matters; preserving stuff seems reasonable enough.
} | ||
} | ||
return symbolToExpression(symbol, context, meaning); | ||
return symbolToExpression(symbol, context, meaning, !!(context.internalFlags & InternalNodeBuilderFlags.WriteComputedProps)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is checking the context flags within symbolToExpression
not sufficient?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
symbolToExpression
is invoked from several places (ex: serializeEntityName
, nodeBuilder.symbolToExpression
, createExpressionFromSymbolChain
). In those places the expected return for symbolToExpression
is always an expression. A computed property is not an expression. So in those cases it should always return an expression regardless of the flag. symbolToNode
already return a generic Node
so you can expect to get anything, including a computed property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mainly meant "does this have to be a parameter if we are already passing in context
"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I guess you're saying that other callers never want the computed prop name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not like pushing this logic into symbolToExpression
and overloading it with a boolean parameter - it ceases to be symbolToExpression
at that point. It really aughta have an explicit Expression
return, because the NodeBuilder
reexposes it as such and we shouldn't be relying on our not-so-strict method assignment rules to ensure it stays that way. I can't see how this is actually required, either - this seems to be the only callsite that passes this flag dynamically; other callers which may or may not want to ensure expression-ness still swap between using symbolToNode
and symbolToExpression
as needed... I would rather the style preservation logic common to the two be factored into a separate function used by both symbolToNode
and symbolToExpression
and leave the layering in-place (so all the logic for building non-expressions continues to live in symbolToNode
).
} | ||
} | ||
return symbolToExpression(symbol, context, meaning); | ||
return symbolToExpression(symbol, context, meaning, !!(context.internalFlags & InternalNodeBuilderFlags.WriteComputedProps)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not like pushing this logic into symbolToExpression
and overloading it with a boolean parameter - it ceases to be symbolToExpression
at that point. It really aughta have an explicit Expression
return, because the NodeBuilder
reexposes it as such and we shouldn't be relying on our not-so-strict method assignment rules to ensure it stays that way. I can't see how this is actually required, either - this seems to be the only callsite that passes this flag dynamically; other callers which may or may not want to ensure expression-ness still swap between using symbolToNode
and symbolToExpression
as needed... I would rather the style preservation logic common to the two be factored into a separate function used by both symbolToNode
and symbolToExpression
and leave the layering in-place (so all the logic for building non-expressions continues to live in symbolToNode
).
stringLiteralName.symbol = symbol; | ||
expression = stringLiteralName; | ||
} | ||
else if (("" + +symbolName) === symbolName && !hasEnumOrSymbolName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably want to use isValidNumberString
to avoid handling a positive or negative Infinity
literal type (which you can get by overflowing) like a number here.
@@ -106,7 +106,7 @@ | |||
}, | |||
{ | |||
"text": "\"jquery\"", | |||
"kind": "stringLiteral" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this means that writeSymbol
call added into the emitter might change how we colorize this quickinfo because of the now attached symbol data in VS (in vscode the quickinfo colorization isn't based on these kinds anymore). Specifically, in the default theme, I think the string's going to be a blue color instead of string color with this? Which seems maybe subjective but less good? I dunno if it matters, and don't actually know if that's still true for VS.
@@ -49,7 +49,7 @@ const e2: E2 = E2.ONE; | |||
|
|||
b1[1] = "a"; | |||
>b1 : Symbol(b1, Decl(numericEnumMappedType.ts, 8, 5)) | |||
>1 : Symbol(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...wat? So all enum reverse mapping symbol names are a computed name of the enum member now? Is that...good...?
Related to #60540
This PR shows what the impact of preserving string delimiters in type printing would be.
cc: @jakebailey @weswigham