-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Eliminate well known symbols as a concept in the checker and rely on unique symbols #42543
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
Changes from all commits
31dc466
a3fc74e
bd49b81
4c808c4
ebac382
b954507
e99c05a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -898,6 +898,7 @@ namespace ts { | |
// This allows users to just specify library files they want to used through --lib | ||
// and they will not get an error from not having unrelated library files | ||
let deferredGlobalESSymbolConstructorSymbol: Symbol | undefined; | ||
let deferredGlobalESSymbolConstructorTypeSymbol: Symbol | undefined; | ||
let deferredGlobalESSymbolType: ObjectType; | ||
let deferredGlobalTypedPropertyDescriptorType: GenericType; | ||
let deferredGlobalPromiseType: GenericType; | ||
|
@@ -3677,11 +3678,30 @@ namespace ts { | |
const additionalContainers = mapDefined(container.declarations, fileSymbolIfFileSymbolExportEqualsContainer); | ||
const reexportContainers = enclosingDeclaration && getAlternativeContainingModules(symbol, enclosingDeclaration); | ||
const objectLiteralContainer = getVariableDeclarationOfObjectLiteral(container, meaning); | ||
if (enclosingDeclaration && getAccessibleSymbolChain(container, enclosingDeclaration, SymbolFlags.Namespace, /*externalOnly*/ false)) { | ||
if ( | ||
enclosingDeclaration && | ||
container.flags & getQualifiedLeftMeaning(meaning) && | ||
getAccessibleSymbolChain(container, enclosingDeclaration, SymbolFlags.Namespace, /*externalOnly*/ false) | ||
weswigham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) { | ||
weswigham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return append(concatenate(concatenate([container], additionalContainers), reexportContainers), objectLiteralContainer); // This order expresses a preference for the real container if it is in scope | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll call this out since it's a new bit compared to the older PR. This bit and the following modify our symbol container lookup to prefer, eg, |
||
} | ||
const res = append(append(additionalContainers, container), objectLiteralContainer); | ||
return concatenate(res, reexportContainers); | ||
// we potentially have a symbol which is a member of the instance side of something - look for a variable in scope with the container's type | ||
// which may be acting like a namespace (eg, `Symbol` acts like a namespace when looking up `Symbol.toStringTag`) | ||
const firstVariableMatch = !(container.flags & getQualifiedLeftMeaning(meaning)) | ||
&& container.flags & SymbolFlags.Type | ||
&& getDeclaredTypeOfSymbol(container).flags & TypeFlags.Object | ||
&& meaning === SymbolFlags.Value | ||
? forEachSymbolTableInScope(enclosingDeclaration, t => { | ||
return forEachEntry(t, s => { | ||
if (s.flags & getQualifiedLeftMeaning(meaning) && getTypeOfSymbol(s) === getDeclaredTypeOfSymbol(container)) { | ||
return s; | ||
} | ||
}); | ||
weswigham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}) : undefined; | ||
let res = firstVariableMatch ? [firstVariableMatch, ...additionalContainers, container] : [...additionalContainers, container]; | ||
res = append(res, objectLiteralContainer); | ||
res = addRange(res, reexportContainers); | ||
return res; | ||
} | ||
const candidates = mapDefined(symbol.declarations, d => { | ||
if (!isAmbientModule(d) && d.parent && hasNonGlobalAugmentationExternalModuleSymbol(d.parent)) { | ||
|
@@ -5107,8 +5127,9 @@ namespace ts { | |
} | ||
} | ||
} | ||
context.enclosingDeclaration = saveEnclosingDeclaration; | ||
context.enclosingDeclaration = propertySymbol.valueDeclaration || propertySymbol.declarations?.[0] || saveEnclosingDeclaration; | ||
const propertyName = getPropertyNameNodeForSymbol(propertySymbol, context); | ||
context.enclosingDeclaration = saveEnclosingDeclaration; | ||
context.approximateLength += (symbolName(propertySymbol).length + 1); | ||
const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? factory.createToken(SyntaxKind.QuestionToken) : undefined; | ||
if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length && !isReadonlySymbol(propertySymbol)) { | ||
|
@@ -5852,9 +5873,6 @@ namespace ts { | |
if (fromNameType) { | ||
return fromNameType; | ||
} | ||
if (isKnownSymbol(symbol)) { | ||
return factory.createComputedPropertyName(factory.createPropertyAccessExpression(factory.createIdentifier("Symbol"), (symbol.escapedName as string).substr(3))); | ||
} | ||
const rawName = unescapeLeadingUnderscores(symbol.escapedName); | ||
const stringNamed = !!length(symbol.declarations) && every(symbol.declarations, isStringNamed); | ||
return createPropertyNameNodeForIdentifierOrLiteral(rawName, stringNamed, singleQuote); | ||
|
@@ -8707,8 +8725,18 @@ namespace ts { | |
return widenTypeForVariableLikeDeclaration(getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true), declaration, reportErrors); | ||
} | ||
|
||
function isGlobalSymbolConstructor(node: Node) { | ||
weswigham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const symbol = getSymbolOfNode(node); | ||
const globalSymbol = getGlobalESSymbolConstructorTypeSymbol(/*reportErrors*/ false); | ||
return globalSymbol && symbol && symbol === globalSymbol; | ||
} | ||
|
||
function widenTypeForVariableLikeDeclaration(type: Type | undefined, declaration: any, reportErrors?: boolean) { | ||
if (type) { | ||
// TODO: If back compat with pre-3.0/4.0 libs isn't required, remove the following SymbolConstructor special case transforming `symbol` into `unique symbol` | ||
if (type.flags & TypeFlags.ESSymbol && isGlobalSymbolConstructor(declaration.parent)) { | ||
type = getESSymbolLikeTypeForNode(declaration); | ||
} | ||
if (reportErrors) { | ||
reportErrorsFromWidening(declaration, type); | ||
} | ||
|
@@ -12859,6 +12887,10 @@ namespace ts { | |
return deferredGlobalESSymbolConstructorSymbol || (deferredGlobalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol" as __String, reportErrors)); | ||
} | ||
|
||
function getGlobalESSymbolConstructorTypeSymbol(reportErrors: boolean) { | ||
return deferredGlobalESSymbolConstructorTypeSymbol || (deferredGlobalESSymbolConstructorTypeSymbol = getGlobalTypeSymbol("SymbolConstructor" as __String, reportErrors)); | ||
} | ||
|
||
function getGlobalESSymbolType(reportErrors: boolean) { | ||
return deferredGlobalESSymbolType || (deferredGlobalESSymbolType = getGlobalType("Symbol" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType; | ||
} | ||
|
@@ -13941,13 +13973,13 @@ namespace ts { | |
function getLiteralTypeFromProperty(prop: Symbol, include: TypeFlags) { | ||
if (!(getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier)) { | ||
let type = getSymbolLinks(getLateBoundSymbol(prop)).nameType; | ||
if (!type && !isKnownSymbol(prop)) { | ||
if (!type) { | ||
if (prop.escapedName === InternalSymbolName.Default) { | ||
type = getLiteralType("default"); | ||
} | ||
else { | ||
const name = prop.valueDeclaration && getNameOfDeclaration(prop.valueDeclaration) as PropertyName; | ||
type = name && getLiteralTypeFromPropertyName(name) || getLiteralType(symbolName(prop)); | ||
type = name && getLiteralTypeFromPropertyName(name) || (!isKnownSymbol(prop) ? getLiteralType(symbolName(prop)) : undefined); | ||
} | ||
} | ||
if (type && type.flags & include) { | ||
|
@@ -14174,11 +14206,8 @@ namespace ts { | |
} | ||
|
||
function getPropertyNameFromIndex(indexType: Type, accessNode: StringLiteral | Identifier | PrivateIdentifier | ObjectBindingPattern | ArrayBindingPattern | ComputedPropertyName | NumericLiteral | IndexedAccessTypeNode | ElementAccessExpression | SyntheticExpression | undefined) { | ||
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined; | ||
return isTypeUsableAsPropertyName(indexType) ? | ||
getPropertyNameFromType(indexType) : | ||
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ? | ||
getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>accessExpression.argumentExpression).name)) : | ||
accessNode && isPropertyName(accessNode) ? | ||
// late bound names are handled in the first branch, so here we only need to handle normal names | ||
getPropertyNameForPropertyNameNode(accessNode) : | ||
|
@@ -25279,9 +25308,6 @@ namespace ts { | |
!isTypeAssignableTo(links.resolvedType, stringNumberSymbolType)) { | ||
error(node, Diagnostics.A_computed_property_name_must_be_of_type_string_number_symbol_or_any); | ||
} | ||
else { | ||
checkThatExpressionIsProperSymbolReference(node.expression, links.resolvedType, /*reportError*/ true); | ||
} | ||
} | ||
|
||
return links.resolvedType; | ||
|
@@ -25342,15 +25368,15 @@ namespace ts { | |
// As otherwise they may not be checked until exports for the type at this position are retrieved, | ||
// which may never occur. | ||
for (const elem of node.properties) { | ||
if (elem.name && isComputedPropertyName(elem.name) && !isWellKnownSymbolSyntactically(elem.name)) { | ||
if (elem.name && isComputedPropertyName(elem.name)) { | ||
checkComputedPropertyName(elem.name); | ||
} | ||
} | ||
|
||
let offset = 0; | ||
for (const memberDecl of node.properties) { | ||
let member = getSymbolOfNode(memberDecl); | ||
const computedNameType = memberDecl.name && memberDecl.name.kind === SyntaxKind.ComputedPropertyName && !isWellKnownSymbolSyntactically(memberDecl.name.expression) ? | ||
const computedNameType = memberDecl.name && memberDecl.name.kind === SyntaxKind.ComputedPropertyName ? | ||
checkComputedPropertyName(memberDecl.name) : undefined; | ||
if (memberDecl.kind === SyntaxKind.PropertyAssignment || | ||
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment || | ||
|
@@ -27006,48 +27032,6 @@ namespace ts { | |
return checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, indexedAccessType.symbol, indexedAccessType, indexExpression), node); | ||
} | ||
|
||
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean { | ||
if (expressionType === errorType) { | ||
// There is already an error, so no need to report one. | ||
return false; | ||
} | ||
|
||
if (!isWellKnownSymbolSyntactically(expression)) { | ||
return false; | ||
} | ||
|
||
// Make sure the property type is the primitive symbol type | ||
if ((expressionType.flags & TypeFlags.ESSymbolLike) === 0) { | ||
if (reportError) { | ||
error(expression, Diagnostics.A_computed_property_name_of_the_form_0_must_be_of_type_symbol, getTextOfNode(expression)); | ||
} | ||
return false; | ||
} | ||
|
||
// The name is Symbol.<someName>, so make sure Symbol actually resolves to the | ||
// global Symbol object | ||
const leftHandSide = <Identifier>(<PropertyAccessExpression>expression).expression; | ||
const leftHandSideSymbol = getResolvedSymbol(leftHandSide); | ||
if (!leftHandSideSymbol) { | ||
return false; | ||
} | ||
|
||
const globalESSymbol = getGlobalESSymbolConstructorSymbol(/*reportErrors*/ true); | ||
if (!globalESSymbol) { | ||
// Already errored when we tried to look up the symbol | ||
return false; | ||
} | ||
|
||
if (leftHandSideSymbol !== globalESSymbol) { | ||
if (reportError) { | ||
error(leftHandSide, Diagnostics.Symbol_reference_does_not_refer_to_the_global_Symbol_constructor_object); | ||
} | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
function callLikeExpressionMayHaveTypeArguments(node: CallLikeExpression): node is CallExpression | NewExpression | TaggedTemplateExpression | JsxOpeningElement { | ||
return isCallOrNewExpression(node) || isTaggedTemplateExpression(node) || isJsxOpeningLikeElement(node); | ||
} | ||
|
@@ -35355,6 +35339,12 @@ namespace ts { | |
} | ||
} | ||
|
||
function getPropertyNameForKnownSymbolName(symbolName: string): __String { | ||
const ctorType = getGlobalESSymbolConstructorSymbol(/*reportErrors*/ false); | ||
const uniqueType = ctorType && getTypeOfPropertyOfType(getTypeOfSymbol(ctorType), escapeLeadingUnderscores(symbolName)); | ||
return uniqueType && isTypeUsableAsPropertyName(uniqueType) ? getPropertyNameFromType(uniqueType) : `__@${symbolName}` as __String; | ||
} | ||
|
||
/** | ||
* Gets the *yield*, *return*, and *next* types of an `Iterable`-like or `AsyncIterable`-like | ||
* type from its members. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -269,8 +269,7 @@ namespace ts { | |
* any such locations | ||
*/ | ||
export function isSimpleInlineableExpression(expression: Expression) { | ||
return !isIdentifier(expression) && isSimpleCopiableExpression(expression) || | ||
isWellKnownSymbolSyntactically(expression); | ||
return !isIdentifier(expression) && isSimpleCopiableExpression(expression); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change is pretty much single-handedly responsible for the .js emit changes in this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rbuckton is probably the best judge of that. |
||
} | ||
|
||
export function isCompoundAssignment(kind: BinaryOperator): kind is CompoundAssignmentOperator { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ interface SymbolConstructor { | |
* A method that returns the default iterator for an object. Called by the semantics of the | ||
* for-of statement. | ||
*/ | ||
readonly iterator: symbol; | ||
readonly iterator: unique symbol; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fun fact! I didn't actually need to make any of these lib changes to make the PR work, what with the compat code in place! But I've updated them anyway, so they serve as examples of what There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm - and that won't conflict with a separate "polyfill" definition right? In other words, if someone separately declares it as interface SymbolConstructor {
readonly iterator: symbol;
} and these merge, they won't end up conflicting, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope - and there's already a test to that effect on the diff. |
||
} | ||
|
||
interface IteratorYieldResult<TYield> { | ||
|
Uh oh!
There was an error while loading. Please reload this page.