我有时会遇到这种问题,但我不确切地知道为什么推理不起作用.

在这种情况下,zip在直接应用时返回正确的类型,但在通过pipe应用时不返回正确的类型,我不知道为什么.

declare const zip: {
    <A, B>(input: readonly [readonly A[], readonly B[]]): [A, B][],
    <A, B, C>(input: readonly [readonly A[], readonly B[], readonly C[]]): [A, B, C][],
    <A, B, C, D>(input: readonly [readonly A[], readonly B[], readonly C[], readonly D[]]): [A, B, C, D][],
    <A extends readonly any[]>(input: readonly A[]): A[0][][],
};

declare const pipe:  <A, B>(a: A, f: (a: A) => B) => B;

declare const x: [number[], string[], boolean[]];

const foo = pipe(x, zip);
//     ^? foo: any[][]
const bar = pipe(x, x => zip(x))
//     ^? bar: [number, string, boolean][]

playground个 这不是什么大事,但它真的让我感到困惑,因为它可能会浪费时间来理解为什么有些东西不能编译,或者浪费了试图键入一些东西以使其最终不像预期的行为的努力.

顺便说一句,这个pipe函数是以下内容的摘录,它在大多数情况下都有效(显然不是这个),但看起来有点难

const pipe: {
    <A>(
        a: A
    ): A
    <A, B>(
        a: A,
        f: (a: A) => B
    ): B
    <A, B, C>(
        a: A,
        f: (a: A) => B,
        g: (b: B) => C
    ): C
    <A, B, C, D>(
        a: A,
        f: (a: A) => B,
        g: (b: B) => C,
        h: (c: C) => D
    ): D
    <A, B, C, D, E>(
        a: A,
        f: (a: A) => B,
        g: (b: B) => C,
        h: (c: C) => D,
        i: (d: D) => E
    ): E
    <A, B, C, D, E, F>(
        a: A,
        f: (a: A) => B,
        g: (b: B) => C,
        h: (c: C) => D,
        i: (d: D) => E,
        j: (e: E) => F
    ): F
    <A, B, C, D, E, F, G>(
        a: A,
        f: (a: A) => B,
        g: (b: B) => C,
        h: (c: C) => D,
        i: (d: D) => E,
        j: (e: E) => F,
        k: (f: F) => G
    ): G
    <A, B, C, D, E, F, G, H>(
        a: A,
        f: (a: A) => B,
        g: (b: B) => C,
        h: (c: C) => D,
        i: (d: D) => E,
        j: (e: E) => F,
        k: (f: F) => G,
        l: (g: G) => H
    ): H
    <A, B, C, D, E, F, G, H, I>(
        a: A,
        f: (a: A) => B,
        g: (b: B) => C,
        h: (c: C) => D,
        i: (d: D) => E,
        j: (e: E) => F,
        k: (f: F) => G,
        l: (g: G) => H,
        m: (h: H) => I
    ): I
    <A, B, C, D, E, F, G, H, I, J>(
        a: A,
        f: (a: A) => B,
        g: (b: B) => C,
        h: (c: C) => D,
        i: (d: D) => E,
        j: (e: E) => F,
        k: (f: F) => G,
        l: (g: G) => H,
        m: (h: H) => I,
        n: (i: I) => J
    ): J
}

您可能会认为,嘿,使用映射类型,您可以得出如下内容(不要注意细节)

import { Lift, free, Init, Tail } from 'free-types';

declare const pipe: {
    <A,B>(a: A, ...fns: Fns<[A, B]>): B
    <A,B, C>(a: A, ...fns: Fns<[A, B, C]>): C
    <A,B, C, D>(a: A, ...fns: Fns<[A, B, C, D]>): D
    <A,B, C, D, E>(a: A, ...fns: Fns<[A, B, C, D, E]>): E
    <A,B, C, D, E, F>(a: A, ...fns: Fns<[A, B, C, D, E, F]>): F
    <A,B, C, D, E, F, G>(a: A, ...fns: Fns<[A, B, C, D, E, F, G]>): G
    <A,B, C, D, E, F, G, H>(a: A, ...fns: Fns<[A, B, C, D, E, F, G, H]>): H
    <A,B, C, D, E, F, G, H, I>(a: A, ...fns: Fns<[A, B, C, D, E, F, G, H, I]>): I
    <A,B, C, D, E, F, G, H, I, J>(a: A, ...fns: Fns<[A, B, C, D, E, F, G, H, I, J]>): J
};

type Fns<Fs extends [unknown, ...unknown[]]> = Lift<free.UnaryFunction, [Init<Fs>, Tail<Fs>]>

playground

但由于某些原因,它只适用于单态函数.我try 了更简单的设计,但基本上在某一点上会有一种间接的方式会使编译器松动,我真的无法预测它.更长/更简单的pipe版本是您在大多数FP库中找到的版本,这显然是有充分理由的.

你能解释一下zip的情况吗?如果你对更复杂的pipe版本有什么 idea ,请随时与我们分享.

推荐答案

对重载函数的灾难恢复推理为您提供了删除所有泛型的最后一个调用签名.这是Typescript 的设计限制.


由于zip()是具有多个调用签名的overloaded function,因此涉及它的推论只考虑最后一个调用签名,而不是 Select 当您直接调用zip()时所获得的"适当的"调用签名.这是一个已知的设计限制,如microsoft/TypeScript#50432(以及其他)中所述.

所以你得到的行为类似于zip只是

declare const zip: {
  <A extends any[]>(input: A[]): A[0][][],
};

虽然在这种情况下,你实际上会得到一个更具体的结果:

declare const x: [number[], string[], boolean[]];
const foo = pipe(x, zip);
//     ^? const foo: (string | number | boolean)[][]

但是因为您有重载,所以当匹配时,推断实际上是erases类型参数generic,如microsoft/TypeScript#52916中所述(这可能是个错误,但可能没有好的方法来更改它,至少根据这个问题),因此类型参数被它的constraint替换.这意味着它实际上的行为就像

declare const zip: {
  (input: any[][]): any[][0][][],
};

它产生了

const foo = pipe(x, zip);
//     ^? const foo: any[][]

如你所见.


Playground link to code

Typescript相关问答推荐

webpack错误:模块找不到.当使用我的其他库时,

基于键的值的打字动态类型;种类;

TypeScrip原始字符串没有方法

在Typescript 中,有没有`index=unfined的速记?未定义:某个数组[索引]`?

如何实现异构数组内函数的类型缩小

已解决:如何使用值类型限制泛型键?

限制返回联合的TS函数的返回类型

类型TTextKey不能用于索引类型 ;TOption

为什么ESLint会抱怨函数调用返回的隐式`any`?

Angular:ngx-echart 5.2.2与Angular 9集成错误:NG 8002:无法绑定到选项,因为它不是div的已知属性'

如何在Vue中使用Enum作为传递属性的键类型?

不带其他属性的精确函数返回类型

有没有办法取消分发元组的联合?

将超类型断言为类型脚本中的泛型参数

如何从JWT Reaction中提取特定值

使用RXJS获取数据的3种不同REST API

Route.ts文件中未导出HTTP方法

确保财产存在

对象只能使用 Typescript 中另一个对象的键

我是否应该在 Next.js 中的服务器组件中获取秘密数据的所有函数中使用使用服务器?