diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6c240dce41291..9552a78f249ee 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10630,6 +10630,10 @@ namespace ts { return type; } + function getIsLateCheckFlag(s: Symbol): CheckFlags { + return getCheckFlags(s) & CheckFlags.Late; + } + /** Resolve the members of a mapped type { [P in K]: T } */ function resolveMappedTypeMembers(type: MappedType) { const members: SymbolTable = createSymbolTable(); @@ -10688,8 +10692,9 @@ namespace ts { const isReadonly = !!(templateModifiers & MappedTypeModifiers.IncludeReadonly || !(templateModifiers & MappedTypeModifiers.ExcludeReadonly) && modifiersProp && isReadonlySymbol(modifiersProp)); const stripOptional = strictNullChecks && !isOptional && modifiersProp && modifiersProp.flags & SymbolFlags.Optional; + const lateFlag: CheckFlags = modifiersProp ? getIsLateCheckFlag(modifiersProp) : 0; const prop = createSymbol(SymbolFlags.Property | (isOptional ? SymbolFlags.Optional : 0), propName, - CheckFlags.Mapped | (isReadonly ? CheckFlags.Readonly : 0) | (stripOptional ? CheckFlags.StripOptional : 0)); + lateFlag | CheckFlags.Mapped | (isReadonly ? CheckFlags.Readonly : 0) | (stripOptional ? CheckFlags.StripOptional : 0)); prop.mappedType = type; prop.nameType = propNameType; prop.keyType = keyType; @@ -14666,7 +14671,7 @@ namespace ts { else if (isSpreadableProperty(prop)) { const isSetonlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor); const flags = SymbolFlags.Property | SymbolFlags.Optional; - const result = createSymbol(flags, prop.escapedName, readonly ? CheckFlags.Readonly : 0); + const result = createSymbol(flags, prop.escapedName, getIsLateCheckFlag(prop) | (readonly ? CheckFlags.Readonly : 0)); result.type = isSetonlyAccessor ? undefinedType : getTypeOfSymbol(prop); result.declarations = prop.declarations; result.nameType = getSymbolLinks(prop).nameType; @@ -14814,7 +14819,7 @@ namespace ts { return prop; } const flags = SymbolFlags.Property | (prop.flags & SymbolFlags.Optional); - const result = createSymbol(flags, prop.escapedName, readonly ? CheckFlags.Readonly : 0); + const result = createSymbol(flags, prop.escapedName, getIsLateCheckFlag(prop) | (readonly ? CheckFlags.Readonly : 0)); result.type = isSetonlyAccessor ? undefinedType : getTypeOfSymbol(prop); result.declarations = prop.declarations; result.nameType = getSymbolLinks(prop).nameType; diff --git a/tests/baselines/reference/declarationEmitReadonlyComputedProperty.errors.txt b/tests/baselines/reference/declarationEmitReadonlyComputedProperty.errors.txt new file mode 100644 index 0000000000000..300bae29cb74d --- /dev/null +++ b/tests/baselines/reference/declarationEmitReadonlyComputedProperty.errors.txt @@ -0,0 +1,24 @@ +tests/cases/compiler/index.ts(3,14): error TS4023: Exported variable 'spread' has or is using name 'SYMBOL' from external module "tests/cases/compiler/bug" but cannot be named. + + +==== tests/cases/compiler/bug.ts (0 errors) ==== + export const SYMBOL = Symbol() + + export interface Interface { + readonly [SYMBOL]: string; // remove readonly and @showEmit to see the expected error + } + + export function createInstance(): Interface { + return { + [SYMBOL]: '' + } + } + +==== tests/cases/compiler/index.ts (1 errors) ==== + import { createInstance } from './bug' + + export const spread = { + ~~~~~~ +!!! error TS4023: Exported variable 'spread' has or is using name 'SYMBOL' from external module "tests/cases/compiler/bug" but cannot be named. + ...createInstance(), + } \ No newline at end of file diff --git a/tests/baselines/reference/declarationEmitReadonlyComputedProperty.js b/tests/baselines/reference/declarationEmitReadonlyComputedProperty.js new file mode 100644 index 0000000000000..43172fb16067f --- /dev/null +++ b/tests/baselines/reference/declarationEmitReadonlyComputedProperty.js @@ -0,0 +1,59 @@ +//// [tests/cases/compiler/declarationEmitReadonlyComputedProperty.ts] //// + +//// [bug.ts] +export const SYMBOL = Symbol() + +export interface Interface { + readonly [SYMBOL]: string; // remove readonly and @showEmit to see the expected error +} + +export function createInstance(): Interface { + return { + [SYMBOL]: '' + } +} + +//// [index.ts] +import { createInstance } from './bug' + +export const spread = { + ...createInstance(), +} + +//// [bug.js] +"use strict"; +exports.__esModule = true; +exports.createInstance = exports.SYMBOL = void 0; +exports.SYMBOL = Symbol(); +function createInstance() { + var _a; + return _a = {}, + _a[exports.SYMBOL] = '', + _a; +} +exports.createInstance = createInstance; +//// [index.js] +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +exports.__esModule = true; +exports.spread = void 0; +var bug_1 = require("./bug"); +exports.spread = __assign({}, bug_1.createInstance()); + + +//// [bug.d.ts] +export declare const SYMBOL: unique symbol; +export interface Interface { + readonly [SYMBOL]: string; +} +export declare function createInstance(): Interface; diff --git a/tests/baselines/reference/declarationEmitReadonlyComputedProperty.symbols b/tests/baselines/reference/declarationEmitReadonlyComputedProperty.symbols new file mode 100644 index 0000000000000..2c3d3284ba09d --- /dev/null +++ b/tests/baselines/reference/declarationEmitReadonlyComputedProperty.symbols @@ -0,0 +1,34 @@ +=== tests/cases/compiler/bug.ts === +export const SYMBOL = Symbol() +>SYMBOL : Symbol(SYMBOL, Decl(bug.ts, 0, 12)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +export interface Interface { +>Interface : Symbol(Interface, Decl(bug.ts, 0, 30)) + + readonly [SYMBOL]: string; // remove readonly and @showEmit to see the expected error +>[SYMBOL] : Symbol(Interface[SYMBOL], Decl(bug.ts, 2, 28)) +>SYMBOL : Symbol(SYMBOL, Decl(bug.ts, 0, 12)) +} + +export function createInstance(): Interface { +>createInstance : Symbol(createInstance, Decl(bug.ts, 4, 1)) +>Interface : Symbol(Interface, Decl(bug.ts, 0, 30)) + + return { + [SYMBOL]: '' +>[SYMBOL] : Symbol([SYMBOL], Decl(bug.ts, 7, 10)) +>SYMBOL : Symbol(SYMBOL, Decl(bug.ts, 0, 12)) + } +} + +=== tests/cases/compiler/index.ts === +import { createInstance } from './bug' +>createInstance : Symbol(createInstance, Decl(index.ts, 0, 8)) + +export const spread = { +>spread : Symbol(spread, Decl(index.ts, 2, 12)) + + ...createInstance(), +>createInstance : Symbol(createInstance, Decl(index.ts, 0, 8)) +} diff --git a/tests/baselines/reference/declarationEmitReadonlyComputedProperty.types b/tests/baselines/reference/declarationEmitReadonlyComputedProperty.types new file mode 100644 index 0000000000000..3517c1c2de1e0 --- /dev/null +++ b/tests/baselines/reference/declarationEmitReadonlyComputedProperty.types @@ -0,0 +1,37 @@ +=== tests/cases/compiler/bug.ts === +export const SYMBOL = Symbol() +>SYMBOL : unique symbol +>Symbol() : unique symbol +>Symbol : SymbolConstructor + +export interface Interface { + readonly [SYMBOL]: string; // remove readonly and @showEmit to see the expected error +>[SYMBOL] : string +>SYMBOL : unique symbol +} + +export function createInstance(): Interface { +>createInstance : () => Interface + + return { +>{ [SYMBOL]: '' } : { [SYMBOL]: string; } + + [SYMBOL]: '' +>[SYMBOL] : string +>SYMBOL : unique symbol +>'' : "" + } +} + +=== tests/cases/compiler/index.ts === +import { createInstance } from './bug' +>createInstance : () => import("tests/cases/compiler/bug").Interface + +export const spread = { +>spread : { [SYMBOL]: string; } +>{ ...createInstance(),} : { [SYMBOL]: string; } + + ...createInstance(), +>createInstance() : import("tests/cases/compiler/bug").Interface +>createInstance : () => import("tests/cases/compiler/bug").Interface +} diff --git a/tests/cases/compiler/declarationEmitReadonlyComputedProperty.ts b/tests/cases/compiler/declarationEmitReadonlyComputedProperty.ts new file mode 100644 index 0000000000000..cf1760806ae5a --- /dev/null +++ b/tests/cases/compiler/declarationEmitReadonlyComputedProperty.ts @@ -0,0 +1,22 @@ +// @declaration: true +// @lib: es2015 + +// @filename: bug.ts +export const SYMBOL = Symbol() + +export interface Interface { + readonly [SYMBOL]: string; // remove readonly and @showEmit to see the expected error +} + +export function createInstance(): Interface { + return { + [SYMBOL]: '' + } +} + +// @filename: index.ts +import { createInstance } from './bug' + +export const spread = { + ...createInstance(), +} \ No newline at end of file