immerjs / immer

Create the next immutable state by mutating the current one

Home Page:https://immerjs.github.io/immer/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Typescript: Cannot assign immutable state from payload to immutable writable draft

gracicot opened this issue Β· comments

πŸ› Bug Report

Immer exposes a Immutable helper type that deeply mark a type as readonly:

type A = Immutable<{ a: number }>;

const a: A = {
  a: 4
};

a.a = 5; // error, a.a is readonly

Immer also conveniently make it so that a WritableDraft<...> will let you assign to subobjects since immer will not mutate the original state, which is correct:

const a2 = produce(a, (draft) => {
  // draft is WritableDraft<Immutable<...>>
  draft.a = 5;
});

However, writable draft don't let an immutable value be assigned to another immutable value:

type B = Immutable<{ arr: number[] }>;

const b1: B = {
  arr: [1, 2, 3]
};

const b2: B = {
  arr: [4, 5, 6]
};

produce(b1, draft => {
  draft.arr = b2.arr;
});

This is technically correct as arr will be assigned to another mutable value

This issue arises a lot when you have an immutable state in redux, use a selector and send back a state in an action payload. When action payloads are immutable, this issue will trigger.

Link to repro

A bug report without a reproduction is not a bug report. Failing to follow this templately is likely to result in an immediate close & lock of the issue.

CodeSandbox demo

To Reproduce

Steps to reproduce the behavior:

type B = Immutable<{ arr: number[] }>;

const b1: B = {
  arr: [1, 2, 3]
};

const b2: B = {
  arr: [4, 5, 6]
};

produce(b1, draft => {
  draft.arr = b2.arr;
});

Observed behavior

Typescript refuses the code

Expected behavior

Typescript should accept the code

Environment

We only accept bug reports against the latest Immer version.

  • Immer version: 10.1.1
  • I filed this report against the latest version of Immer
  • Occurs with setUseProxies(true)
  • Occurs with setUseProxies(false) (ES5 only)

It seems castDraft works. It still would be nice to not need that so I'll keep the issue open. I'm the case it is completely unfixable, then maintainers can close this issue.