From f824e7214ddda789baa6379ff599c5f5012a42b6 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 24 Aug 2017 16:48:11 -0700 Subject: [PATCH] Give mapped type properties a synthetic declaration name (#18023) * Escape symbol names which are not valid identifiers and wrap them in quotes * Pass forward type, do work in getNameOfSymbol * Minimal test * Fix nit --- src/compiler/checker.ts | 16 +++++++++++++++- src/compiler/types.ts | 1 + .../reference/declarationQuotedMembers.js | 17 +++++++++++++++++ .../reference/declarationQuotedMembers.symbols | 9 +++++++++ .../reference/declarationQuotedMembers.types | 9 +++++++++ .../cases/compiler/declarationQuotedMembers.ts | 3 +++ 6 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/declarationQuotedMembers.js create mode 100644 tests/baselines/reference/declarationQuotedMembers.symbols create mode 100644 tests/baselines/reference/declarationQuotedMembers.types create mode 100644 tests/cases/compiler/declarationQuotedMembers.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 21db77b5e3..55d26bb48e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3114,6 +3114,12 @@ namespace ts { return "(Anonymous function)"; } } + if ((symbol as TransientSymbol).syntheticLiteralTypeOrigin) { + const stringValue = (symbol as TransientSymbol).syntheticLiteralTypeOrigin.value; + if (!isIdentifierText(stringValue, compilerOptions.target)) { + return `"${escapeString(stringValue, CharacterCodes.doubleQuote)}"`; + } + } return unescapeLeadingUnderscores(symbol.escapedName); } @@ -5724,7 +5730,14 @@ namespace ts { } setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined); - function addMemberForKeyType(t: Type, propertySymbol?: Symbol) { + function addMemberForKeyType(t: Type, propertySymbolOrIndex?: Symbol | number) { + let propertySymbol: Symbol; + // forEachType delegates to forEach, which calls with a numeric second argument + // the type system currently doesn't catch this incompatibility, so we annotate + // the function ourselves to indicate the runtime behavior and deal with it here + if (typeof propertySymbolOrIndex === "object") { + propertySymbol = propertySymbolOrIndex; + } // Create a mapper from T to the current iteration type constituent. Then, if the // mapped type is itself an instantiated type, combine the iteration mapper with the // instantiation mapper. @@ -5744,6 +5757,7 @@ namespace ts { prop.syntheticOrigin = propertySymbol; prop.declarations = propertySymbol.declarations; } + prop.syntheticLiteralTypeOrigin = t as StringLiteralType; members.set(propName, prop); } else if (t.flags & TypeFlags.String) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index fe18839b90..e3c8661deb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2972,6 +2972,7 @@ namespace ts { leftSpread?: Symbol; // Left source for synthetic spread property rightSpread?: Symbol; // Right source for synthetic spread property syntheticOrigin?: Symbol; // For a property on a mapped or spread type, points back to the original property + syntheticLiteralTypeOrigin?: StringLiteralType; // For a property on a mapped type, indicates the type whose text to use as the declaration name, instead of the symbol name isDiscriminantProperty?: boolean; // True if discriminant synthetic property resolvedExports?: SymbolTable; // Resolved exports of module exportsChecked?: boolean; // True if exports of external module have been checked diff --git a/tests/baselines/reference/declarationQuotedMembers.js b/tests/baselines/reference/declarationQuotedMembers.js new file mode 100644 index 0000000000..ab40231eb3 --- /dev/null +++ b/tests/baselines/reference/declarationQuotedMembers.js @@ -0,0 +1,17 @@ +//// [declarationQuotedMembers.ts] +export declare const mapped: { [K in 'a-b-c']: number } +export const example = mapped; + +//// [declarationQuotedMembers.js] +"use strict"; +exports.__esModule = true; +exports.example = exports.mapped; + + +//// [declarationQuotedMembers.d.ts] +export declare const mapped: { + [K in 'a-b-c']: number; +}; +export declare const example: { + "a-b-c": number; +}; diff --git a/tests/baselines/reference/declarationQuotedMembers.symbols b/tests/baselines/reference/declarationQuotedMembers.symbols new file mode 100644 index 0000000000..3bf1a2829b --- /dev/null +++ b/tests/baselines/reference/declarationQuotedMembers.symbols @@ -0,0 +1,9 @@ +=== tests/cases/compiler/declarationQuotedMembers.ts === +export declare const mapped: { [K in 'a-b-c']: number } +>mapped : Symbol(mapped, Decl(declarationQuotedMembers.ts, 0, 20)) +>K : Symbol(K, Decl(declarationQuotedMembers.ts, 0, 32)) + +export const example = mapped; +>example : Symbol(example, Decl(declarationQuotedMembers.ts, 1, 12)) +>mapped : Symbol(mapped, Decl(declarationQuotedMembers.ts, 0, 20)) + diff --git a/tests/baselines/reference/declarationQuotedMembers.types b/tests/baselines/reference/declarationQuotedMembers.types new file mode 100644 index 0000000000..c9f6c047b7 --- /dev/null +++ b/tests/baselines/reference/declarationQuotedMembers.types @@ -0,0 +1,9 @@ +=== tests/cases/compiler/declarationQuotedMembers.ts === +export declare const mapped: { [K in 'a-b-c']: number } +>mapped : { a-b-c: number; } +>K : K + +export const example = mapped; +>example : { a-b-c: number; } +>mapped : { a-b-c: number; } + diff --git a/tests/cases/compiler/declarationQuotedMembers.ts b/tests/cases/compiler/declarationQuotedMembers.ts new file mode 100644 index 0000000000..08bae2c375 --- /dev/null +++ b/tests/cases/compiler/declarationQuotedMembers.ts @@ -0,0 +1,3 @@ +// @declaration: true +export declare const mapped: { [K in 'a-b-c']: number } +export const example = mapped; \ No newline at end of file