From 689091a4311a5545a01f2ed283050b15322d4e9c Mon Sep 17 00:00:00 2001 From: huymobile Date: Sat, 3 Oct 2026 08:50:04 +0700 Subject: [PATCH] fix: avoid excessive Draft recursion for readonly arrays in recursive types WritableDraft only took the `Draft[]` shortcut for mutable arrays, so readonly arrays went through the mapped type and recursive types such as type-fest's JsonValue (which uses `readonly JsonValue[]`) still hit TS2589. Match `readonly any[]` so readonly plain arrays take the same path. Co-Authored-By: Claude Opus 5.5 --- __tests__/type-external.ts | 19 ++++++++++++++++++- src/types/types-external.ts | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/__tests__/type-external.ts b/__tests__/type-external.ts index fcf3b2df9..c5eef3ffc 100644 --- a/__tests__/type-external.ts +++ b/__tests__/type-external.ts @@ -1,6 +1,6 @@ import {isType, JSONArray, JSONObject, JSONTypes} from "type-plus" import {Draft} from "../src/types/types-external" -import {createDraft, current, original} from "../src/immer" +import {createDraft, current, original, produce} from "../src/immer" describe("Draft", () => { test("can use JSONTypes as T", () => { @@ -17,6 +17,23 @@ describe("Draft", () => { type A = Draft<[string, number, JSONArray, JSONObject]> isType.equal() }) + + it("can use recursive types containing readonly arrays as T (#839)", () => { + // Same shape as type-fest's JsonValue + type JsonValue = string | number | boolean | null | JsonObject | JsonArray + type JsonObject = {[Key in string]: JsonValue} & { + [Key in string]?: JsonValue | undefined + } + type JsonArray = JsonValue[] | readonly JsonValue[] + + type A = Draft + isType.equal[], A>() + + const base: {readonly items: readonly JsonValue[]} = {items: []} + produce(base, draft => { + draft.items.push({a: [1, "b"]}) + }) + }) }) describe("current() typings", () => { diff --git a/src/types/types-external.ts b/src/types/types-external.ts index d42ab8f24..e599b8217 100644 --- a/src/types/types-external.ts +++ b/src/types/types-external.ts @@ -28,7 +28,7 @@ export type IfAvailable = */ type WeakReferences = IfAvailable> | IfAvailable> -export type WritableDraft = T extends any[] +export type WritableDraft = T extends readonly any[] ? number extends T["length"] ? IsPlainArray extends true ? Draft[]