Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Typescript 通过更改累加器类型来减少类型脚本_Typescript - Fatal编程技术网

Typescript 通过更改累加器类型来减少类型脚本

Typescript 通过更改累加器类型来减少类型脚本,typescript,Typescript,我试图让一个类似的操作员接受一个程序列表,然后以流水线的方式处理它们。但是,Typescript无法检查Reduce的动态类型,因为累加器具有基于上一次执行的类型。有没有合适的方法 const pipe = (...fns:Function[])=>{ return fns.reduce((prevRes,curFn)=>curFn(prevRes),undefined) } pipe( ()=>true, (res:string)=>"res

我试图让一个类似的操作员接受一个程序列表,然后以流水线的方式处理它们。但是,Typescript无法检查Reduce的动态类型,因为累加器具有基于上一次执行的类型。有没有合适的方法

const pipe = (...fns:Function[])=>{
   return fns.reduce((prevRes,curFn)=>curFn(prevRes),undefined)
}
pipe(
   ()=>true,
   (res:string)=>"res should be boolean, this should yell at you"
)
请看一下管道函数

export interface UnaryFunction<T, R> { (source: T): R; }

export function pipe<T>(): UnaryFunction<T, T>;
export function pipe<T, A>(fn1: UnaryFunction<T, A>): UnaryFunction<T, A>;
export function pipe<T, A, B>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>): UnaryFunction<T, B>;
export function pipe<T, A, B, C>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>): UnaryFunction<T, C>;
export function pipe<T, A, B, C, D>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>): UnaryFunction<T, D>;
export function pipe<T, A, B, C, D, E>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>, fn5: UnaryFunction<D, E>): UnaryFunction<T, E>;
export function pipe<T, A, B, C, D, E, F>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>, fn5: UnaryFunction<D, E>, fn6: UnaryFunction<E, F>): UnaryFunction<T, F>;
export function pipe<T, A, B, C, D, E, F, G>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>, fn5: UnaryFunction<D, E>, fn6: UnaryFunction<E, F>, fn7: UnaryFunction<F, G>): UnaryFunction<T, G>;
export function pipe<T, A, B, C, D, E, F, G, H>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>, fn5: UnaryFunction<D, E>, fn6: UnaryFunction<E, F>, fn7: UnaryFunction<F, G>, fn8: UnaryFunction<G, H>): UnaryFunction<T, H>;
export function pipe<T, A, B, C, D, E, F, G, H, I>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>, fn5: UnaryFunction<D, E>, fn6: UnaryFunction<E, F>, fn7: UnaryFunction<F, G>, fn8: UnaryFunction<G, H>, fn9: UnaryFunction<H, I>): UnaryFunction<T, I>;
export function pipe<T, A, B, C, D, E, F, G, H, I>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>, fn5: UnaryFunction<D, E>, fn6: UnaryFunction<E, F>, fn7: UnaryFunction<F, G>, fn8: UnaryFunction<G, H>, fn9: UnaryFunction<H, I>, ...fns: UnaryFunction<any, any>[]): UnaryFunction<T, {}>;
为了使类型推断对运算符起作用,它们必须定义一组重载函数签名,并使用通用占位符来填充运算符签名。根据传递给管道的运算符数量,函数决定使用哪种重载,最多九个,超过九个,它将返回到任何重载

我已经做了一个简化的例子,你可以使用你的代码来玩

这并不是Typescript最优雅的用法,但它是可以接受的,因为在一个管道中需要这么多操作符可能并不常见。如果您确实需要更多,您可以将另一个调用链接到管道

幸运的是,最近发布的Typescript 4.0极大地改进了对和推断的支持,这可能提供了一个更好的解决方案,不需要大量函数签名重载。RxJS维护人员的路线图。

查看管道功能

export interface UnaryFunction<T, R> { (source: T): R; }

export function pipe<T>(): UnaryFunction<T, T>;
export function pipe<T, A>(fn1: UnaryFunction<T, A>): UnaryFunction<T, A>;
export function pipe<T, A, B>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>): UnaryFunction<T, B>;
export function pipe<T, A, B, C>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>): UnaryFunction<T, C>;
export function pipe<T, A, B, C, D>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>): UnaryFunction<T, D>;
export function pipe<T, A, B, C, D, E>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>, fn5: UnaryFunction<D, E>): UnaryFunction<T, E>;
export function pipe<T, A, B, C, D, E, F>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>, fn5: UnaryFunction<D, E>, fn6: UnaryFunction<E, F>): UnaryFunction<T, F>;
export function pipe<T, A, B, C, D, E, F, G>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>, fn5: UnaryFunction<D, E>, fn6: UnaryFunction<E, F>, fn7: UnaryFunction<F, G>): UnaryFunction<T, G>;
export function pipe<T, A, B, C, D, E, F, G, H>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>, fn5: UnaryFunction<D, E>, fn6: UnaryFunction<E, F>, fn7: UnaryFunction<F, G>, fn8: UnaryFunction<G, H>): UnaryFunction<T, H>;
export function pipe<T, A, B, C, D, E, F, G, H, I>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>, fn5: UnaryFunction<D, E>, fn6: UnaryFunction<E, F>, fn7: UnaryFunction<F, G>, fn8: UnaryFunction<G, H>, fn9: UnaryFunction<H, I>): UnaryFunction<T, I>;
export function pipe<T, A, B, C, D, E, F, G, H, I>(fn1: UnaryFunction<T, A>, fn2: UnaryFunction<A, B>, fn3: UnaryFunction<B, C>, fn4: UnaryFunction<C, D>, fn5: UnaryFunction<D, E>, fn6: UnaryFunction<E, F>, fn7: UnaryFunction<F, G>, fn8: UnaryFunction<G, H>, fn9: UnaryFunction<H, I>, ...fns: UnaryFunction<any, any>[]): UnaryFunction<T, {}>;
为了使类型推断对运算符起作用,它们必须定义一组重载函数签名,并使用通用占位符来填充运算符签名。根据传递给管道的运算符数量,函数决定使用哪种重载,最多九个,超过九个,它将返回到任何重载

我已经做了一个简化的例子,你可以使用你的代码来玩

这并不是Typescript最优雅的用法,但它是可以接受的,因为在一个管道中需要这么多操作符可能并不常见。如果您确实需要更多,您可以将另一个调用链接到管道


幸运的是,最近发布的Typescript 4.0极大地改进了对和推断的支持,这可能提供了一个更好的解决方案,不需要大量函数签名重载。RxJS维护人员在他们的路线图上。

我可以表示您想要的约束,但它对类型推断有一些不幸的副作用。如果您愿意显式地注释所有回调的参数,那就太好了。如果您希望编译器为您推断出这些函数,那么恐怕您不会比为每个可能的函数数列出一个重载列表做得更好,如另一个答案中所示

无论如何,让我们介绍一下这些帮助程序:

type Last<T extends any[]> = T extends [...infer _, infer U] ? U : never;

type Prev<T extends any[], K extends keyof T, D> = 
  K extends keyof [D, ...T] ? [D, ...T][K] : never;
我们希望管道在元组T中是泛型的,元组T对应于作为fns传递的函数的返回类型的有序列表。类型{[K in keyof T]:arg:Prev=>T[K]}就是我们需要的函数类型链;对于T中的每个索引K,结果函数的输入类型是上一个索引处的元素,如果没有上一个索引Prev,则为未定义的元素,而输出类型是T[K]的当前元素。管道的输出就是最后一个,返回类型元组中的最后一个元素

让我们看看它是如何工作的。好人:

const good = pipe(
  () => true,
  (res: boolean) => res ? 123 : 456,
);
// const good: 123 | 456
编译时没有错误,val被推断为123 | 456类型。坏的方面是:

const bad = pipe(
  () => true,
  (res: string) => res // error!
//~~~~~~~~~~~~~~~~~~~~
//Type 'boolean' is not assignable to type 'string'
)
这正是您想要的错误;编译器希望res的类型为boolean,但您已将其注释为string。现在来看看丑陋的人:

const ugly = pipe(
  () => "hello", x => x.length
); // any!!!
哎呀,我忘了在回调中注释x。编译器不再能够从返回类型=>hello中上下文地键入x,而是放弃并隐式地使用任何。这将导致整个元组类型T被推断为any[],并且所有类型安全性都将丢失。该故障可能是由于所需推理的循环性质造成的;元组类型T依赖于回调的类型,而回调的类型本身也依赖于T的类型。如果它能神奇地工作,那就太好了,但事实并非如此

不过,在任何情况下,您都可以表示约束,它甚至可能很有用。只是要小心推理中的陷阱


我可以表示您想要使用的约束,但它对类型推断有一些不幸的副作用。如果您愿意显式地注释所有回调的参数,那就太好了。如果您希望编译器为您推断出这些函数,那么恐怕您不会比为每个可能的函数数列出一个重载列表做得更好,如另一个答案中所示

无论如何,让我们介绍一下这些帮助程序:

type Last<T extends any[]> = T extends [...infer _, infer U] ? U : never;

type Prev<T extends any[], K extends keyof T, D> = 
  K extends keyof [D, ...T] ? [D, ...T][K] : never;
我们希望管道在元组T中是泛型的,元组T对应于作为fns传递的函数的返回类型的有序列表。类型{[K in keyof T]:arg:Prev=>T[K]}就是我们需要的函数类型链;对于T中的每个索引K,结果函数的输入类型是上一个索引处的元素,如果没有上一个索引Prev,则为未定义的元素,而输出类型是T[K]的当前元素。和 管道的输出只是最后一个,返回类型元组中的最后一个元素

让我们看看它是如何工作的。好人:

const good = pipe(
  () => true,
  (res: boolean) => res ? 123 : 456,
);
// const good: 123 | 456
编译时没有错误,val被推断为123 | 456类型。坏的方面是:

const bad = pipe(
  () => true,
  (res: string) => res // error!
//~~~~~~~~~~~~~~~~~~~~
//Type 'boolean' is not assignable to type 'string'
)
这正是您想要的错误;编译器希望res的类型为boolean,但您已将其注释为string。现在来看看丑陋的人:

const ugly = pipe(
  () => "hello", x => x.length
); // any!!!
哎呀,我忘了在回调中注释x。编译器不再能够从返回类型=>hello中上下文地键入x,而是放弃并隐式地使用任何。这将导致整个元组类型T被推断为any[],并且所有类型安全性都将丢失。该故障可能是由于所需推理的循环性质造成的;元组类型T依赖于回调的类型,而回调的类型本身也依赖于T的类型。如果它能神奇地工作,那就太好了,但事实并非如此

不过,在任何情况下,您都可以表示约束,它甚至可能很有用。只是要小心推理中的陷阱


一个问题是,你会发现很难让编译器推断回调函数的参数类型,即使它应该是显而易见的。不幸的是,这不是TS中的自然约束。@jcalz这是一些非常出色的代码,您应该将其作为答案。我很乐意写出一个答案,但我打赌重载列表在许多情况下会表现得更好,尤其是在推理方面。一个问题是,你会发现很难让编译器推断回调函数的参数类型,即使它应该是显而易见的。不幸的是,这不是TS中的自然约束。@jcalz这是一些非常出色的代码,您应该将其作为答案。我很乐意写出一个答案,但我打赌重载列表在许多情况下实际上会表现得更好,尤其是在推理方面。谢谢!我抛硬币接受了jcalz的回答,但你的回答也很好。谢谢!我抛硬币接受了jcalz的答案,但你的答案也很好。