Skip to content

Commit bfb21c2

Browse files
feat(entity): strengthen typing of getInitialState (#4819)
Closes #4422
1 parent fa50f43 commit bfb21c2

File tree

4 files changed

+70
-5
lines changed

4 files changed

+70
-5
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { expecter } from 'ts-snippet';
2+
import { compilerOptions } from './utils';
3+
4+
describe('EntityState Types', () => {
5+
const expectSnippet = expecter(
6+
(code) => `
7+
import { EntityState, createEntityAdapter, EntityAdapter } from '@ngrx/entity';
8+
9+
interface Book {
10+
id: string;
11+
title: string;
12+
}
13+
14+
interface BookState extends EntityState<Book> {
15+
selectedBookId: string | null;
16+
}
17+
18+
export const adapter: EntityAdapter<Book> = createEntityAdapter<Book>();
19+
${code}
20+
`,
21+
compilerOptions()
22+
);
23+
24+
describe('getInitialState', () => {
25+
it('can set the initial state', () => {
26+
expectSnippet(`
27+
export const initialState: BookState = adapter.getInitialState({
28+
selectedBookId: '1',
29+
});
30+
31+
`).toSucceed();
32+
});
33+
34+
it('can set the initial state with additional properties', () => {
35+
expectSnippet(`
36+
export const initialState: BookState = adapter.getInitialState({
37+
selectedBookId: '1',
38+
});
39+
40+
`).toSucceed();
41+
});
42+
43+
it('throws when setting the initial state with unknown properties', () => {
44+
expectSnippet(`
45+
export const initialState: BookState = adapter.getInitialState({
46+
selectedBookId: '1',
47+
otherProperty: 'value',
48+
});
49+
`).toFail(
50+
/Object literal may only specify known properties, and 'otherProperty' does not exist in type 'Omit<BookState, keyof EntityState<T>>'/i
51+
);
52+
});
53+
54+
it('can set the initial state with unknown properties when the state is untyped', () => {
55+
expectSnippet(`
56+
export const initialState = adapter.getInitialState({
57+
selectedBookId: '1',
58+
otherProperty: 'value',
59+
});
60+
`).toSucceed();
61+
});
62+
});
63+
});

modules/entity/src/entity_state.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ export function getInitialEntityState<V>(): EntityState<V> {
99

1010
export function createInitialStateFactory<V>() {
1111
function getInitialState(): EntityState<V>;
12-
function getInitialState<S extends object>(
13-
additionalState: S
14-
): EntityState<V> & S;
12+
function getInitialState<S extends EntityState<V>>(
13+
additionalState: Omit<S, keyof EntityState<V>>
14+
): S;
1515
function getInitialState(additionalState: any = {}): any {
1616
return Object.assign(getInitialEntityState(), additionalState);
1717
}

modules/entity/src/models.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ export interface EntityAdapter<T> extends EntityStateAdapter<T> {
110110
selectId: IdSelector<T>;
111111
sortComparer: false | Comparer<T>;
112112
getInitialState(): EntityState<T>;
113-
getInitialState<S extends object>(state: S): EntityState<T> & S;
113+
getInitialState<S extends EntityState<T>>(
114+
state: Omit<S, keyof EntityState<T>>
115+
): S;
114116
getSelectors(): EntitySelectors<T, EntityState<T>>;
115117
getSelectors<V>(
116118
selectState: (state: V) => EntityState<T>

modules/store/src/selector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export function defaultMemoize(
7373
isResultEqual = isEqualCheck
7474
): MemoizedProjection {
7575
let lastArguments: null | IArguments = null;
76-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, , , , ,
76+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
7777
let lastResult: any = null;
7878
let overrideResult: any;
7979

0 commit comments

Comments
 (0)