Skip to content

Commit

Permalink
fix(signals): allow using signalStore and signalState in TS libs (#4152)
Browse files Browse the repository at this point in the history
  • Loading branch information
markostanimirovic authored Nov 27, 2023
1 parent c05d09b commit ecc247c
Show file tree
Hide file tree
Showing 16 changed files with 114 additions and 84 deletions.
2 changes: 1 addition & 1 deletion modules/signals/spec/patch-state.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { patchState, signalState, signalStore, withState } from '../src';
import { STATE_SIGNAL } from '../src/signal-state';
import { STATE_SIGNAL } from '../src/state-signal';

describe('patchState', () => {
const initialState = {
Expand Down
4 changes: 2 additions & 2 deletions modules/signals/spec/signal-state.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { effect, isSignal } from '@angular/core';
import * as angular from '@angular/core';
import { effect, isSignal } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { patchState, signalState } from '../src';
import { STATE_SIGNAL } from '../src/signal-state';
import { STATE_SIGNAL } from '../src/state-signal';

describe('signalState', () => {
const initialState = {
Expand Down
2 changes: 1 addition & 1 deletion modules/signals/spec/signal-store-feature.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
withMethods,
withState,
} from '../src';
import { STATE_SIGNAL } from '../src/signal-state';
import { STATE_SIGNAL } from '../src/state-signal';

describe('signalStoreFeature', () => {
function withCustomFeature1() {
Expand Down
2 changes: 1 addition & 1 deletion modules/signals/spec/signal-store.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
withMethods,
withState,
} from '../src';
import { STATE_SIGNAL } from '../src/signal-state';
import { STATE_SIGNAL } from '../src/state-signal';
import { createLocalService } from './helpers';

describe('signalStore', () => {
Expand Down
23 changes: 10 additions & 13 deletions modules/signals/spec/types/signal-store.types.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('signalStore', () => {

expectSnippet(snippet).toInfer(
'Store',
'Type<{ foo: Signal<string>; bar: Signal<number[]>; [STATE_SIGNAL]: WritableSignal<{ foo: string; bar: number[]; }>; }>'
'Type<{ foo: Signal<string>; bar: Signal<number[]>; } & StateSignal<{ foo: string; bar: number[]; }>>'
);
});

Expand Down Expand Up @@ -63,7 +63,7 @@ describe('signalStore', () => {

expectSnippet(snippet).toInfer(
'store',
'{ user: DeepSignal<{ age: number; details: { first: string; flags: boolean[]; }; }>; [STATE_SIGNAL]: WritableSignal<{ user: { age: number; details: { first: string; flags: boolean[]; }; }; }>; }'
'{ user: DeepSignal<{ age: number; details: { first: string; flags: boolean[]; }; }>; } & StateSignal<{ user: { age: number; details: { first: string; flags: boolean[]; }; }; }>'
);

expectSnippet(snippet).toInfer(
Expand Down Expand Up @@ -203,10 +203,7 @@ describe('signalStore', () => {

expectSnippet(snippet).toSucceed();

expectSnippet(snippet).toInfer(
'Store',
'Type<{ [STATE_SIGNAL]: WritableSignal<{}>; }>'
);
expectSnippet(snippet).toInfer('Store', 'Type<{} & StateSignal<{}>>');
});

it('succeeds when state slices are union types', () => {
Expand Down Expand Up @@ -237,7 +234,7 @@ describe('signalStore', () => {

expectSnippet(snippet).toInfer(
'store',
'{ foo: Signal<number | { s: string; }>; bar: DeepSignal<{ baz: { b: boolean; } | null; }>; x: DeepSignal<{ y: { z: number | undefined; }; }>; [STATE_SIGNAL]: WritableSignal<...>; }'
'{ foo: Signal<number | { s: string; }>; bar: DeepSignal<{ baz: { b: boolean; } | null; }>; x: DeepSignal<{ y: { z: number | undefined; }; }>; } & StateSignal<{ foo: number | { ...; }; bar: { ...; }; x: { ...; }; }>'
);

expectSnippet(snippet).toInfer('foo', 'Signal<number | { s: string; }>');
Expand Down Expand Up @@ -277,7 +274,7 @@ describe('signalStore', () => {

expectSnippet(snippet1).toInfer(
'Store',
'Type<{ name: DeepSignal<{ x: { y: string; }; }>; arguments: Signal<number[]>; call: Signal<boolean>; [STATE_SIGNAL]: WritableSignal<{ name: { x: { y: string; }; }; arguments: number[]; call: boolean; }>; }>'
'Type<{ name: DeepSignal<{ x: { y: string; }; }>; arguments: Signal<number[]>; call: Signal<boolean>; } & StateSignal<{ name: { x: { y: string; }; }; arguments: number[]; call: boolean; }>>'
);

const snippet2 = `
Expand All @@ -294,7 +291,7 @@ describe('signalStore', () => {

expectSnippet(snippet2).toInfer(
'Store',
' Type<{ apply: Signal<string>; bind: DeepSignal<{ foo: string; }>; prototype: Signal<string[]>; [STATE_SIGNAL]: WritableSignal<{ apply: string; bind: { foo: string; }; prototype: string[]; }>; }>'
'Type<{ apply: Signal<string>; bind: DeepSignal<{ foo: string; }>; prototype: Signal<string[]>; } & StateSignal<{ apply: string; bind: { foo: string; }; prototype: string[]; }>>'
);

const snippet3 = `
Expand All @@ -310,7 +307,7 @@ describe('signalStore', () => {

expectSnippet(snippet3).toInfer(
'Store',
'Type<{ length: Signal<number>; caller: Signal<undefined>; [STATE_SIGNAL]: WritableSignal<{ length: number; caller: undefined; }>; }>'
'Type<{ length: Signal<number>; caller: Signal<undefined>; } & StateSignal<{ length: number; caller: undefined; }>>'
);
});

Expand Down Expand Up @@ -365,7 +362,7 @@ describe('signalStore', () => {

expectSnippet(snippet).toInfer(
'store',
'{ bar: DeepSignal<{ baz?: number | undefined; }>; x: DeepSignal<{ y?: { z: boolean; } | undefined; }>; [STATE_SIGNAL]: WritableSignal<{ bar: { baz?: number | undefined; }; x: { y?: { ...; } | undefined; }; }>; }'
'{ bar: DeepSignal<{ baz?: number | undefined; }>; x: DeepSignal<{ y?: { z: boolean; } | undefined; }>; } & StateSignal<{ bar: { baz?: number | undefined; }; x: { y?: { z: boolean; } | undefined; }; }>'
);

expectSnippet(snippet).toInfer(
Expand Down Expand Up @@ -594,7 +591,7 @@ describe('signalStore', () => {

expectSnippet(snippet).toInfer(
'store',
'{ ngrx: Signal<string>; x: DeepSignal<{ y: string; }>; signals: Signal<number[]>; mgmt: (arg: boolean) => number; [STATE_SIGNAL]: WritableSignal<{ ngrx: string; x: { y: string; }; }>; }'
'{ ngrx: Signal<string>; x: DeepSignal<{ y: string; }>; signals: Signal<number[]>; mgmt: (arg: boolean) => number; } & StateSignal<{ ngrx: string; x: { y: string; }; }>'
);
});

Expand Down Expand Up @@ -622,7 +619,7 @@ describe('signalStore', () => {

expectSnippet(snippet).toInfer(
'store',
'{ foo: Signal<number>; bar: Signal<string>; baz: (x: number) => void; [STATE_SIGNAL]: WritableSignal<{ foo: number; }>; }'
'{ foo: Signal<number>; bar: Signal<string>; baz: (x: number) => void; } & StateSignal<{ foo: number; }>'
);
});

Expand Down
2 changes: 1 addition & 1 deletion modules/signals/spec/with-state.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isSignal, signal } from '@angular/core';
import { withComputed, withMethods, withState } from '../src';
import { STATE_SIGNAL } from '../src/signal-state';
import { STATE_SIGNAL } from '../src/state-signal';
import { getInitialInnerStore } from '../src/signal-store';

describe('withState', () => {
Expand Down
2 changes: 1 addition & 1 deletion modules/signals/src/deep-signal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IsKnownRecord } from './ts-helpers';

// An extended Signal type that enables the correct typing
// of nested signals with the `name` or `length` key.
interface Signal<T> extends NgSignal<T> {
export interface Signal<T> extends NgSignal<T> {
name: unknown;
length: unknown;
}
Expand Down
6 changes: 3 additions & 3 deletions modules/signals/src/get-state.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { STATE_SIGNAL, SignalStateMeta } from './signal-state';
import { STATE_SIGNAL, StateSignal } from './state-signal';

export function getState<State extends object>(
signalState: SignalStateMeta<State>
stateSignal: StateSignal<State>
): State {
return signalState[STATE_SIGNAL]();
return stateSignal[STATE_SIGNAL]();
}
6 changes: 3 additions & 3 deletions modules/signals/src/patch-state.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { STATE_SIGNAL, SignalStateMeta } from './signal-state';
import { STATE_SIGNAL, StateSignal } from './state-signal';

export type PartialStateUpdater<State extends object> = (
state: State
) => Partial<State>;

export function patchState<State extends object>(
signalState: SignalStateMeta<State>,
stateSignal: StateSignal<State>,
...updaters: Array<Partial<State & {}> | PartialStateUpdater<State & {}>>
): void {
signalState[STATE_SIGNAL].update((currentState) =>
stateSignal[STATE_SIGNAL].update((currentState) =>
updaters.reduce(
(nextState: State, updater) => ({
...nextState,
Expand Down
12 changes: 3 additions & 9 deletions modules/signals/src/signal-state.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { signal, WritableSignal } from '@angular/core';
import { signal } from '@angular/core';
import { STATE_SIGNAL, StateSignal } from './state-signal';
import { DeepSignal, toDeepSignal } from './deep-signal';

export const STATE_SIGNAL = Symbol('STATE_SIGNAL');

export type SignalStateMeta<State extends object> = {
[STATE_SIGNAL]: WritableSignal<State>;
};

type SignalState<State extends object> = DeepSignal<State> &
SignalStateMeta<State>;
type SignalState<State extends object> = DeepSignal<State> & StateSignal<State>;

export function signalState<State extends object>(
initialState: State
Expand Down
11 changes: 5 additions & 6 deletions modules/signals/src/signal-store-models.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Signal } from '@angular/core';
import { DeepSignal } from './deep-signal';
import { SignalStateMeta } from './signal-state';
import { StateSignal } from './state-signal';
import { IsKnownRecord, Prettify } from './ts-helpers';

export type SignalStoreConfig = { providedIn: 'root' };
Expand All @@ -15,12 +15,11 @@ export type SignalStoreSlices<State> = IsKnownRecord<
}
: {};

export type SignalStore<FeatureResult extends SignalStoreFeatureResult> =
export type SignalStoreProps<FeatureResult extends SignalStoreFeatureResult> =
Prettify<
SignalStoreSlices<FeatureResult['state']> &
FeatureResult['signals'] &
FeatureResult['methods'] &
SignalStateMeta<Prettify<FeatureResult['state']>>
FeatureResult['methods']
>;

export type SignalsDictionary = Record<string, Signal<unknown>>;
Expand All @@ -41,7 +40,7 @@ export type InnerSignalStore<
signals: Signals;
methods: Methods;
hooks: SignalStoreHooks;
} & SignalStateMeta<State>;
} & StateSignal<State>;

export type SignalStoreFeatureResult = {
state: object;
Expand All @@ -61,7 +60,7 @@ export type SignalStoreFeature<
export type MergeFeatureResults<
FeatureResults extends SignalStoreFeatureResult[]
> = FeatureResults extends []
? {}
? EmptyFeatureResult
: FeatureResults extends [infer First extends SignalStoreFeatureResult]
? First
: FeatureResults extends [
Expand Down
Loading

0 comments on commit ecc247c

Please sign in to comment.