簡體   English   中英

如何在打字稿中鍵入管道函數?

[英]How do I typed a pipe function in typescript?

這是普通 ol' js 中的管道函數:

 const pipe = (f, ...fs) => x => f === undefined ? x : pipe(...fs)(f(x)) const foo = pipe( x => x + 1, x => `hey look ${x * 2} a string!`, x => x.substr(0, x.length) + Array(5).join(x.substring(x.length - 1)), console.log ) foo(3) // hey look 8 a string!!!!!

(取自這個答案)

我如何用類型在打字稿中寫同樣的東西?

即當我在管道函數時,我可以從當前最后一個函數的返回類型中獲取類型信息

原始(仍然推薦)答案

可悲的是,這在 Typescript 中目前是不可能的,除非您准備為您可能想要的每個長度定義pipe ,這似乎不是很有趣。

但是你可以靠近!

此示例使用受Promise啟發的then來鏈接函數,但您可以根據需要重命名它。

// Alias because TS function types get tedious fast
type Fn<A, B> = (_: A) => B;

// Describe the shape of Pipe. We can't actually use `class` because while TS
// supports application syntax in types, it doesn't in object literals or classes.
interface Pipe<A, B> extends Fn<A, B> {
  // More idiomatic in the land of FP where `pipe` has its origins would be
  // `map` / `fmap`, but this feels more familiar to the average JS/TS-er.
  then<C>(g: Fn<B, C>): Pipe<A, C>
}

// Builds the `id` function as a Pipe.
function pipe<A>(): Pipe<A, A> {
  // Accept a function, and promise to augment it.
  function _pipe<A, B>(f: Fn<A, B>): Pipe<A, B> {
    // Take our function and start adding stuff.
    return Object.assign(f, {
      // Accept a function to tack on, also with augmentations.
      then<C>(g: Fn<B, C>): Pipe<A, C> {
        // Compose the functions!
        return _pipe<A, C>(a => g(f(a)));
      }
    });
  }
  // Return an augmented `id`
  return _pipe(a => a);
}

const foo = pipe<number>()
  .then(x => x + 1)
  .then(x => `hey look ${x * 2} a string!`)
  .then(x => x.substr(0, x.length) + Array(5).join(x.substring(x.length - 1)))
  .then(console.log);

foo(3); // "hey look 8 a string!!!!!"

在 Typescript Playground 上查看

編輯:危險區域

下面是一個靈活大小定義的示例,它的容量有限,但對於大多數應用程序來說應該足夠大,並且您始終可以遵循該模式來擴展它。 我不建議使用它,因為它非常混亂,但我想我會把它放在一起是為了好玩並展示這個概念。

在幕后,它使用您的 JS 實現(以類型安全的方式實現它是可能的,但很費力),在現實世界中,您可能只是將它放在一個 JS 文件中,將此簽名更改為declare function ,然后刪除執行。 TS 不會讓您在沒有抱怨的情況下在單個文件中執行此操作,因此我只是為示例手動連接了它。

筆記:

  • 為避免推理問題,您需要為鏈中的第一個函數或返回的函數注釋參數類型。 后者對我來說似乎更整潔,所以這就是我在示例中使用的
  • 我的條件類型為錯誤留下了一些空間。 如果您在第一個和最后一個定義的參數之間提供任何未定義的參數,我斷言的類型可能不正確。 不過應該可以解決這個問題,我現在無法面對它😉
    type Fn<A, B> = (_: A) => B;
    const Marker: unique symbol = Symbol();
    type Z = typeof Marker;

    function pipe<
      A,
      B,
      C = Z,
      D = Z,
      E = Z,
      F = Z,
      G = Z,
      H = Z,
      I = Z,
      J = Z,
      K = Z
    >(
      f: Fn<A, B>,
      g?: Fn<B, C>,
      h?: Fn<C, D>,
      i?: Fn<D, E>,
      j?: Fn<E, F>,
      k?: Fn<F, G>,
      l?: Fn<G, H>,
      m?: Fn<H, I>,
      n?: Fn<I, J>,
      o?: Fn<J, K>
    ): Fn<
      A,
      K extends Z
        ? J extends Z
          ? I extends Z
            ? H extends Z
              ? G extends Z
                ? F extends Z
                  ? E extends Z
                    ? D extends Z
                      ? C extends Z
                        ? B
                        : C
                      : D
                    : E
                  : F
                : G
              : H
            : I
          : J
        : K
    > {
      // @ts-ignore
      const pipe = (f, ...fs) => x => f === undefined ? x : pipe(...fs)(f(x));
      return pipe(f, g, h, i, j, k, l, m, n, o);
    }

    // Typechecks fine.
    const foo: Fn<number, void> = pipe(
      x => x + 1,
      x => `hey look ${x * 2} a string!`,
      x => x.substr(0, x.length) + Array(5).join(x.substring(x.length - 1)),
      console.log
    )

    foo(3) // hey look 8 a string!!!!!

    // Typechecks fine with fewer params.
    const bar: Fn<string, Date> = pipe(
      x => x + 1,
      _ => new Date()
    );
    console.log(bar("This string is ignored, but we'll put a date in console."));

在 TS Playground 上看看這個怪物

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM