编写自定义 ngrx 运算符并返回源可观察类型
Writing a custom ngrx operator and returning the source observable type
我有一个自定义运算符,waitFor
,我在我的效果中使用它:
public effect$: Observable<Action> = createEffect(() => {
return this.actions$.pipe(
ofType(myAction),
waitFor<ReturnType<typeof myAction>>([anotherAction]),
...etc
);
});
它基本上会查看 correlationId,以便在分派操作数组之前不继续执行。但这不是重点。
正如预期的那样,ofType
采用源可观察并将其用作 return 类型,但是我正在努力实现相同的效果。正如您在上面看到的,我在我的 waitFor
方法中使用 ReturnType<typeof myAction>>
和以下内容:
export function waitFor<A extends Action>(actionsToWaitFor$: Array<Actions>): OperatorFunction<A, A> {
所以现在如果我这样调用 waitFor
:
public effect$: Observable<Action> = createEffect(() => {
return this.actions$.pipe(
ofType(myAction),
waitFor([anotherAction]),
...etc
);
});
然后它的类型被推断为Action
,但我希望这个默认为ReturnType<typeof theSourceObservable>
。所以我假设我的 waitFor
方法中需要这样的东西:
export function waitFor<A extends ReturnType<typeof sourceObservable?!>>(actionsToWaitFor$: Array<Actions>): OperatorFunction<A, A> {
waitFor
看起来像这样:
export function waitFor<A extends Action>(actionsToWaitFor$: Array<Actions>): OperatorFunction<A, A> {
return (source$) => {
return source$.pipe(
switchMap((action: A & { correlationId: string}) => {
// use zip() to wait for all actions
// and when omitting map((action) => action)
// so the original action is always returned
})
);
};
}
从ofType
source看来我需要用Extract
更新
显示了 StackBlitz 示例 here
这至少可以编译;不知道是不是也满足你的需求
public effect3$: Observable<Action> = createEffect(() => {
const a:Action[]= []
return this.actions$.pipe(
ofType(doSomething),
this.someCustomOperatorReturningStaticTypes(),
this.thisWontWork(a),
tap(({aCustomProperty}) => {
// The type is inferred
console.log(aCustomProperty);
}),
)
});
private thisWontWork<A extends Action>(actionsToWaitFor$: Action[]): OperatorFunction<A, A> {
return (source$) => {
return source$.pipe(
tap(() => {
console.log('Should work')
})
)
}
}
我无法在 StackBlitz 中运行它,有什么提示吗?
希望对您有所帮助
遇到这个问题,我想我会添加一些解释,以防你仍然想知道,如果以后有人遇到这个问题!
首先 - 在您的示例中,您在 return 类型方面想得太多了。
如果您想根据传递的类型推断某些东西(例如,在这里您希望您的操作按其类型 returned),您只需 return 相同的泛型即可你通过了——正如答案所说:
function foo<A>(val: A): A {
return val;
}
如果我用一个数字调用 foo(),我会得到一个数字。如果我用特定的动作类型调用它,那么会发生同样的事情。这就是为什么你只需要 return type OperatorFunction<A, A>
- 同样的东西进来,同样的东西出来。
其次 - ofType 是如何工作的?
这里的关键是您需要一些正在缩小范围的操作。
使用 createAction 可以为您解决这个问题,但为了举例,我将手动创建它。
type FooAction = { type: 'FOO_ACTION' };
type BarAction = { type: 'BAR_ACTION' };
// This type gets built by you using createAction - this is your Action type
type MyActions = FooAction | BarAction;
接下来我们需要一个类型来执行我们的操作集 (MyActions
) - 并根据类型将其缩小到特定操作。
type NarrowActionType<T extends { type: any }, K extends string> = T extends { type: K }
? T
: never;
我们可以验证这是否有效:
type NarrowedActionTest = NarrowActionType<MyActions, 'FOO_ACTION'>;
ofType 是一个高阶函数,它首先接受一个字符串,然后是要操作的可观察流。我不会使用 observables 来让它更简单,但原理是一样的。
本质上,我们想要类似于 (type: string) => (action: Action) => action is NarrowActionType
的类型签名。
- NarrowActionType 需要整体操作类型作为第一个类型参数,我们将其称为
A
- NarrowActionType 需要我们用来缩小的字符串文字类型,我们称之为
T
- T 将是一个字符串,所以
T
扩展字符串
- 我们一开始只知道
T
的类型,然后在应用对象流后知道 A
的类型,所以让我们在每个函数应用程序中使用一个通用参数
- 该函数将最终检查
A.type
是否与类型相同。所以我们应该强制这个存在,A extends { type: any }
function isOfType<T extends string>(type: T) {
return <A extends { type: any }>(action: A): action is NarrowActionType<A, T> => {
return action.type === type
}
}
现在我们可以在 MyActions 类型上测试这个函数,看看它是否可以缩小输出:
const myAction: MyActions = { type: 'FOO_ACTION' };
const isFooAction = isOfType('FOO_ACTION')(myAction);
if (isOfType('FOO_ACTION')(myAction)) {
type myNarrowedAction = typeof myAction; // is FooAction
}
我有一个自定义运算符,waitFor
,我在我的效果中使用它:
public effect$: Observable<Action> = createEffect(() => {
return this.actions$.pipe(
ofType(myAction),
waitFor<ReturnType<typeof myAction>>([anotherAction]),
...etc
);
});
它基本上会查看 correlationId,以便在分派操作数组之前不继续执行。但这不是重点。
正如预期的那样,ofType
采用源可观察并将其用作 return 类型,但是我正在努力实现相同的效果。正如您在上面看到的,我在我的 waitFor
方法中使用 ReturnType<typeof myAction>>
和以下内容:
export function waitFor<A extends Action>(actionsToWaitFor$: Array<Actions>): OperatorFunction<A, A> {
所以现在如果我这样调用 waitFor
:
public effect$: Observable<Action> = createEffect(() => {
return this.actions$.pipe(
ofType(myAction),
waitFor([anotherAction]),
...etc
);
});
然后它的类型被推断为Action
,但我希望这个默认为ReturnType<typeof theSourceObservable>
。所以我假设我的 waitFor
方法中需要这样的东西:
export function waitFor<A extends ReturnType<typeof sourceObservable?!>>(actionsToWaitFor$: Array<Actions>): OperatorFunction<A, A> {
waitFor
看起来像这样:
export function waitFor<A extends Action>(actionsToWaitFor$: Array<Actions>): OperatorFunction<A, A> {
return (source$) => {
return source$.pipe(
switchMap((action: A & { correlationId: string}) => {
// use zip() to wait for all actions
// and when omitting map((action) => action)
// so the original action is always returned
})
);
};
}
从ofType
source看来我需要用Extract
更新
显示了 StackBlitz 示例 here
这至少可以编译;不知道是不是也满足你的需求
public effect3$: Observable<Action> = createEffect(() => {
const a:Action[]= []
return this.actions$.pipe(
ofType(doSomething),
this.someCustomOperatorReturningStaticTypes(),
this.thisWontWork(a),
tap(({aCustomProperty}) => {
// The type is inferred
console.log(aCustomProperty);
}),
)
});
private thisWontWork<A extends Action>(actionsToWaitFor$: Action[]): OperatorFunction<A, A> {
return (source$) => {
return source$.pipe(
tap(() => {
console.log('Should work')
})
)
}
}
我无法在 StackBlitz 中运行它,有什么提示吗?
希望对您有所帮助
遇到这个问题,我想我会添加一些解释,以防你仍然想知道,如果以后有人遇到这个问题!
首先 - 在您的示例中,您在 return 类型方面想得太多了。
如果您想根据传递的类型推断某些东西(例如,在这里您希望您的操作按其类型 returned),您只需 return 相同的泛型即可你通过了——正如答案所说:
function foo<A>(val: A): A {
return val;
}
如果我用一个数字调用 foo(),我会得到一个数字。如果我用特定的动作类型调用它,那么会发生同样的事情。这就是为什么你只需要 return type OperatorFunction<A, A>
- 同样的东西进来,同样的东西出来。
其次 - ofType 是如何工作的?
这里的关键是您需要一些正在缩小范围的操作。
使用 createAction 可以为您解决这个问题,但为了举例,我将手动创建它。
type FooAction = { type: 'FOO_ACTION' };
type BarAction = { type: 'BAR_ACTION' };
// This type gets built by you using createAction - this is your Action type
type MyActions = FooAction | BarAction;
接下来我们需要一个类型来执行我们的操作集 (MyActions
) - 并根据类型将其缩小到特定操作。
type NarrowActionType<T extends { type: any }, K extends string> = T extends { type: K }
? T
: never;
我们可以验证这是否有效:
type NarrowedActionTest = NarrowActionType<MyActions, 'FOO_ACTION'>;
ofType 是一个高阶函数,它首先接受一个字符串,然后是要操作的可观察流。我不会使用 observables 来让它更简单,但原理是一样的。
本质上,我们想要类似于 (type: string) => (action: Action) => action is NarrowActionType
的类型签名。
- NarrowActionType 需要整体操作类型作为第一个类型参数,我们将其称为
A
- NarrowActionType 需要我们用来缩小的字符串文字类型,我们称之为
T
- T 将是一个字符串,所以
T
扩展字符串 - 我们一开始只知道
T
的类型,然后在应用对象流后知道A
的类型,所以让我们在每个函数应用程序中使用一个通用参数 - 该函数将最终检查
A.type
是否与类型相同。所以我们应该强制这个存在,A extends { type: any }
function isOfType<T extends string>(type: T) {
return <A extends { type: any }>(action: A): action is NarrowActionType<A, T> => {
return action.type === type
}
}
现在我们可以在 MyActions 类型上测试这个函数,看看它是否可以缩小输出:
const myAction: MyActions = { type: 'FOO_ACTION' };
const isFooAction = isOfType('FOO_ACTION')(myAction);
if (isOfType('FOO_ACTION')(myAction)) {
type myNarrowedAction = typeof myAction; // is FooAction
}