繁体   English   中英

为什么 Typescript 不能推断嵌套泛型函数的类型?

[英]Why can't Typescript infer the type of a nested generic function?

Typescript 将下面的applyReducer调用的state参数applyReducerunknown 如果我用applyReducer<State>显式指定类型,它会起作用,但为什么这是必要的? 似乎很清楚类型应该是State

(打字稿 v4.1.2)

减速器Flow.ts:

type UnaryReducer<S> = (state: S) => S

export const applyReducer = <S>(reducer: UnaryReducer<S>) =>
  (state: S) => reducer(state)

interface State { a: number, b: number }
const initialState: State = { a: 0, b: 0 }

// Why is the type of state unknown?
// Typescript can't infer that it is State?
const foo = applyReducer(
    state => ({ ...state, b: state.b + 1 })
)(initialState)

TS游乐场

更新这是我的答案:

例子:


type UnaryReducer = <S>(state: S) => S

interface ApplyReducer {
  <T extends UnaryReducer>(reducer: T): <S,>(state: ReturnType<T> & S) => ReturnType<T> & S;
}

export const applyReducer: ApplyReducer = (reducer) =>
  (state) => reducer(state)


interface State { a: number, b: number }
const initialState: State = { a: 0, b: 0 }


const bar = applyReducer(
  state => ({ ...state, b: 2, })
)(initialState)
bar // {b: number; } & State

const bar2 = applyReducer(
  state => ({ ...state, b: '2', }) 
)(initialState) // Error: b is not a string

const bar3 = applyReducer(
  state => ({ ...state, b: 2, c:'2' }) 
)(initialState) // Error: Property 'c' is missing in type 'State'
 
const bar4 = applyReducer(
  state => ({ ...state }) 
)(initialState) // Ok

const bar5 = applyReducer(
  state => ({ a: 0, b: 0 }) // Error: you should always return object wich is extended by State
)(initialState)

const bar6 = applyReducer(
  state => ({...state, a: 0, b: 0 }) // Ok
)(initialState)

我们应该直接为箭头函数定义泛型参数

type UnaryReducer = <S>(state: S) => S

我们应该以某种方式结合initialState减速机的参数和返回类型

interface ApplyReducer {
  <T extends UnaryReducer>(reducer: T): <S,>(state: ReturnType<T> & S) => ReturnType<T> & S;
}

这意味着reducer(回调)的state参数应该始终是返回类型的一部分。

这意味着,如果您尝试:

state => ({ a:0, b: 2, }) 

这是行不通的,但我认为没有必要这样做

回答你的问题:

如果我使用 applyReducer 显式指定类型,它会起作用,但为什么需要这样做?

问题在于这是一个柯里化函数,即(x) => (y) => z 因为类型参数S在第一个函数上,它会在您调用该函数时立即“实例化”(获得具体类型)。 您可以通过查看下面的fn类型来了解实际情况:

const fn = applyReducer((state) => ({ ...state, b: 2 }))
//                                    ^^^^^^^^ error (because you can't spread 'unknown')
//
// const fn: (state: unknown) => unknown

因为在参数(state) => ({ ...state, b: 2 })中没有关于S应该变成什么的信息,打字稿默认为unknown 所以S现在是未知的,不管你后来用什么来调用fn ,它都会保持未知。

解决此问题的一种方法是 - 正如您所提到的 - 明确提供类型参数:

const fna = applyReducer<State>((state) => ({ ...state, b: 2 }))

另一种方法是为打字稿提供一些信息,从中可以推断S的类型,例如对state参数的类型约束:

const fnb = applyReducer((state: State) => ({ ...state, b: 2 }))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM