【问题标题】:TypeScript doesn't infer correct typeTypeScript 不能推断出正确的类型
【发布时间】:2018-06-04 17:05:08
【问题描述】:

对于这件作品:

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

TypeScript 没有提及a.splita。我预计它会失败:

“字符串”类型上不存在属性“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 2.6.2 重现此问题,那么您使用的是什么版本?
  • @cartant 我也没有注意到句子中的否定。 OP 期待一个他没有观察到的编译错误。我也是,没有看到错误。

标签: typescript types functional-programming static-typing


【解决方案1】:

感谢您指出我不太明白的问题。我确实更多地使用了您的代码,这是我的发现。类型推断不适用于 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) =&gt; T 的类型推断失败,但适用于非泛型返回类型函数 (T) =&gt; string。在路上,identityResultany 类型,而 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] 类型“字符串”上不存在属性“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);

会达到同样的效果:

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

【讨论】:

  • 哦,你也不知道我对此有多希望。但是您可能错过了我希望all 函数能够接收多个参数的类型:all(identity, identity..),因此是重载。不幸的是,重新排序类型会引入两个问题:type from aboveall 中的number of arguments
  • 感谢您的努力,伊戈尔!
  • @Birowsky 现在我想,我们对why 有了答案,这正在发生,但不知道如何克服它。 :/ 如果你找到让 TS 推断类型的方法,我会为这个问题加注星标。
  • 我会为这项工作投赞成票:} 太糟糕了,我们仍然没有这个功能。它会像诗歌一样读起来,现在它看起来像日常政治。
  • 好吧,好消息是,他们认为这是一个错误:github.com/Microsoft/TypeScript/issues/…
猜你喜欢
  • 1970-01-01
  • 2021-12-21
  • 2015-10-31
  • 1970-01-01
  • 2021-02-11
  • 2017-03-08
  • 1970-01-01
  • 2018-07-03
相关资源
最近更新 更多