Skip to content

Commit b7c5c2f

Browse files
committed
Expand immutability section with readonly types and helpers
1 parent 99ab342 commit b7c5c2f

1 file changed

Lines changed: 47 additions & 2 deletions

File tree

  • apps/web/src/app/(docs)/docs/conventions

apps/web/src/app/(docs)/docs/conventions/page.mdx

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,16 @@ const bar = () => {
6666

6767
## Immutability
6868

69-
Mutable state can be tricky because it increases the risk of unintended side effects, makes code harder to predict, and complicates debugging—especially in complex applications where data might be shared or modified unexpectedly.
69+
Mutable state is tricky because it increases the risk of unintended side effects, makes code harder to predict, and complicates debugging—especially in complex applications where data might be shared or modified unexpectedly. Favor immutable values using readonly types to reduce these risks and improve clarity.
7070

71-
Favor immutable values using TypeScript's type system to reduce these risks and improve clarity. Use `ReadonlyArray` and `NonEmptyReadonlyArray` for arrays and prefix interface properties with readonly to enforce immutability at the type level.
71+
### Readonly types
72+
73+
Use readonly types for collections and prefix interface properties with `readonly`:
74+
75+
- `ReadonlyArray<T>` and `NonEmptyReadonlyArray<T>` for arrays
76+
- `ReadonlySet<T>` for sets
77+
- `ReadonlyRecord<K, V>` for records
78+
- `ReadonlyMap<K, V>` for maps
7279

7380
```ts
7481
// Use ReadonlyArray for immutable arrays.
@@ -78,9 +85,47 @@ const values: ReadonlyArray<string> = ["a", "b", "c"];
7885
interface Example {
7986
readonly id: number;
8087
readonly items: ReadonlyArray<string>;
88+
readonly tags: ReadonlySet<string>;
8189
}
8290
```
8391

92+
### The `readonly` helper
93+
94+
Use the [readonly](/docs/api-reference/common/Function/functions/readonly) helper to cast arrays, sets, records, and maps to their readonly counterparts with zero runtime cost.
95+
96+
```ts
97+
import { readonly, NonEmptyArray } from "@evolu/common";
98+
99+
// Array literals become NonEmptyReadonlyArray
100+
const items = readonly([1, 2, 3]);
101+
// Type: NonEmptyReadonlyArray<number>
102+
103+
// NonEmptyArray is preserved as NonEmptyReadonlyArray
104+
const nonEmpty: NonEmptyArray<number> = [1, 2, 3];
105+
const readonlyNonEmpty = readonly(nonEmpty);
106+
// Type: NonEmptyReadonlyArray<number>
107+
108+
// Regular arrays become ReadonlyArray
109+
const arr: Array<number> = getNumbers();
110+
const readonlyArr = readonly(arr);
111+
// Type: ReadonlyArray<number>
112+
113+
// Sets, Records, and Maps
114+
const ids = readonly(new Set(["a", "b"]));
115+
// Type: ReadonlySet<string>
116+
117+
const users: Record<UserId, string> = { ... };
118+
const readonlyUsers = readonly(users);
119+
// Type: ReadonlyRecord<UserId, string>
120+
121+
const lookup = readonly(new Map([["key", "value"]]));
122+
// Type: ReadonlyMap<string, string>
123+
```
124+
125+
### Immutable helpers
126+
127+
Evolu provides helpers in the [Array](/docs/api-reference/common/Array) and [Object](/docs/api-reference/common/Object) modules that do not mutate and preserve readonly types.
128+
84129
## Interface over type
85130

86131
Prefer `interface` over `type` because interfaces always appear by name in error messages and tooltips.

0 commit comments

Comments
 (0)