TypeScript 无法推断出正确的类型

TypeScript doesn't infer correct type

对于这篇文章:

Promise.resolve('kromid')
  .then(all(identity))
  .then(([a]) => a.splita);

TypeScript 没有说明任何关于 a.splita 的内容。我预计它会失败:

Property 'splita' does not exist on type 'string'. Did you mean 'split'?

下面是代码的其余部分:

function all<T1, Param>(a1: Res<Param, T1>): (p: Param) => Promise<[T1]>;
function all<T1, T2, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>): (p: Param) => Promise<[T1, T2]>;
function all<T1, T2, T3, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>): (p: Param) => Promise<[T1, T2, T3]>;
function all<T1, T2, T3, T4, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>): (p: Param) => Promise<[T1, T2, T3, T4]>;
function all<T1, T2, T3, T4, T5, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>): (p: Param) => Promise<[T1, T2, T3, T4, T5]>;
function all<T1, T2, T3, T4, T5, T6, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6]>;
function all<T1, T2, T3, T4, T5, T6, T7, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6, T7]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, T9, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>, a9: Res<Param, T9>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>, a9: Res<Param, T9>, a10: Res<Param, T10>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
function all<Param>(...values: Res<Param, any>[]): (p: Param) => Promise<any[]>;
function all<Param>(...values: Res<Param, any>[]): (p: Param) => Promise<any[]> {
  return param => Promise.all(values.map(obj => obj.apply ? obj(param) : obj));
}
type Res<I, O> = ((i: I) => O | Promise<O>) | O | Promise<O>


function identity<T>(a: T): T {
  return a;
}

你能找出问题所在吗?

感谢您指出我不太明白的问题。我确实更多地使用了您的代码,这是我的发现。类型推断不适用于 TypeScript 中的函数组合:

function identity<T>(a: T): T { return a; }
function toString<T>(a: T): string { return JSON.stringify(a); }

Promise.resolve('kromid')
  .then(promiseValue => {
    const composedFn = all(identity, toString);
//        ^^^^^^^^^^
//        T type of `promiseValue` was not inferred properly,
//        and was replaces with `any`
    return composedFn(promiseValue);
  })
  .then(([identityResult, toStringResult]) => {

  })

请注意,类型推断对函数 (T) => T 无效,但对非泛型 return 类型的函数 (T) => string 有效。在路上,identityResult 是类型 any,而 toStringResult 是正确的类型 string

我认为,真正的根本原因包含在这个 GitHub issue 中。抱歉,我的回答不是很有帮助。类型推断对我来说是最复杂的主题,这就是我深入研究您的示例的原因。

抓取我的旧答案

为什么会这样?

我认为,关键在于这一行:

  .then(all(identity))

相当于

  .then(x => all(identity)(x))

而不是

  .then(x => all(identity(x)))

在两种情况下,类型的推断会有所不同,我认为您希望在获得前者的同时对代码进行后一种解释。

如何实现您期望看到的错误

变体 1:如果您重写代码以便 all() 以正确的顺序接收 string 承诺值和 identity() ,您会看到错误。

Promise.resolve('kromid')
  .then(text => all(text)(identity))
  .then(([a]) => a.splita);

导致:

[ts] Property 'splita' does not exist on type 'string'. Did you mean 'split'?

变体 2。重新排序重载的 all() 函数的参数,以便它能够以您想要的方式使用它:

function all<T1, Param>(p: Param): (a1: Res<Param, T1>) => Promise<[T1]>;
function all<T1, T2, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>) => Promise<[T1, T2]>;
function all<T1, T2, T3, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>) => Promise<[T1, T2, T3]>;
function all<T1, T2, T3, T4, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>) => Promise<[T1, T2, T3, T4]>;
function all<T1, T2, T3, T4, T5, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>) => Promise<[T1, T2, T3, T4, T5]>;
function all<T1, T2, T3, T4, T5, T6, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>) => Promise<[T1, T2, T3, T4, T5, T6]>;
function all<T1, T2, T3, T4, T5, T6, T7, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>) => Promise<[T1, T2, T3, T4, T5, T6, T7]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, T9, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>, a9: Res<Param, T9>) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>, a9: Res<Param, T9>, a10: Res<Param, T10>) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
function all<Param>(p: Param): (...values: Res<Param, any>[]) => Promise<any[]>;

function all<Param>(p: Param): (...values: Res<Param, any>[]) => Promise<any[]> {
  return values => Promise.all(values.map((obj: any) => obj.apply ? obj(p) : obj));
}

type Res<I, O> = ((i: I) => O | Promise<O>) | O | Promise<O>;

function identity<T>(a: T): T {
  return a;
}

Promise.resolve('kromid')
  .then(all(identity))
  .then(([a]) => a.splita);

它会达到相同的结果:

我希望,我的回答现在是完整和正确的。 :)