Skip to content

Prevent readonly symbols widening #54778

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11055,8 +11055,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
reportErrorsFromWidening(declaration, type);
}

// always widen a 'unique symbol' type if the type was created for a different declaration.
if (type.flags & TypeFlags.UniqueESSymbol && (isBindingElement(declaration) || !declaration.type) && type.symbol !== getSymbolOfDeclaration(declaration)) {
// always widen a 'unique symbol' type if the type was created for a different declaration and if it isn't accessible
if (type.flags & TypeFlags.UniqueESSymbol && !declaration.type && type.symbol !== getSymbolOfDeclaration(declaration) && !isValueSymbolAccessible(type.symbol, type.symbol.valueDeclaration)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a feeling that passing type.symbol.valueDeclaration to isValueSymbolAccessible here might not be 100% correct but so far I wasn't able to write a test that would fail with this

type = esSymbolType;
}

Expand Down Expand Up @@ -37570,7 +37570,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function widenTypeInferredFromInitializer(declaration: HasExpressionInitializer, type: Type) {
const widened = getCombinedNodeFlagsCached(declaration) & NodeFlags.Constant || isDeclarationReadonly(declaration) ? type : getWidenedLiteralType(type);
const widened = getCombinedNodeFlagsCached(declaration) & NodeFlags.Constant || isDeclarationReadonly(declaration) ? type : getWidenedUniqueESSymbolType(getWidenedLiteralType(type));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that perhaps as const should preserve the uniqueness of symbols created in object literals but that's not strictly related to this issue so I think it should be fine to tackle this separately if that's something that you think should be done.

if (isInJSFile(declaration)) {
if (isEmptyLiteralType(widened)) {
reportImplicitAny(declaration, anyType);
Expand Down
36 changes: 36 additions & 0 deletions tests/baselines/reference/indirectUniqueSymbolDeclarationEmit2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//// [tests/cases/compiler/indirectUniqueSymbolDeclarationEmit2.ts] ////

//// [indirectUniqueSymbolDeclarationEmit2.ts]
// repro from https://github.com/microsoft/TypeScript/issues/53276

export const a = Symbol.toStringTag;

export class F {
[a](){ return "" }
}

export const b = (new F())[a];


//// [indirectUniqueSymbolDeclarationEmit2.js]
"use strict";
// repro from https://github.com/microsoft/TypeScript/issues/53276
Object.defineProperty(exports, "__esModule", { value: true });
exports.b = exports.F = exports.a = void 0;
exports.a = Symbol.toStringTag;
var F = /** @class */ (function () {
function F() {
}
F.prototype[exports.a] = function () { return ""; };
return F;
}());
exports.F = F;
exports.b = (new F())[exports.a];


//// [indirectUniqueSymbolDeclarationEmit2.d.ts]
export declare const a: typeof Symbol.toStringTag;
export declare class F {
[a](): string;
}
export declare const b: () => string;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//// [tests/cases/compiler/indirectUniqueSymbolDeclarationEmit2.ts] ////

=== indirectUniqueSymbolDeclarationEmit2.ts ===
// repro from https://github.com/microsoft/TypeScript/issues/53276

export const a = Symbol.toStringTag;
>a : Symbol(a, Decl(indirectUniqueSymbolDeclarationEmit2.ts, 2, 12))
>Symbol.toStringTag : Symbol(SymbolConstructor.toStringTag, Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --))
>toStringTag : Symbol(SymbolConstructor.toStringTag, Decl(lib.es2015.symbol.wellknown.d.ts, --, --))

export class F {
>F : Symbol(F, Decl(indirectUniqueSymbolDeclarationEmit2.ts, 2, 36))

[a](){ return "" }
>[a] : Symbol(F[a], Decl(indirectUniqueSymbolDeclarationEmit2.ts, 4, 16))
>a : Symbol(a, Decl(indirectUniqueSymbolDeclarationEmit2.ts, 2, 12))
}

export const b = (new F())[a];
>b : Symbol(b, Decl(indirectUniqueSymbolDeclarationEmit2.ts, 8, 12))
>F : Symbol(F, Decl(indirectUniqueSymbolDeclarationEmit2.ts, 2, 36))
>a : Symbol(a, Decl(indirectUniqueSymbolDeclarationEmit2.ts, 2, 12))

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//// [tests/cases/compiler/indirectUniqueSymbolDeclarationEmit2.ts] ////

=== indirectUniqueSymbolDeclarationEmit2.ts ===
// repro from https://github.com/microsoft/TypeScript/issues/53276

export const a = Symbol.toStringTag;
>a : unique symbol
>Symbol.toStringTag : unique symbol
>Symbol : SymbolConstructor
>toStringTag : unique symbol

export class F {
>F : F

[a](){ return "" }
>[a] : () => string
>a : unique symbol
>"" : ""
}

export const b = (new F())[a];
>b : () => string
>(new F())[a] : () => string
>(new F()) : F
>new F() : F
>F : typeof F
>a : unique symbol

24 changes: 12 additions & 12 deletions tests/baselines/reference/uniqueSymbols.types
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const constTypeAndCall: unique symbol = Symbol();

// declaration from initializer
const constInitToConstCall = constCall;
>constInitToConstCall : symbol
>constInitToConstCall : unique symbol
>constCall : unique symbol

const constInitToLetCall = letCall;
Expand All @@ -41,7 +41,7 @@ const constInitToVarCall = varCall;
>varCall : symbol

const constInitToConstDeclAmbient = constType;
>constInitToConstDeclAmbient : symbol
>constInitToConstDeclAmbient : unique symbol
>constType : unique symbol

let letInitToConstCall = constCall;
Expand Down Expand Up @@ -201,19 +201,19 @@ declare const c: C;
>c : C

const constInitToCReadonlyStaticCall = C.readonlyStaticCall;
>constInitToCReadonlyStaticCall : symbol
>constInitToCReadonlyStaticCall : unique symbol
>C.readonlyStaticCall : unique symbol
>C : typeof C
>readonlyStaticCall : unique symbol

const constInitToCReadonlyStaticType = C.readonlyStaticType;
>constInitToCReadonlyStaticType : symbol
>constInitToCReadonlyStaticType : unique symbol
>C.readonlyStaticType : unique symbol
>C : typeof C
>readonlyStaticType : unique symbol

const constInitToCReadonlyStaticTypeAndCall = C.readonlyStaticTypeAndCall;
>constInitToCReadonlyStaticTypeAndCall : symbol
>constInitToCReadonlyStaticTypeAndCall : unique symbol
>C.readonlyStaticTypeAndCall : unique symbol
>C : typeof C
>readonlyStaticTypeAndCall : unique symbol
Expand Down Expand Up @@ -311,7 +311,7 @@ declare const i: I;
>i : I

const constInitToIReadonlyType = i.readonlyType;
>constInitToIReadonlyType : symbol
>constInitToIReadonlyType : unique symbol
>i.readonlyType : unique symbol
>i : I
>readonlyType : unique symbol
Expand Down Expand Up @@ -536,17 +536,17 @@ class C0 {
>C0 : C0

static readonly a = s;
>a : symbol
>a : unique symbol
>s : unique symbol

static readonly b = N.s;
>b : symbol
>b : unique symbol
>N.s : unique symbol
>N : typeof N
>s : unique symbol

static readonly c = N["s"];
>c : symbol
>c : unique symbol
>N["s"] : unique symbol
>N : typeof N
>"s" : "s"
Expand All @@ -568,17 +568,17 @@ class C0 {
>"s" : "s"

readonly a = s;
>a : symbol
>a : unique symbol
>s : unique symbol

readonly b = N.s;
>b : symbol
>b : unique symbol
>N.s : unique symbol
>N : typeof N
>s : unique symbol

readonly c = N["s"];
>c : symbol
>c : unique symbol
>N["s"] : unique symbol
>N : typeof N
>"s" : "s"
Expand Down
24 changes: 12 additions & 12 deletions tests/baselines/reference/uniqueSymbolsDeclarations.js
Original file line number Diff line number Diff line change
Expand Up @@ -414,10 +414,10 @@ declare let letCall: symbol;
declare var varCall: symbol;
declare const constType: unique symbol;
declare const constTypeAndCall: unique symbol;
declare const constInitToConstCall: symbol;
declare const constInitToConstCall: typeof constCall;
declare const constInitToLetCall: symbol;
declare const constInitToVarCall: symbol;
declare const constInitToConstDeclAmbient: symbol;
declare const constInitToConstDeclAmbient: typeof constType;
declare let letInitToConstCall: symbol;
declare let letInitToLetCall: symbol;
declare let letInitToVarCall: symbol;
Expand Down Expand Up @@ -451,9 +451,9 @@ declare class C {
readwriteCall: symbol;
}
declare const c: C;
declare const constInitToCReadonlyStaticCall: symbol;
declare const constInitToCReadonlyStaticType: symbol;
declare const constInitToCReadonlyStaticTypeAndCall: symbol;
declare const constInitToCReadonlyStaticCall: typeof C.readonlyStaticCall;
declare const constInitToCReadonlyStaticType: typeof C.readonlyStaticType;
declare const constInitToCReadonlyStaticTypeAndCall: typeof C.readonlyStaticTypeAndCall;
declare const constInitToCReadwriteStaticCall: symbol;
declare const constInitToCReadonlyStaticCallWithTypeQuery: typeof C.readonlyStaticCall;
declare const constInitToCReadonlyStaticTypeWithTypeQuery: typeof C.readonlyStaticType;
Expand All @@ -469,7 +469,7 @@ interface I {
readonly readonlyType: unique symbol;
}
declare const i: I;
declare const constInitToIReadonlyType: symbol;
declare const constInitToIReadonlyType: typeof i.readonlyType;
declare const constInitToIReadonlyTypeWithTypeQuery: typeof i.readonlyType;
declare const constInitToIReadonlyTypeWithIndexedAccess: I["readonlyType"];
type L = {
Expand Down Expand Up @@ -509,15 +509,15 @@ declare const o2: {
method5(p?: symbol): symbol;
};
declare class C0 {
static readonly a: symbol;
static readonly b: symbol;
static readonly c: symbol;
static readonly a: typeof s;
static readonly b: typeof N.s;
static readonly c: typeof N.s;
static d: symbol;
static e: symbol;
static f: symbol;
readonly a: symbol;
readonly b: symbol;
readonly c: symbol;
readonly a: typeof s;
readonly b: typeof N.s;
readonly c: typeof N.s;
d: symbol;
e: symbol;
f: symbol;
Expand Down
24 changes: 12 additions & 12 deletions tests/baselines/reference/uniqueSymbolsDeclarations.types
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const constTypeAndCall: unique symbol = Symbol();

// declaration from initializer
const constInitToConstCall = constCall;
>constInitToConstCall : symbol
>constInitToConstCall : unique symbol
>constCall : unique symbol

const constInitToLetCall = letCall;
Expand All @@ -41,7 +41,7 @@ const constInitToVarCall = varCall;
>varCall : symbol

const constInitToConstDeclAmbient = constType;
>constInitToConstDeclAmbient : symbol
>constInitToConstDeclAmbient : unique symbol
>constType : unique symbol

let letInitToConstCall = constCall;
Expand Down Expand Up @@ -194,19 +194,19 @@ declare const c: C;
>c : C

const constInitToCReadonlyStaticCall = C.readonlyStaticCall;
>constInitToCReadonlyStaticCall : symbol
>constInitToCReadonlyStaticCall : unique symbol
>C.readonlyStaticCall : unique symbol
>C : typeof C
>readonlyStaticCall : unique symbol

const constInitToCReadonlyStaticType = C.readonlyStaticType;
>constInitToCReadonlyStaticType : symbol
>constInitToCReadonlyStaticType : unique symbol
>C.readonlyStaticType : unique symbol
>C : typeof C
>readonlyStaticType : unique symbol

const constInitToCReadonlyStaticTypeAndCall = C.readonlyStaticTypeAndCall;
>constInitToCReadonlyStaticTypeAndCall : symbol
>constInitToCReadonlyStaticTypeAndCall : unique symbol
>C.readonlyStaticTypeAndCall : unique symbol
>C : typeof C
>readonlyStaticTypeAndCall : unique symbol
Expand Down Expand Up @@ -304,7 +304,7 @@ declare const i: I;
>i : I

const constInitToIReadonlyType = i.readonlyType;
>constInitToIReadonlyType : symbol
>constInitToIReadonlyType : unique symbol
>i.readonlyType : unique symbol
>i : I
>readonlyType : unique symbol
Expand Down Expand Up @@ -529,17 +529,17 @@ class C0 {
>C0 : C0

static readonly a = s;
>a : symbol
>a : unique symbol
>s : unique symbol

static readonly b = N.s;
>b : symbol
>b : unique symbol
>N.s : unique symbol
>N : typeof N
>s : unique symbol

static readonly c = N["s"];
>c : symbol
>c : unique symbol
>N["s"] : unique symbol
>N : typeof N
>"s" : "s"
Expand All @@ -561,17 +561,17 @@ class C0 {
>"s" : "s"

readonly a = s;
>a : symbol
>a : unique symbol
>s : unique symbol

readonly b = N.s;
>b : symbol
>b : unique symbol
>N.s : unique symbol
>N : typeof N
>s : unique symbol

readonly c = N["s"];
>c : symbol
>c : unique symbol
>N["s"] : unique symbol
>N : typeof N
>"s" : "s"
Expand Down
13 changes: 13 additions & 0 deletions tests/cases/compiler/indirectUniqueSymbolDeclarationEmit2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @strict: true
// @lib: esnext
// @declaration: true

// repro from https://github.com/microsoft/TypeScript/issues/53276

export const a = Symbol.toStringTag;

export class F {
[a](){ return "" }
}

export const b = (new F())[a];