Skip to content

Commit

Permalink
feat(store): remove deprecated createFeature method (#3825)
Browse files Browse the repository at this point in the history
Closes #3814 

BREAKING CHANGES:

The `createFeature` signature with root state is removed in favor of a signature without root state.
An automatic migration is added to remove this signature.

BEFORE:

```ts
interface AppState {
  users: State;
}

export const usersFeature = createFeature<AppState>({
  name: 'users',
  reducer: createReducer(initialState, /* case reducers */),
});
```

AFTER:

```ts
export const usersFeature = createFeature({
  name: 'users',
  reducer: createReducer(initialState, /* case reducers */),
});
```
  • Loading branch information
apramendorfer authored Apr 15, 2023
1 parent 351a75e commit fd8f347
Show file tree
Hide file tree
Showing 2 changed files with 0 additions and 193 deletions.
181 changes: 0 additions & 181 deletions modules/store/spec/types/feature_creator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,187 +173,6 @@ describe('createFeature()', () => {
});
});

describe('with passed app state type', () => {
it('should create', () => {
const snippet = expectSnippet(`
const enter = createAction('[Books Page] Enter');
const loadBooksSuccess = createAction(
'[Books API] Load Books Success',
props<{ books: Book[] }>()
);
interface Book {
id: number;
title: string;
}
type LoadState = 'init' | 'loading' | 'loaded' | 'error';
interface BooksState {
books: Book[];
loadState: LoadState;
}
interface AppState {
books: BooksState;
}
const initialState: BooksState = {
books: [],
loadState: 'init',
};
const booksFeature = createFeature<AppState>({
name: 'books',
reducer: createReducer(
initialState,
on(enter, (state) => ({ ...state, loadState: 'loading' })),
on(loadBooksSuccess, (state, { books }) => ({
...state,
books,
loadState: 'loaded',
}))
),
});
const {
name,
reducer,
selectBooksState,
selectBooks,
selectLoadState,
} = booksFeature;
let booksFeatureKeys: keyof typeof booksFeature;
`);

snippet.toInfer('name', '"books"');
snippet.toInfer('reducer', 'ActionReducer<BooksState, Action>');
snippet.toInfer(
'selectBooksState',
'MemoizedSelector<AppState, BooksState, (featureState: BooksState) => BooksState>'
);
snippet.toInfer(
'selectBooks',
'MemoizedSelector<AppState, Book[], (featureState: BooksState) => Book[]>'
);
snippet.toInfer(
'selectLoadState',
'MemoizedSelector<AppState, LoadState, (featureState: BooksState) => LoadState>'
);
snippet.toInfer(
'booksFeatureKeys',
'"selectBooksState" | "selectBooks" | "selectLoadState" | keyof FeatureConfig<"books", BooksState>'
);
});

it('should create a feature when reducer is created outside', () => {
const snippet = expectSnippet(`
interface State {
bar: string;
}
const initialState: State = { bar: 'ngrx' };
const fooReducer = createReducer(initialState);
const fooFeature = createFeature<{ foo: State }>({
name: 'foo',
reducer: fooReducer,
});
const {
name,
reducer,
selectFooState,
selectBar,
} = fooFeature;
`);

snippet.toInfer('name', '"foo"');
snippet.toInfer('reducer', 'ActionReducer<State, Action>');
snippet.toInfer(
'selectFooState',
'MemoizedSelector<{ foo: State; }, State, (featureState: State) => State>'
);
snippet.toInfer(
'selectBar',
'MemoizedSelector<{ foo: State; }, string, (featureState: State) => string>'
);
});

it('should fail when name is not key of app state', () => {
expectSnippet(`
interface AppState {
counter1: number;
counter2: number;
}
const counterFeature = createFeature<AppState>({
name: 'counter3',
reducer: createReducer(0),
});
`).toFail(
/Type '"counter3"' is not assignable to type '"counter1" | "counter2"'/
);
});

it('should allow use with StoreModule.forFeature', () => {
expectSnippet(`
const counterFeature = createFeature<{ counter: number }>({
name: 'counter',
reducer: createReducer(0),
});
StoreModule.forFeature(counterFeature);
`).toSucceed();
});

it('should allow use with untyped store.select', () => {
expectSnippet(`
const { selectCounterState, selectCount } = createFeature<{ counter: { count: number } }>({
name: 'counter',
reducer: createReducer({ count: 0 }),
});
let store!: Store;
const counterState$ = store.select(selectCounterState);
const count$ = store.select(selectCount);
`).toFail(
/Type 'object' is not assignable to type '{ counter: { count: number; }; }'/
);
});

it('should allow use with typed store.select', () => {
const snippet = expectSnippet(`
const { selectCounterState } = createFeature<{ counter: number }>({
name: 'counter',
reducer: createReducer(0),
});
let store!: Store<{ counter: number }>;
const counterState$ = store.select(selectCounterState);
`);

snippet.toInfer('counterState$', 'Observable<number>');
});

it('should fail when feature state contains optional properties', () => {
expectSnippet(`
interface CounterState {
count?: number;
}
interface AppState {
counter: CounterState;
}
const counterFeature = createFeature<AppState>({
name: 'counter',
reducer: createReducer({} as CounterState),
});
`).toFail(/optional properties are not allowed in the feature state/);
});
});

describe('nested selectors', () => {
it('should not create with feature state as a primitive value', () => {
expectSnippet(`
Expand Down
12 changes: 0 additions & 12 deletions modules/store/src/feature_creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,6 @@ export function createFeature<FeatureName extends string, FeatureState>(
featureConfig: FeatureConfig<FeatureName, FeatureState> &
NotAllowedFeatureStateCheck<FeatureState>
): Feature<Record<string, any>, FeatureName, FeatureState>;
/**
* @deprecated Use the `createFeature` signature without root state instead.
* For more info see: https://github.com/ngrx/platform/issues/3737
*/
export function createFeature<
AppState extends Record<string, any>,
FeatureName extends keyof AppState & string = keyof AppState & string,
FeatureState extends AppState[FeatureName] = AppState[FeatureName]
>(
featureConfig: FeatureConfig<FeatureName, FeatureState> &
NotAllowedFeatureStateCheck<FeatureState>
): Feature<AppState, FeatureName, FeatureState>;
/**
* @description
* A function that accepts a feature name and a feature reducer, and creates
Expand Down

0 comments on commit fd8f347

Please sign in to comment.