TypeScript没有推断出正确的类型

时间:2017-12-22 01:22:36

标签: typescript types functional-programming static-typing

对于这件作品:

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

TypeScript没有提及a.splita的任何内容。我期待它失败:

  

Property' splita'类型'字符串'上不存在。你的意思是   '分裂'?

以下是代码的其余部分:

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;
}

你能发现问题所在吗?

1 个答案:

答案 0 :(得分:3)

感谢您指出我没有得到的问题。我确实更多地使用了你的代码,这是我的发现。类型推断不适用于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而破坏,但适用于非泛型返回类型函数(T) => string。在路上,identityResult的类型为any,而toStringResult的类型为string

enter image description here

我认为,这个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&#39; splita&#39;类型&#39;字符串&#39;上不存在。你是说&#39;分裂&#39;?

enter image description here enter image description here

变体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);

它将实现相同的结果:

enter image description here

我希望,我的答案现在完整正确。 :)

<击>