我的项目遇到了一些我完全不清楚的问题
// =========================== RxJS types imitation ===========================================================
interface Subscription {
unsubscribe(): void
}
type Subscribe<T> = (value: T) => void
interface Observable<T> {
subscribe(subscribe: Subscribe<T>): Subscription
}
interface Subject<T> {
next(value: T): void
subscribe(subscribe: Subscribe<T>): Subscription
}
// =========================== My custom types ===========================================================
export interface Reducer<S, A> {
(state: S, action: A): S;
}
export interface Effect<A, S> {
(action$: Subject<A>, state$: Observable<S>): Subscription;
}
export interface OptionalParams<S, A> {
effects?: Effect<A, S>[];
}
// =========================== Reproducing concrete types and variables ===========================================================
const initialState = {
some: true
}
type State = typeof initialState
interface BaseAction<T extends string> {
type: T
}
interface ActionA extends BaseAction<"A"> {
payload: string
}
interface ActionB extends BaseAction<"B"> {
payload: number
}
interface ActionC extends BaseAction<"C"> {
payload: boolean
}
declare const reducer: Reducer<State, ActionA | ActionB | ActionC>
declare const outerAction$: Observable<ActionA | ActionB>
declare function createState$<S, A>(
reducer: Reducer<S, A>,
initialState: S,
outerAction$: Observable<A>,
optionalParams?: OptionalParams<S, A>,
): Observable<S>
// =========================== Reproducing the problem ===========================================================
const state$1 = createState$(reducer, initialState, outerAction$)
declare const someEffect: Effect<ActionA | ActionB | ActionC, State>
const state$2 = createState$(reducer, initialState, outerAction$, {
effects: [
someEffect,
]
})
如果启用了--sctrictFunctionTypes
选项,则在上一节中悬停createState$
函数将显示推断出的一般A
不是ActionA | ActionB | ActionC
类型,而是ActionA | ActionB
类型。我不了解--sctrictFunctionTypes
如何影响联合推理。我以为打字稿会尝试推断最常见的联合类型。这些知识对我来说是必不可少的,因为对于定义为嵌入式箭头函数的效果,我需要将action$
参数的类型设为Subject<ActionA | ActionB | ActionC>
。不仅如此:如果在someEffect
下添加箭头功能效果,我的项目中的打字稿(v3.1 +)会显示一个错误,提示Types of property 'effects' are incompatible
。
谢谢。
答案 0 :(得分:1)
您可能知道,TypeScript通过将参数类型与参数类型进行匹配并为每个类型参数生成一组“推论”来推断函数调用的类型参数的类型参数。每个推论是“协变”还是“逆变”,这取决于推论的类型是否与协变位置或协变位置中类型参数的出现相匹配。然后有规则根据协变和反推论的集合确定类型参数的最终推断类型。 strictFunctionTypes
会影响某些位置是协变还是逆变,从而改变最终结果。我还没有弄清您的示例中发生的情况的细节;如果您好奇,请查看TypeScript checker中的inferTypes
和getInferredType
函数。
我了解您的实际问题是,您正在(a, s) => { ... }
数组中编写内联箭头函数effects
,并且希望a
获得上下文类型Subject<ActionA | ActionB | ActionC>
因此您可以使用a.next
来调用ActionC
。我可以想到几种实现此目的的方法:
通过使用虚拟交集,将A
参数类型的outerAction$
中createState$
的出现的“推理优先级”降低:
declare function createState$<S, A>(
reducer: Reducer<S, A>,
initialState: S,
outerAction$: Observable<A & {dummy?: undefined}>,
optionalParams?: OptionalParams<S, A>,
): Observable<S>
然后,来自A
的{{1}}的推断将比来自reducer
的推断优先。但是,更改outerAction$
的类型可能会产生不良后果。
向outerAction$
添加类型为A
的伪属性,以便Reducer
参数为您提供协变推断,而不仅仅是协变推断:
reducer
但是,这可能会对export interface Reducer<S, A> {
(state: S, action: A): S;
_dummy?: A;
}
界面的其他使用产生不良影响。
Reducer
的调用上传递显式类型参数。createState$
分解为一个使用减速器的咖喱函数,然后返回另一个使用其余参数的函数。然后,createState$
和S
的类型参数将在第一个函数调用期间基于化简器进行固定,并且不受A
的影响。