diff --git a/packages/type-utils/src/isTypeReadonly.ts b/packages/type-utils/src/isTypeReadonly.ts index 632108cd6b3..b8757b0a222 100644 --- a/packages/type-utils/src/isTypeReadonly.ts +++ b/packages/type-utils/src/isTypeReadonly.ts @@ -216,7 +216,8 @@ function isTypeReadonlyRecurser( const result = unionTypeParts(type).every( t => seenTypes.has(t) || - isTypeReadonlyRecurser(checker, t, options, seenTypes), + isTypeReadonlyRecurser(checker, t, options, seenTypes) === + Readonlyness.Readonly, ); const readonlyness = result ? Readonlyness.Readonly : Readonlyness.Mutable; return readonlyness; diff --git a/packages/type-utils/tests/isTypeReadonly.test.ts b/packages/type-utils/tests/isTypeReadonly.test.ts index d63ecf422d9..6c39955b7ee 100644 --- a/packages/type-utils/tests/isTypeReadonly.test.ts +++ b/packages/type-utils/tests/isTypeReadonly.test.ts @@ -137,6 +137,33 @@ describe('isTypeReadonly', () => { ); }); }); + + describe('Union', () => { + describe('is readonly', () => { + const runTests = runTestIsReadonly; + + it.each([ + [ + 'type Test = Readonly<{ foo: string; bar: number; }> & Readonly<{ bar: number; }>;', + ], + ['type Test = readonly string[] | readonly number[];'], + ])('handles a union of 2 fully readonly types', runTests); + }); + + describe('is not readonly', () => { + const runTests = runTestIsNotReadonly; + + it.each([ + ['type Test = { foo: string; bar: number; } | { bar: number; };'], + [ + 'type Test = { foo: string; bar: number; } | Readonly<{ bar: number; }>;', + ], + [ + 'type Test = Readonly<{ foo: string; bar: number; }> | { bar: number; };', + ], + ])('handles a union of non fully readonly types', runTests); + }); + }); }); describe('treatMethodsAsReadonly', () => {