diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index caab75c99fa8a..366914d03064f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -39007,8 +39007,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Get accessors without matching set accessors // Enum members // Object.defineProperty assignments with writable false or no setter - // Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation) - return !!(getCheckFlags(symbol) & CheckFlags.Readonly || + // Unions and intersections of the above + const checkFlags = getCheckFlags(symbol); + if (checkFlags & CheckFlags.Synthetic) { + // unions and intersections eagerly compute Readonly flag on creation + return !!(checkFlags & CheckFlags.Readonly); + } + return !!(checkFlags & CheckFlags.Readonly || symbol.flags & SymbolFlags.Property && getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.Readonly || symbol.flags & SymbolFlags.Variable && getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Constant || symbol.flags & SymbolFlags.Accessor && !(symbol.flags & SymbolFlags.SetAccessor) || diff --git a/tests/baselines/reference/intersectionTypeReadonly2.symbols b/tests/baselines/reference/intersectionTypeReadonly2.symbols new file mode 100644 index 0000000000000..240a29b78a8b9 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeReadonly2.symbols @@ -0,0 +1,55 @@ +//// [tests/cases/conformance/types/intersection/intersectionTypeReadonly2.ts] //// + +=== intersectionTypeReadonly2.ts === +// https://github.com/microsoft/TypeScript/issues/61344 + +type A = { k: number }; +>A : Symbol(A, Decl(intersectionTypeReadonly2.ts, 0, 0)) +>k : Symbol(k, Decl(intersectionTypeReadonly2.ts, 2, 10)) + +type B = { readonly k: number }; +>B : Symbol(B, Decl(intersectionTypeReadonly2.ts, 2, 23)) +>k : Symbol(k, Decl(intersectionTypeReadonly2.ts, 3, 10)) + +(({}) as A & B).k = 0; // ok +>(({}) as A & B).k : Symbol(k, Decl(intersectionTypeReadonly2.ts, 2, 10), Decl(intersectionTypeReadonly2.ts, 3, 10)) +>A : Symbol(A, Decl(intersectionTypeReadonly2.ts, 0, 0)) +>B : Symbol(B, Decl(intersectionTypeReadonly2.ts, 2, 23)) +>k : Symbol(k, Decl(intersectionTypeReadonly2.ts, 2, 10), Decl(intersectionTypeReadonly2.ts, 3, 10)) + +(({}) as B & A).k = 0; // ok +>(({}) as B & A).k : Symbol(k, Decl(intersectionTypeReadonly2.ts, 3, 10), Decl(intersectionTypeReadonly2.ts, 2, 10)) +>B : Symbol(B, Decl(intersectionTypeReadonly2.ts, 2, 23)) +>A : Symbol(A, Decl(intersectionTypeReadonly2.ts, 0, 0)) +>k : Symbol(k, Decl(intersectionTypeReadonly2.ts, 3, 10), Decl(intersectionTypeReadonly2.ts, 2, 10)) + +type MakeWritable<T> = { -readonly [K in keyof T]: T[K] }; +>MakeWritable : Symbol(MakeWritable, Decl(intersectionTypeReadonly2.ts, 6, 22)) +>T : Symbol(T, Decl(intersectionTypeReadonly2.ts, 8, 18)) +>K : Symbol(K, Decl(intersectionTypeReadonly2.ts, 8, 36)) +>T : Symbol(T, Decl(intersectionTypeReadonly2.ts, 8, 18)) +>T : Symbol(T, Decl(intersectionTypeReadonly2.ts, 8, 18)) +>K : Symbol(K, Decl(intersectionTypeReadonly2.ts, 8, 36)) + +type C = MakeWritable<B>; +>C : Symbol(C, Decl(intersectionTypeReadonly2.ts, 8, 58)) +>MakeWritable : Symbol(MakeWritable, Decl(intersectionTypeReadonly2.ts, 6, 22)) +>B : Symbol(B, Decl(intersectionTypeReadonly2.ts, 2, 23)) + +(({}) as C).k = 0; // ok +>(({}) as C).k : Symbol(k, Decl(intersectionTypeReadonly2.ts, 3, 10)) +>C : Symbol(C, Decl(intersectionTypeReadonly2.ts, 8, 58)) +>k : Symbol(k, Decl(intersectionTypeReadonly2.ts, 3, 10)) + +(({}) as C & B).k = 0; // ok +>(({}) as C & B).k : Symbol(k, Decl(intersectionTypeReadonly2.ts, 3, 10), Decl(intersectionTypeReadonly2.ts, 3, 10)) +>C : Symbol(C, Decl(intersectionTypeReadonly2.ts, 8, 58)) +>B : Symbol(B, Decl(intersectionTypeReadonly2.ts, 2, 23)) +>k : Symbol(k, Decl(intersectionTypeReadonly2.ts, 3, 10), Decl(intersectionTypeReadonly2.ts, 3, 10)) + +(({}) as B & C).k = 0; // ok +>(({}) as B & C).k : Symbol(k, Decl(intersectionTypeReadonly2.ts, 3, 10), Decl(intersectionTypeReadonly2.ts, 3, 10)) +>B : Symbol(B, Decl(intersectionTypeReadonly2.ts, 2, 23)) +>C : Symbol(C, Decl(intersectionTypeReadonly2.ts, 8, 58)) +>k : Symbol(k, Decl(intersectionTypeReadonly2.ts, 3, 10), Decl(intersectionTypeReadonly2.ts, 3, 10)) + diff --git a/tests/baselines/reference/intersectionTypeReadonly2.types b/tests/baselines/reference/intersectionTypeReadonly2.types new file mode 100644 index 0000000000000..d611f5daaded2 --- /dev/null +++ b/tests/baselines/reference/intersectionTypeReadonly2.types @@ -0,0 +1,115 @@ +//// [tests/cases/conformance/types/intersection/intersectionTypeReadonly2.ts] //// + +=== intersectionTypeReadonly2.ts === +// https://github.com/microsoft/TypeScript/issues/61344 + +type A = { k: number }; +>A : A +> : ^ +>k : number +> : ^^^^^^ + +type B = { readonly k: number }; +>B : B +> : ^ +>k : number +> : ^^^^^^ + +(({}) as A & B).k = 0; // ok +>(({}) as A & B).k = 0 : 0 +> : ^ +>(({}) as A & B).k : number +> : ^^^^^^ +>(({}) as A & B) : A & B +> : ^^^^^ +>({}) as A & B : A & B +> : ^^^^^ +>({}) : {} +> : ^^ +>{} : {} +> : ^^ +>k : number +> : ^^^^^^ +>0 : 0 +> : ^ + +(({}) as B & A).k = 0; // ok +>(({}) as B & A).k = 0 : 0 +> : ^ +>(({}) as B & A).k : number +> : ^^^^^^ +>(({}) as B & A) : B & A +> : ^^^^^ +>({}) as B & A : B & A +> : ^^^^^ +>({}) : {} +> : ^^ +>{} : {} +> : ^^ +>k : number +> : ^^^^^^ +>0 : 0 +> : ^ + +type MakeWritable<T> = { -readonly [K in keyof T]: T[K] }; +>MakeWritable : MakeWritable<T> +> : ^^^^^^^^^^^^^^^ + +type C = MakeWritable<B>; +>C : MakeWritable<B> +> : ^^^^^^^^^^^^^^^ + +(({}) as C).k = 0; // ok +>(({}) as C).k = 0 : 0 +> : ^ +>(({}) as C).k : number +> : ^^^^^^ +>(({}) as C) : MakeWritable<B> +> : ^^^^^^^^^^^^^^^ +>({}) as C : MakeWritable<B> +> : ^^^^^^^^^^^^^^^ +>({}) : {} +> : ^^ +>{} : {} +> : ^^ +>k : number +> : ^^^^^^ +>0 : 0 +> : ^ + +(({}) as C & B).k = 0; // ok +>(({}) as C & B).k = 0 : 0 +> : ^ +>(({}) as C & B).k : number +> : ^^^^^^ +>(({}) as C & B) : MakeWritable<B> & B +> : ^^^^^^^^^^^^^^^^^^^ +>({}) as C & B : MakeWritable<B> & B +> : ^^^^^^^^^^^^^^^^^^^ +>({}) : {} +> : ^^ +>{} : {} +> : ^^ +>k : number +> : ^^^^^^ +>0 : 0 +> : ^ + +(({}) as B & C).k = 0; // ok +>(({}) as B & C).k = 0 : 0 +> : ^ +>(({}) as B & C).k : number +> : ^^^^^^ +>(({}) as B & C) : B & MakeWritable<B> +> : ^^^^^^^^^^^^^^^^^^^ +>({}) as B & C : B & MakeWritable<B> +> : ^^^^^^^^^^^^^^^^^^^ +>({}) : {} +> : ^^ +>{} : {} +> : ^^ +>k : number +> : ^^^^^^ +>0 : 0 +> : ^ + diff --git a/tests/cases/conformance/types/intersection/intersectionTypeReadonly2.ts b/tests/cases/conformance/types/intersection/intersectionTypeReadonly2.ts new file mode 100644 index 0000000000000..8710ea44a184a --- /dev/null +++ b/tests/cases/conformance/types/intersection/intersectionTypeReadonly2.ts @@ -0,0 +1,17 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/61344 + +type A = { k: number }; +type B = { readonly k: number }; + +(({}) as A & B).k = 0; // ok +(({}) as B & A).k = 0; // ok + +type MakeWritable<T> = { -readonly [K in keyof T]: T[K] }; + +type C = MakeWritable<B>; +(({}) as C).k = 0; // ok +(({}) as C & B).k = 0; // ok +(({}) as B & C).k = 0; // ok