【问题标题】:"--strictFunctionTypes" and inference of generic union types“--strictFunctionTypes”和泛型联合类型的推断
【发布时间】:2019-04-10 18:08:39
【问题描述】:

我在项目中遇到了一些我完全不清楚的问题

// =========================== 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,
  ]
})

the same code in typescript playground (please switch on/off strictFunctionTypes option and hover createState$ function in the last section)

如果启用--sctrictFunctionTypes 选项,悬停在最后一节中的createState$ 函数将显示泛型A 不是类型ActionA | ActionB | ActionC 而是ActionA | ActionB。我不明白--sctrictFunctionTypes 如何影响联合推理。我认为打字稿会尝试推断最常见的联合类型。这方面的知识对我来说很重要,因为我需要将 action$ 参数的类型设为 Subject&lt;ActionA | ActionB | ActionC&gt;,才能将我的效果定义为内联箭头函数。不仅如此:如果我在someEffect 下方添加箭头功能效果,我会在我的项目中从 typescript (v3.1+) 中收到错误消息Types of property 'effects' are incompatible

谢谢。

【问题讨论】:

    标签: typescript


    【解决方案1】:

    您可能知道,TypeScript 通过将实参类型与形参类型匹配并为每个类型形参生成一组“推断”来推断函数调用的类型形参的类型实参。每个推理要么是“协变的”,要么是“逆变的”,这取决于推理的类型是否匹配类型参数在协变或逆变位置的出现。然后有一些规则根据协变和逆变推断的集合确定类型参数的最终推断类型。 strictFunctionTypes 可以影响某些位置是否被认为是协变或逆变的,从而改变最终结果。我还没有弄清楚你的例子中发生的事情的细节;如果您好奇,请查看 TypeScript checker 中的 inferTypesgetInferredType 函数。

    我了解您的实际问题是您正在 effects 数组中编写内联箭头函数 (a, s) =&gt; { ... } 并且您希望 a 获得 Subject&lt;ActionA | ActionB | ActionC&gt; 的上下文类型,以便您可以调用 a.next带有ActionC。我可以想出几种方法来实现这一点:

    1. 使用虚拟交集将outerAction$参数类型中A中出现的“推理优先级”降低为createState$

      declare function createState$<S, A>(
        reducer: Reducer<S, A>,
        initialState: S,
        outerAction$: Observable<A & {dummy?: undefined}>,
        optionalParams?: OptionalParams<S, A>,
      ): Observable<S>
      

      那么来自reducerA 的推理将优先于来自outerAction$ 的推理。但是,更改 outerAction$ 的类型可能会产生不希望的副作用。

    2. A 类型的虚拟属性添加到Reducer,以便reducer 参数为您提供协变推断,而不仅仅是逆变推断:

      export interface Reducer<S, A> {
        (state: S, action: A): S;
        _dummy?: A;
      }
      

      但是,这可能会对Reducer 接口的其他用途产生不良副作用。

    3. 在调用 createState$ 时传递显式类型参数。
    4. createState$ 分解为一个柯里化函数,该函数接受reducer 并返回另一个接受剩余参数的函数。那么SA的类型参数将在第一次函数调用时根据reducer固定,不受outerAction$的影响。

    【讨论】:

      猜你喜欢
      • 2021-06-05
      • 2020-08-25
      • 2020-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多