typescript 无法正确推断类型

type GetValue<T> = T | (() => T)
type GetValueType<T> = T extends () => any
  ? ReturnType<Extract<T, () => any>> | Exclude<T, () => any>
  : T

declare function f<T extends any>(
  map: [probability: number, value: T][]
): () => GetValueType<T>

f<GetValue<number>>([
  [1, 1],
  [1, () => 1],
])
f([
  [1, 1],
  [1, () => 1], // The type is not correctly inferred
)

image.png

阅读 3.3k
3 个回答

这不是已经正确推断出来了嘛.

f([
  [1, 1], // 从这里可以推断出 T 为 number
  [1, () => 1], // 而这里 T 却不是 number,而是 ()=>1 所以会报错
)

如果需要兼容() => 1,那可以这样定义

declare function f<T extends any>(
  map: [probability: number, value: T | (() => T)][],
): () => GetValueType<T>

union type你得手动告诉他,没法自动推断出来
要么你就修改function f的类型定义

declare function f<T extends any>(
  map: [probability: number, value: GetValue<T>][]
): () => GetValueType<T>
感谢 @robin 邀请。虽然来得比较晚,但是我也大概分析一下这个问题,供参考。

类型 GetValue<T> 我理解为,它是 T 类型的「值」,或者「工厂」。

类型 GetValueType<T> 的不太理解,比如说

type GetValueType<T> = T extends () => any
    ? ReturnType<Extract<T, () => any>> | Exclude<T, () => any>
    : T

const f1: GetValueType<() => number> = 1;

这种情况下,f1 的类型是 ReturnType<() => number>,也就是 number

但是,如果把 ReturnType<...> 这部分去掉,

type GetValueType<T> = T extends () => any
    ? Exclude<T, () => any>
    : T

const f1: GetValueType<() => number> = 1;
// ---^^-----> Type 'number' is not assignable to type 'never'. 

这里 f1 被认定为 never 类型,因为 Exclude<T, () => any> 表示 T 是“一个不是函数的函数”。

我猜题主这里是想定义 GetValue<T> 的值类型,也就是说,如果 T 不是函数,就是 T 这个类型;如果 T 是函数,那就是它的返回类型(正好是「值」或「工厂」的返回值)。那么应该这样定义就好:

// ❶
type GetValueType<T> = T extends () => any
    ? ReturnType<T>
    : T

如果能这么理解的话,那 function f<T> (),我也就能理解了,是接受一个 map 参数(以列表实现的表),其中 map 元素(也是数组)的第 2 个元素,是 T 或者 T 工厂类型(工厂类型是根据下面的调用人脑推测的)。但不管如何,返回类型是一个 T 工厂。

按这个逻辑,f<t> 的声明应该是:

// ❷
declare function f<T>(
    map: [probability: number, value: GetValue<T>][]
    // -------------------------------^^^^^^^^^^^-----------
): () => GetValueType<T>

顺便提一句:f<T extends any>f<T> 没啥区别,对于 T 来说,都等于是没有约束。


接下来就是比较关键的地方了。

在题主的代码中

declare function f<T extends any>(
  map: [probability: number, value: T][]
): () => GetValueType<T>

f<T> 这里,不管是 T 还是 T extends anyT 都表示一个类型(或其子类型,Only one,不是 Any one)。它如果代表了 string,那就不是能是 number;如果代表了 number 就不能是函数……所以在没有泛型参数,也就是 T 类型不明确的情况下,推导……

f([
    [1, 1],
    // -^---- 这里推导出来 T 是 number
    [1, () => 1],
    // -^^^^^^^----- 所以 T 就不能是函数
])

为了明确推导结果,要么在调用 f 的时候具体化 T 类型,也就是 f<GetValue<number>>(...),也就是题主给的第一个 f<GetValue<number>>() 调用;

要么就在申明 map 参数的时候,明确 value 的类型是 GetValue<T>,这样 number() => number 都符合,也就是上面的代码 ❷。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进