如何让包装函数返回正确的类型?

问题描述 投票:0回答:1

如何让这个reduce函数返回类型Set<1>? (游乐场)

const toSet = <const V>(acc=new Set<V>(),v:V) =>{
  acc.add(v);
  return acc;
}
const set = toSet(undefined,1); // Set<1> correct

const reduce = 
  <NextReducer extends (acc:any,value:any)=>any>(nextReducer:NextReducer) =>
    (acc: unknown, value: unknown) =>
      nextReducer(acc, value);

const reduced = reduce(toSet)(undefined,1); // any (should be Set<1>)
typescript typescript-generics higher-order-functions
1个回答
0
投票

TypeScript 对“从泛型函数进行高阶类型推断”的支持仅在特定的有限情况下有效,如 microsoft/TypeScript#30215 中实现的那样。 一项要求是高阶函数需要在其输入函数的参数和返回类型中单独通用,并且在整个输入函数类型中不是通用的。 意味着您不能像您在问题中所写的那样写

<F extends (acc:any,value:any)=>any>(nextReducer: F)=>⋯

。 相反,您需要写类似

<A, V, R>(nextReducer: (acc: A, value: V) => R)=>⋯
的内容。这将为您提供以下版本的
reduce
const reduce =
  <A, V, R>(nextReducer: (acc: A, v: V) => R) =>
    (acc: A, value: V) => nextReducer(acc, value);

reduce

的结果类型是

<A, V, R>(f: (a: A, v: V) => R) => (a: A, v: V) => R
这比您的版本更安全,因为返回的函数不应具有像 

(acc: unknown, value: unknown)

这样的参数,因为

nextReducer
可能不接受
unknown
参数。 (例如,没有什么可以阻止某人调用
reduce(toSet)("oops", "darn")
并在
"oops".add("darn")
上收到运行时错误)
而且,对于你的问题更重要的是,这允许高阶类型推断起作用:

const reduceToSet = reduce(toSet); // const reduceToSet: <const V>(acc: Set<V> | undefined, value: V) => Set<V>

您可以看到 
reduce(toSet)

保留了

toSet
的泛型。这样你就得到了想要的行为:
const reduced = reduce(toSet)(undefined, 1); 
// const reduce: Set<1>

这就是所问问题的答案。

请注意,如果您实际上打算尝试将

reduce

转换为真正的

归约操作
(对数组或树等递归数据结构进行操作),您可能会发现这更复杂并且保存/传播仿制药可能会更困难,甚至不可能。但这超出了所问问题的范围。

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.