如何在打字稿中键入管道函数?

Ric*_*ler 6 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!!!!!
Run Code Online (Sandbox Code Playgroud)

(取自这个答案)

我如何用类型在打字稿中写同样的东西?

即当我在管道函数时,我可以从当前最后一个函数的返回类型中获取类型信息

mir*_*han 3

原始(仍然推荐)答案

遗憾的是,目前在 Typescript 中这是不可能的,除非你准备好定义pipe您可能想要的每个长度,这看起来不太有趣。

但你可以靠近!

此示例使用Promise-inspiredthen来链接函数,但您可以根据需要重命名它。

// 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!!!!!"
Run Code Online (Sandbox Code Playgroud)

在 Typescript Playground 上查看

编辑:危险地带

下面是一个灵活大小的定义的示例,该定义的容量有限,但对于大多数应用程序来说应该足够大,并且您始终可以遵循该模式来扩展它。我不建议使用它,因为它非常混乱,但我想我会把它放在一起以获得乐趣并演示这个概念。

它在底层使用您的 JS 实现(以类型安全的方式实现它是可能的,但很费力),在现实世界中,您可能只是将其放入 JS 文件中,将此签名更改为declare function,然后删除实现。不过,TS 不会让您在单个文件中执行此操作而不抱怨,因此我只是手动将其连接起来作为示例。

笔记:

  • 为了避免推理问题,您需要注释链中第一个函数的参数类型或返回的函数。后者对我来说似乎更整洁,所以这就是我在示例中使用的
  • 我的条件类型留下了一些出错的空间。如果您在第一个和最后一个定义的参数之间提供任何未定义的参数,则我断言的类型可能不正确。虽然应该可以解决这个问题,但我现在无法面对它
// 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!!!!!"
Run Code Online (Sandbox Code Playgroud)

在 TS Playground 上看看这个怪物

  • 你知道在 Typescript 4.0 中这是否仍然不可能吗?https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html (2认同)