Skip to content

Commit

Permalink
Merge pull request #495 from cynecx/readonly-state
Browse files Browse the repository at this point in the history
improve typings for state setter by utilizing readonly
  • Loading branch information
ryansolid committed Jun 19, 2021
2 parents 3f0ffff + 140c88a commit 99b3182
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 8 deletions.
25 changes: 23 additions & 2 deletions packages/solid/src/reactive/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,33 @@ export function updatePath(current: StateNode, path: any[], traversed: (number |
} else setProperty(current, part, value);
}

export declare type Readonly<T> = { readonly [K in keyof T]: DeepReadonly<T[K]> };
export declare type DeepReadonly<T> = T extends [infer A]
? Readonly<[A]>
: T extends [infer A, infer B]
? Readonly<[A, B]>
: T extends [infer A, infer B, infer C]
? Readonly<[A, B, C]>
: T extends [infer A, infer B, infer C, infer D]
? Readonly<[A, B, C, D]>
: T extends [infer A, infer B, infer C, infer D, infer E]
? Readonly<[A, B, C, D, E]>
: T extends [infer A, infer B, infer C, infer D, infer E, infer F]
? Readonly<[A, B, C, D, E, F]>
: T extends [infer A, infer B, infer C, infer D, infer E, infer F, infer G]
? Readonly<[A, B, C, D, E, F, G]>
: T extends [infer A, infer B, infer C, infer D, infer E, infer F, infer G, infer H]
? Readonly<[A, B, C, D, E, F, G, H]>
: T extends object
? Readonly<T>
: T;

export type StateSetter<T> =
| Partial<T>
| ((
prevState: T extends NotWrappable ? T : State<T>,
prevState: T extends NotWrappable ? T : State<DeepReadonly<T>>,
traversed?: (string | number)[]
) => Partial<T> | void);
) => Partial<T | DeepReadonly<T>> | void);
export type StatePathRange = { from?: number; to?: number; by?: number };

export type ArrayFilterFn<T> = (
Expand Down
27 changes: 21 additions & 6 deletions packages/solid/src/reactive/stateModifiers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { setProperty, unwrap, isWrappable, State, NotWrappable, StateNode, $RAW } from "./state";
import {
setProperty,
unwrap,
isWrappable,
State,
NotWrappable,
StateNode,
DeepReadonly,
$RAW,
} from "./state";

export type ReconcileOptions = {
key?: string | null;
Expand Down Expand Up @@ -106,10 +115,13 @@ function applyState(
export function reconcile<T>(
value: T | State<T>,
options: ReconcileOptions = {}
): (state: T extends NotWrappable ? T : State<T>) => T extends NotWrappable ? T : State<T> {
): (
state: T extends NotWrappable ? T : State<DeepReadonly<T>>
) => T extends NotWrappable ? T : State<T> {
const { merge, key = "id" } = options,
v = unwrap(value);
return state => {
return s => {
const state = s as T extends NotWrappable ? T : State<T>;
if (!isWrappable(state) || !isWrappable(v)) return v as T extends NotWrappable ? T : State<T>;
applyState(v, { state }, "state", merge, key);
return state;
Expand Down Expand Up @@ -137,9 +149,12 @@ const setterTraps: ProxyHandler<StateNode> = {
// Immer style mutation style
export function produce<T>(
fn: (state: T) => void
): (state: T extends NotWrappable ? T : State<T>) => T extends NotWrappable ? T : State<T> {
return state => {
if (isWrappable(state)) fn((new Proxy(state as object, setterTraps) as unknown) as T);
): (
state: T extends NotWrappable ? T : State<DeepReadonly<T>>
) => T extends NotWrappable ? T : State<T> {
return s => {
const state = s as T extends NotWrappable ? T : State<T>;
if (isWrappable(state)) fn(new Proxy(state as object, setterTraps) as unknown as T);
return state;
};
}

0 comments on commit 99b3182

Please sign in to comment.