mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
fix(50375): Errors for missing enum-named properties should attempt to preserve names (#50382)
* fix(50375): preserve enum-named properties * add AllowComputedPropertyEnums option * use bit shifting * rename AllowComputedPropertyEnum -> WriteComputedProps * mark WriteComputedProps as internal * mark symbolToNode as internal
This commit is contained in:
parent
fb717df6bf
commit
8d7ad8c3ae
@ -464,6 +464,7 @@ namespace ts {
|
||||
signatureToSignatureDeclaration: nodeBuilder.signatureToSignatureDeclaration,
|
||||
symbolToEntityName: nodeBuilder.symbolToEntityName,
|
||||
symbolToExpression: nodeBuilder.symbolToExpression,
|
||||
symbolToNode: nodeBuilder.symbolToNode,
|
||||
symbolToTypeParameterDeclarations: nodeBuilder.symbolToTypeParameterDeclarations,
|
||||
symbolToParameterDeclaration: nodeBuilder.symbolToParameterDeclaration,
|
||||
typeParameterToDeclaration: nodeBuilder.typeParameterToDeclaration,
|
||||
@ -4822,7 +4823,10 @@ namespace ts {
|
||||
if (flags & SymbolFormatFlags.DoNotIncludeSymbolChain) {
|
||||
nodeFlags |= NodeBuilderFlags.DoNotIncludeSymbolChain;
|
||||
}
|
||||
const builder = flags & SymbolFormatFlags.AllowAnyNodeKind ? nodeBuilder.symbolToExpression : nodeBuilder.symbolToEntityName;
|
||||
if (flags & SymbolFormatFlags.WriteComputedProps) {
|
||||
nodeFlags |= NodeBuilderFlags.WriteComputedProps;
|
||||
}
|
||||
const builder = flags & SymbolFormatFlags.AllowAnyNodeKind ? nodeBuilder.symbolToNode : nodeBuilder.symbolToEntityName;
|
||||
return writer ? symbolToStringWorker(writer).getText() : usingSingleLineStringWriter(symbolToStringWorker);
|
||||
|
||||
function symbolToStringWorker(writer: EmitTextWriter) {
|
||||
@ -4919,8 +4923,25 @@ namespace ts {
|
||||
withContext(enclosingDeclaration, flags, tracker, context => typeParameterToDeclaration(parameter, context)),
|
||||
symbolTableToDeclarationStatements: (symbolTable: SymbolTable, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker, bundled?: boolean) =>
|
||||
withContext(enclosingDeclaration, flags, tracker, context => symbolTableToDeclarationStatements(symbolTable, context, bundled)),
|
||||
symbolToNode: (symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) =>
|
||||
withContext(enclosingDeclaration, flags, tracker, context => symbolToNode(symbol, context, meaning)),
|
||||
};
|
||||
|
||||
function symbolToNode(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags) {
|
||||
if (context.flags & NodeBuilderFlags.WriteComputedProps) {
|
||||
if (symbol.valueDeclaration) {
|
||||
const name = getNameOfDeclaration(symbol.valueDeclaration);
|
||||
if (name && isComputedPropertyName(name)) return name;
|
||||
}
|
||||
const nameType = getSymbolLinks(symbol).nameType;
|
||||
if (nameType && nameType.flags & (TypeFlags.EnumLiteral | TypeFlags.UniqueESSymbol)) {
|
||||
context.enclosingDeclaration = nameType.symbol.valueDeclaration;
|
||||
return factory.createComputedPropertyName(symbolToExpression(nameType.symbol, context, meaning));
|
||||
}
|
||||
}
|
||||
return symbolToExpression(symbol, context, meaning);
|
||||
}
|
||||
|
||||
function withContext<T>(enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined, tracker: SymbolTracker | undefined, cb: (context: NodeBuilderContext) => T): T | undefined {
|
||||
Debug.assert(enclosingDeclaration === undefined || (enclosingDeclaration.flags & NodeFlags.Synthesized) === 0);
|
||||
const context: NodeBuilderContext = {
|
||||
@ -20310,7 +20331,7 @@ namespace ts {
|
||||
shouldSkipElaboration = true; // Retain top-level error for interface implementing issues, otherwise omit it
|
||||
}
|
||||
if (props.length === 1) {
|
||||
const propName = symbolToString(unmatchedProperty);
|
||||
const propName = symbolToString(unmatchedProperty, /*enclosingDeclaration*/ undefined, SymbolFlags.None, SymbolFormatFlags.AllowAnyNodeKind | SymbolFormatFlags.WriteComputedProps);
|
||||
reportError(Diagnostics.Property_0_is_missing_in_type_1_but_required_in_type_2, propName, ...getTypeNamesForErrorDisplay(source, target));
|
||||
if (length(unmatchedProperty.declarations)) {
|
||||
associateRelatedInfo(createDiagnosticForNode(unmatchedProperty.declarations![0], Diagnostics._0_is_declared_here, propName));
|
||||
|
||||
@ -4595,6 +4595,8 @@ namespace ts {
|
||||
/** Note that the resulting nodes cannot be checked. */
|
||||
symbolToExpression(symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined): Expression | undefined;
|
||||
/** Note that the resulting nodes cannot be checked. */
|
||||
/* @internal */ symbolToNode(symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined): Node | undefined;
|
||||
/** Note that the resulting nodes cannot be checked. */
|
||||
symbolToTypeParameterDeclarations(symbol: Symbol, enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined): NodeArray<TypeParameterDeclaration> | undefined;
|
||||
/** Note that the resulting nodes cannot be checked. */
|
||||
symbolToParameterDeclaration(symbol: Symbol, enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined): ParameterDeclaration | undefined;
|
||||
@ -4872,6 +4874,7 @@ namespace ts {
|
||||
AllowEmptyTuple = 1 << 19,
|
||||
AllowUniqueESSymbolType = 1 << 20,
|
||||
AllowEmptyIndexInfoType = 1 << 21,
|
||||
/* @internal */ WriteComputedProps = 1 << 30, // { [E.A]: 1 }
|
||||
|
||||
// Errors (cont.)
|
||||
AllowNodeModulesRelativePaths = 1 << 26,
|
||||
@ -4930,27 +4933,30 @@ namespace ts {
|
||||
}
|
||||
|
||||
export const enum SymbolFormatFlags {
|
||||
None = 0x00000000,
|
||||
None = 0,
|
||||
|
||||
// Write symbols's type argument if it is instantiated symbol
|
||||
// eg. class C<T> { p: T } <-- Show p as C<T>.p here
|
||||
// var a: C<number>;
|
||||
// var p = a.p; <--- Here p is property of C<number> so show it as C<number>.p instead of just C.p
|
||||
WriteTypeParametersOrArguments = 0x00000001,
|
||||
WriteTypeParametersOrArguments = 1 << 0,
|
||||
|
||||
// Use only external alias information to get the symbol name in the given context
|
||||
// eg. module m { export class c { } } import x = m.c;
|
||||
// When this flag is specified m.c will be used to refer to the class instead of alias symbol x
|
||||
UseOnlyExternalAliasing = 0x00000002,
|
||||
UseOnlyExternalAliasing = 1 << 1,
|
||||
|
||||
// Build symbol name using any nodes needed, instead of just components of an entity name
|
||||
AllowAnyNodeKind = 0x00000004,
|
||||
AllowAnyNodeKind = 1 << 2,
|
||||
|
||||
// Prefer aliases which are not directly visible
|
||||
UseAliasDefinedOutsideCurrentScope = 0x00000008,
|
||||
UseAliasDefinedOutsideCurrentScope = 1 << 3,
|
||||
|
||||
// { [E.A]: 1 }
|
||||
/* @internal */ WriteComputedProps = 1 << 4,
|
||||
|
||||
// Skip building an accessible symbol chain
|
||||
/* @internal */ DoNotIncludeSymbolChain = 0x00000010,
|
||||
/* @internal */ DoNotIncludeSymbolChain = 1 << 5,
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
@ -642,11 +642,9 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function createPropertyNameFromSymbol(symbol: Symbol, target: ScriptTarget, quotePreference: QuotePreference, checker: TypeChecker) {
|
||||
if (isTransientSymbol(symbol) && symbol.nameType && symbol.nameType.flags & TypeFlags.UniqueESSymbol) {
|
||||
const expression = checker.symbolToExpression((symbol.nameType as UniqueESSymbolType).symbol, SymbolFlags.Value, symbol.valueDeclaration, NodeBuilderFlags.AllowUniqueESSymbolType);
|
||||
if (expression) {
|
||||
return factory.createComputedPropertyName(expression);
|
||||
}
|
||||
if (isTransientSymbol(symbol)) {
|
||||
const prop = checker.symbolToNode(symbol, SymbolFlags.Value, /*enclosingDeclaration*/ undefined, NodeBuilderFlags.WriteComputedProps);
|
||||
if (prop && isComputedPropertyName(prop)) return prop;
|
||||
}
|
||||
return createPropertyNameNodeForIdentifierOrLiteral(symbol.name, target, quotePreference === QuotePreference.Single);
|
||||
}
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithEnumIndexer.ts(5,5): error TS2741: Property '[E.A]' is missing in type '{}' but required in type 'Record<E, any>'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithEnumIndexer.ts (1 errors) ====
|
||||
enum E {
|
||||
A
|
||||
}
|
||||
|
||||
let foo: Record<E, any> = {}
|
||||
~~~
|
||||
!!! error TS2741: Property '[E.A]' is missing in type '{}' but required in type 'Record<E, any>'.
|
||||
|
||||
14
tests/baselines/reference/assignmentCompatWithEnumIndexer.js
Normal file
14
tests/baselines/reference/assignmentCompatWithEnumIndexer.js
Normal file
@ -0,0 +1,14 @@
|
||||
//// [assignmentCompatWithEnumIndexer.ts]
|
||||
enum E {
|
||||
A
|
||||
}
|
||||
|
||||
let foo: Record<E, any> = {}
|
||||
|
||||
|
||||
//// [assignmentCompatWithEnumIndexer.js]
|
||||
var E;
|
||||
(function (E) {
|
||||
E[E["A"] = 0] = "A";
|
||||
})(E || (E = {}));
|
||||
var foo = {};
|
||||
@ -0,0 +1,13 @@
|
||||
=== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithEnumIndexer.ts ===
|
||||
enum E {
|
||||
>E : Symbol(E, Decl(assignmentCompatWithEnumIndexer.ts, 0, 0))
|
||||
|
||||
A
|
||||
>A : Symbol(E.A, Decl(assignmentCompatWithEnumIndexer.ts, 0, 8))
|
||||
}
|
||||
|
||||
let foo: Record<E, any> = {}
|
||||
>foo : Symbol(foo, Decl(assignmentCompatWithEnumIndexer.ts, 4, 3))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>E : Symbol(E, Decl(assignmentCompatWithEnumIndexer.ts, 0, 0))
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
=== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithEnumIndexer.ts ===
|
||||
enum E {
|
||||
>E : E
|
||||
|
||||
A
|
||||
>A : E.A
|
||||
}
|
||||
|
||||
let foo: Record<E, any> = {}
|
||||
>foo : Record<E, any>
|
||||
>{} : {}
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
enum E {
|
||||
A
|
||||
}
|
||||
|
||||
let foo: Record<E, any> = {}
|
||||
18
tests/cases/fourslash/codeFixAddMissingProperties23.ts
Normal file
18
tests/cases/fourslash/codeFixAddMissingProperties23.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////enum E {
|
||||
//// A
|
||||
////}
|
||||
////let obj: Record<E, any> = {}
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: ts.Diagnostics.Add_missing_properties.message,
|
||||
newFileContent:
|
||||
`enum E {
|
||||
A
|
||||
}
|
||||
let obj: Record<E, any> = {
|
||||
[E.A]: undefined
|
||||
}`,
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user