Skip to content

Commit e37c57b

Browse files
feat(store): remove forbidden chars and empty str checks from createActionGroup (#3857)
1 parent 482fa5d commit e37c57b

File tree

3 files changed

+11
-131
lines changed

3 files changed

+11
-131
lines changed

modules/store/spec/types/action_group_creator.spec.ts

Lines changed: 0 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -127,32 +127,6 @@ describe('createActionGroup', () => {
127127
);
128128
});
129129

130-
it('should fail when event name is an empty string', () => {
131-
expectSnippet(`
132-
const booksApiActions = createActionGroup({
133-
source: 'Books API',
134-
events: {
135-
'': emptyProps(),
136-
},
137-
});
138-
`).toFail(
139-
/event name cannot be an empty string or contain only spaces/
140-
);
141-
});
142-
143-
it('should fail when event name contains only spaces', () => {
144-
expectSnippet(`
145-
const booksApiActions = createActionGroup({
146-
source: 'Books API',
147-
events: {
148-
' ': emptyProps(),
149-
},
150-
});
151-
`).toFail(
152-
/event name cannot be an empty string or contain only spaces/
153-
);
154-
});
155-
156130
it('should fail when event name is not a string literal type', () => {
157131
expectSnippet(`
158132
const booksApiActions = createActionGroup({
@@ -164,70 +138,6 @@ describe('createActionGroup', () => {
164138
`).toFail(/event name must be a string literal type/);
165139
});
166140

167-
describe('forbidden characters', () => {
168-
[
169-
String.raw`\\`,
170-
'/',
171-
'|',
172-
'<',
173-
'>',
174-
'[',
175-
']',
176-
'{',
177-
'}',
178-
'(',
179-
')',
180-
'.',
181-
',',
182-
'!',
183-
'?',
184-
'#',
185-
'%',
186-
'^',
187-
'&',
188-
'*',
189-
'+',
190-
'-',
191-
'~',
192-
'"',
193-
String.raw`\'`,
194-
'`',
195-
].forEach((char) => {
196-
it(`should fail when event name contains ${char} in the beginning`, () => {
197-
expectSnippet(`
198-
const booksApiActions = createActionGroup({
199-
source: 'Books API',
200-
events: {
201-
'${char}Load Books Success': emptyProps(),
202-
},
203-
});
204-
`).toFail(/event name cannot contain the following characters:/);
205-
});
206-
207-
it(`should fail when event name contains ${char} in the middle`, () => {
208-
expectSnippet(`
209-
const booksApiActions = createActionGroup({
210-
source: 'Books API',
211-
events: {
212-
'Load Books ${char} Success': emptyProps(),
213-
},
214-
});
215-
`).toFail(/event name cannot contain the following characters:/);
216-
});
217-
218-
it(`should fail when event name contains ${char} in the end`, () => {
219-
expectSnippet(`
220-
const booksApiActions = createActionGroup({
221-
source: 'Books API',
222-
events: {
223-
'Load Books Success${char}': emptyProps(),
224-
},
225-
});
226-
`).toFail(/event name cannot contain the following characters:/);
227-
});
228-
});
229-
});
230-
231141
it('should fail when two event names are mapped to the same action name', () => {
232142
expectSnippet(`
233143
const booksApiActions = createActionGroup({

modules/store/src/action_group_creator_models.ts

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,10 @@ type Join<
1818
? Join<`${First}${Rest}`, Separator>
1919
: Str;
2020

21-
type Trim<Str extends string> = Str extends ` ${infer S}`
22-
? Trim<S>
23-
: Str extends `${infer S} `
24-
? Trim<S>
25-
: Str;
26-
27-
type TitleCase<Str extends string> = Str extends `${infer First} ${infer Rest}`
28-
? `${Capitalize<First>} ${TitleCase<Rest>}`
29-
: Capitalize<Str>;
30-
31-
type ForbiddenCharactersStr =
32-
'/ \\ | < > [ ] { } ( ) . , ! ? # % ^ & * + - ~ \' " `';
33-
34-
type ForbiddenCharacters<Str extends string = ForbiddenCharactersStr> =
21+
type CapitalizeWords<Str extends string> =
3522
Str extends `${infer First} ${infer Rest}`
36-
? First | ForbiddenCharacters<Rest>
37-
: Str extends ''
38-
? never
39-
: Str;
40-
41-
type ForbiddenCharactersCheck<
42-
Str extends string,
43-
Name extends string
44-
> = Str extends `${infer _}${ForbiddenCharacters}${infer _}`
45-
? `${Name} cannot contain the following characters: ${ForbiddenCharactersStr}`
46-
: unknown;
47-
48-
type EmptyStringCheck<
49-
Str extends string,
50-
Name extends string
51-
> = Trim<Str> extends ''
52-
? `${Name} cannot be an empty string or contain only spaces`
53-
: unknown;
23+
? `${Capitalize<First>} ${CapitalizeWords<Rest>}`
24+
: Capitalize<Str>;
5425

5526
type StringLiteralCheck<
5627
Str extends string,
@@ -95,7 +66,7 @@ type EventCreator<
9566
: never;
9667

9768
export type ActionName<EventName extends string> = Uncapitalize<
98-
Join<TitleCase<EventName>>
69+
Join<CapitalizeWords<EventName>>
9970
>;
10071

10172
export interface ActionGroupConfig<
@@ -104,12 +75,10 @@ export interface ActionGroupConfig<
10475
> {
10576
source: Source & StringLiteralCheck<Source, 'source'>;
10677
events: Events & {
107-
[EventName in keyof Events]: EmptyStringCheck<
78+
[EventName in keyof Events]: StringLiteralCheck<
10879
EventName & string,
10980
'event name'
11081
> &
111-
StringLiteralCheck<EventName & string, 'event name'> &
112-
ForbiddenCharactersCheck<EventName & string, 'event name'> &
11382
UniqueEventNameCheck<keyof Events & string, EventName & string> &
11483
NotAllowedEventPropsCheck<Events[EventName]>;
11584
};

projects/ngrx.io/content/guide/store/action-groups.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,14 @@ export class ProductsComponent implements OnInit {
6969

7070
```
7171

72-
## Limitations and Restrictions
72+
## Limitations
7373

74-
An action group uses the event descriptions to create properties within the group that represent the action(s).
75-
The property names are auto-generated and are the camelCased version of the event description. For example `Query Changed` becomes `queryChanged`.
76-
This has the drawback that not all characters can be used to describe an event because some characters can't be used to create a valid name. For example, any of the following characters are not allowed and result in a compile error `/ \\ | < > [ ] { } ( ) . , ! ? # % ^ & * + - ~ \' "`.
74+
An action group uses the event names to create properties within the group that represent the action creators.
75+
The action creator names are generated and are the camelCased version of the event names.
76+
For example, for the event name `Query Changed`, the action creator name will be `queryChanged`.
77+
Therefore, it is not possible to define action creators whose names differ from their event names using the `createActionGroup` function.
7778

7879
You can read more about Action Groups:
7980

8081
- [NgRx Action Group Creator](https://dev.to/ngrx/ngrx-action-group-creator-1deh)
81-
- [Creating Actions with NgRx Just Got Even Easier](https://www.youtube.com/watch?v=rk83ZMqEDV4)
82+
- [Creating Actions with NgRx Just Got Even Easier](https://www.youtube.com/watch?v=rk83ZMqEDV4)

0 commit comments

Comments
 (0)