Home > Enterprise >  Typescript typings for basic recursively curried function
Typescript typings for basic recursively curried function

Time:10-05

I'm trying to define a basic function with 2 signatures to allow basic currying without including any complicated 3rd party libraries.

This is the basic function, it takes 2 arguments, if the second argument is not provided it returns a function that takes the last argument, then it will execute the function.

function concat(x, y) {
  if (arguments.length === 1) {
    return (_y) => concat(x, _y);
  }
  return x.concat(y);
}

If I use a separate index.d.ts typescript definition file, I can use the following types.

export function concat<T>(x: T, y: T): T;
export function concat<T>(x: T): (y: T) => T;

However, if I overload the function in a ts file I get typedef errors.

export function concat<T>(x: T, y: T): T;
export function concat<T>(x: T): (y: T) => T {
  if (arguments.length === 1) {
    return (_y) => concat(x, _y);
  }
  return x.concat(y); // Type error: TS2304: Cannot find name 'y'.
}

Is there something I am missing in the type definition inline in the ts file?

function concat(x, y) {
  if (arguments.length === 1) {
    return (_y) => concat(x, _y);
  }
  return x.concat(y);
}

// both args provided
console.log(
  concat(['one'], ['two'])
);

// curried
console.log(
  concat(['one'])(['two'])
);

CodePudding user response:

You've mixed the overload declarations with the implementation. They need to be separate, regardless whether they are in the same file or not:

export function concat<T>(x: T, y: T): T;
export function concat<T>(x: T): (y: T) => T;
export function concat(x, y?) {
  if (arguments.length === 1) {
    return (_y) => concat(x, _y);
  }
  return x.concat(y);
}

CodePudding user response:

Your overload signatures are independent of the function declaration itself. Your function declaration should sport a signature that effectively "combines" the others. Here, that just means y has to be optional (since it is required in only one of the two overloads):

export function concat<T extends { concat(other: T): T }>(x: T, y: T): T;
export function concat<T extends { concat(other: T): T }>(x: T): (y: T) => T;
export function concat<T extends { concat(other: T): T }>(x: T, y?: T) {
  if (typeof y === "undefined") {
    return (y: T) => concat(x, y);
  }

  return x.concat(y);
}

Also, you will probably noticed that I have changed arguments.length === 1 to typeof y === "undefined". This is just more "modern" and integrates better with TypeScript in a lot of cases (but not this one... sadly). And in the returned function, you have to add a type annotation to the y parameter.

I have added a generic constraint as well so when you attempt to call x.concat it no longer errors. In case you'll use this for other things that have a concat method, I chose { concat(other: T): T } as the type.

Here is a playground, but I am unsure how well this applies to your actual use case...

  • Related