[英]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!!!!!"
下面是一個靈活大小定義的示例,它的容量有限,但對於大多數應用程序來說應該足夠大,並且您始終可以遵循該模式來擴展它。 我不建議使用它,因為它非常混亂,但我想我會把它放在一起是為了好玩並展示這個概念。
在幕后,它使用您的 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."));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.