אנגולר - NgRX שימוש בסוג גנרי ב-props של createAction
לפני שאני מתחיל, כבר נשאלה שאלה דומה על ידי מישהו אחר, אבל זה נראה מיושן והתשובה כבר לא פותרת את הבעיה.
הבעיה שלי היא אותה הבעיה שמתוארת בשאלה ההיא. אני משתמש ב-@ngrx/store גרסה 14.3.0. אני רוצה ליצור פונקציה שיוצרת פעולה על סמך הפרמטר המועבר. לדוגמה:
import { createAction, props } from '@ngrx/store';
export function loadContent<TParams extends object>(key: string) {
return createAction(
`[${key}] Load ${key}`,
props<TParams>()
// ^^^^^^^^^^^^^
);
}
קוד זה גורם לשגיאת קומפיילר כיוון ש-TParams אינו תואם לדרישות הסוג שהופעלו על ידי הפונקציה props. הנה השגיאה המלאה:
Error: libs/state/content-primitives/src/lib/content-actions.ts:7:9 - error TS2769: No overload matches this call.
Overload 1 of 3, '(type: `[${string}] Load ${string}`, config: ActionCreatorProps<TParams> & NotAllowedCheck<TParams>): ActionCreator<`[${string}] Load ${string}`, (props: TParams & NotAllowedCheck<...>) => TParams & TypedAction<...>>', gave the following error.
Argument of type 'ActionCreatorProps<TParams>' is not assignable to parameter of type 'ActionCreatorProps<TParams> & NotAllowedCheck<TParams>'.
Type 'ActionCreatorProps<TParams>' is not assignable to type 'NotAllowedCheck<TParams>'.
Overload 2 of 3, '(type: `[${string}] Load ${string}`, creator: Creator<any[], never>): FunctionWithParametersType<any[], object & TypedAction<`[${string}] Load ${string}`>> & TypedAction<...>', gave the following error.
Argument of type 'ActionCreatorProps<TParams>' is not assignable to parameter of type 'Creator<any[], never>'.
Type 'ActionCreatorProps<TParams>' provides no match for the signature '(...args: any[]): never'.
7 props<TParams>()
~~~~~~~~~~~~~~~~
Error: libs/state/content-primitives/src/lib/content-actions.ts:7:15 - error TS2344: Type 'TParams' does not satisfy the constraint 'NotAllowedInPropsCheck<TParams>'.
Type 'object' is not assignable to type 'NotAllowedInPropsCheck<TParams>'.
7 props<TParams>()
~~~~~~~
לאחר מכן, ניסיתי להתאים את פרמטר הסוג הזה בדיוק כפי שהוגדר בקוד המקור של NgRX עבור props. בעצם לקחתי את ההגדרות הכלליות של סוג והדבקתי בקוד שלי:
import { createAction, props } from '@ngrx/store';
import { NotAllowedInPropsCheck } from '@ngrx/store/src/models';
export function loadContent<TParams extends SafeProps, SafeProps = NotAllowedInPropsCheck<TParams>>(key: string) {
return createAction(
`[${key}] Load ${key}`,
props<TParams>()
// ^^^^^^^^^^^^^
);
}
גם זה לא עבד ונכשל עם שגיאת הקומפילציה הבאה:
Error: libs/state/content-primitives/src/lib/content-actions.ts:7:9 - error TS2769: No overload matches this call.
Overload 1 of 3, '(type: `[${string}] Load ${string}`, config: ActionCreatorProps<object> & "action creator cannot return an empty object"): ActionCreator<`[${string}] Load ${string}`, (props: never) => object & TypedAction<...>>', gave the following error.
Argument of type 'ActionCreatorProps<TParams>' is not assignable to parameter of type 'ActionCreatorProps<object> & "action creator cannot return an empty object"'.
Type 'ActionCreatorProps<TParams>' is not assignable to type 'ActionCreatorProps<object>'.
Type 'TParams' is not assignable to type 'object'.
Type 'SafeProps' is not assignable to type 'object'.
Overload 2 of 3, '(type: `[${string}] Load ${string}`, creator: Creator<any[], never>): FunctionWithParametersType<any[], object & TypedAction<`[${string}] Load ${string}`>> & TypedAction<...>', gave the following error.
Argument of type 'ActionCreatorProps<TParams>' is not assignable to parameter of type 'Creator<any[], never>'.
Type 'ActionCreatorProps<TParams>' provides no match for the signature '(...args: any[]): never'.
7 props<TParams>()
~~~~~~~~~~~~~~~~
libs/state/content-primitives/src/lib/content-actions.ts:4:56
4 export function loadContent<TParams extends SafeProps, SafeProps = NotAllowedInPropsCheck<TParams>>(key: string) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This type parameter might need an `extends object` constraint.
Error: libs/state/content-primitives/src/lib/content-actions.ts:7:15 - error TS2344: Type 'TParams' does not satisfy the constraint 'NotAllowedInPropsCheck<TParams>'.
Type 'SafeProps' is not assignable to type 'NotAllowedInPropsCheck<TParams>'.
7 props<TParams>()
~~~~~~~
libs/state/content-primitives/src/lib/content-actions.ts:4:56
4 export function loadContent<TParams extends SafeProps, SafeProps = NotAllowedInPropsCheck<TParams>>(key: string) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This type parameter might need an `extends NotAllowedInPropsCheck<TParams>` constraint.
השגיאה מציינת ש-SafeProps לא ניתן להקצות לסוג object, אבל אני לא מבין איך זה יכול להיות כאשר סוג גנרי NotAllowedInPropsCheck<TParams> בודק האם הסוג המסופק הוא object.
גם, איך זה ייתכן שהנוסחה של סוג גנרי בדיוק לא חלה כאן. אני מחפש פתרון שבו אני יכול לגרום לזה לקמפל בלי להשתמש בסוג any.
תשובה #1
NgRx בעצם מאלץ שיהיה אובייקט עם מפתח כ- payload לפעולה. המשמעות היא שבתוך אותו אובייקט, אפשר שיהיה כל דבר, אבל ברמה השורשית צריך שיהיה אובייקט עם לפחות מפתח אחד (לא מותר אובייקטים ריקים). הנה תיקון יחסית פשוט:
export function loadContent<TParams>(key: string) {
return createAction(
`[${key}] Load ${key}`,
(payload: TParams) => ({payload}),
);
}
const action = loadContent<number>('Content');
action({payload: 6});
אם אתה לא אוהב להוסיף את המפתח payload כל פעם כשאתה מפעיל את הפעולה, אתה יכול לוותר על הפונקציה props ולספק מימוש ידני בצורה פשוטה:
export function loadContent<TParams>(key: string) {
return createAction(
`[${key}] Load ${key}`,
(payload: TParams) => ({payload}),
);
}
const load = loadContent<number>('Content');
load(6);
תשובה #2
אני חושב שמצאתי דרך לעקיפה.
אני לא בטוח לגבי הטרמינולוגיה המדויקת, אבל הוסף אובייקט עם פרמטר אחד כסוג גיבוי.
props<TParams | { x: any }>()
אם סוג קונקרטי יסופק כארגומנט לפונקציה שלך (אני מניח שתמיד תספק אחד), אז הוא ישתמש בו, אחרת הוא ישתמש בסוג { x: any }.
זה יעשה את הקומפיילר שמח.
