返回返回curried函数的函数

时间:2016-11-04 15:08:34

标签: typescript

假设我们有像ramda.assoc(http://ramdajs.com/docs/#assoc

这样的关联函数

此功能用于设置对象的属性。因此,我们希望使用一个属性名称参数为函数变量进行打字。

该函数接受一个参数属性名称并返回一个curried函数,该函数可能需要一个或两个参数:

  • 如果它需要两个参数(值和对象),它会在属性上设置值 对象并返回属性设置为value的已修改对象。

  • 如果它需要一个参数(值),则返回一个需要修改对象的函数。

是否可以进行一些打字,以便TS可以处理这两种情况?这是我的看法,但它不起作用:

interface Assoc {
  // we may describe both signatures but only first will work
  (prop: string): <T, U>(val: T, obj: U) => U;
  (prop: string): <T>(val: T) => <U>(obj: U) => U;  
}

// implementation
const assoc: Assoc = (prop: string): any => {
   ....
}

// then there is two function for testing both cases    

// this one expects function with one arg that returns function
const map = (func: (some: string) => (x: number) => 1) => {
} 

// this one expects function with two args that returns value
const map2 = (func: (some: string, other: string) => 1) => {

} 

map(assoc('xxx')) // gives an error

map2(assoc('xxx')) //works ok

我希望两者兼顾。

2 个答案:

答案 0 :(得分:1)

我不认为这是可能的 两个签名都需要相同的参数,因此编译器无法推断出要采用的签名,第一个签名可能是默认行为。

这很有道理,因为当编译器认为你可能犯了错误时,他会试图照顾你并且错误。
在这种情况下,编译器没有足够的信息来知道您是否正确,因为assoc可以仅根据两种情况下相同的输入返回不同类型。

如果您尝试在打字稿中为这样的方法编写实现,编译器就不会让您:

function fn(base: number): (a: number) => number;
function fn(base: number): (a: number, b: number) => string;
function fn(base: number) { // Error: No best common type exists among return expressions
    if (base < 10) {
        return (a: number) => base * a;
    }

    return (a: number, b: number) => "";
}

code in playground

但是如果您正在使用正在执行此操作的js库,那么您可以通过断言来告诉编译器您知道自己在做什么:

type Assoc1 = (prop: string) => <T, U>(val: T, obj: U) => U;
type Assoc2 = (prop: string) => <T>(val: T) => <U>(obj: U) => U;
type Assoc = Assoc1 | Assoc2;

const assoc: Assoc = (prop: string) => {
    return null;
}

const map = (func: (some: string) => (x: number) => number) => {} 

const map2 = (func: (some: string, other: string) => string) => {}

map((assoc as Assoc2)('xxx'));
map2((assoc as Assoc1)('xxx'));

code in playground

它不那么优雅,但我很确定你无法绕过它。
如果有人能想出办法,我很乐意看到它。

答案 1 :(得分:0)

@ nitzan-托默

你知道,似乎有更好的解决方案:

interface CurriedFunction {
  <T1, T2, R>(t1: T1, t2: T2): R;
  <T1>(t1: T1): <T2, R>(t2: T2) => R;
}

interface Assoc {
  (prop: string): CurriedFunction     
}

对此主题进行了很好的讨论:https://gist.github.com/donnut/fd56232da58d25ceecf1