Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does TypeScript not infer types for some piped functions?

Update Feb 20, 2018: posted this as an issue on GitHub.

Update Feb 28: closed that issue in favor of a new one.

In the following code snippet, if you look at the last two lines, TypeScript shows an error in the first one, and correctly infers types in the second one, although the difference is only the order in which the functions are piped.

const pipe = <A, B, C>(
  x: A,
  a: (x: A) => B, 
  b: (x: B) => C,
) => b(a(x));

// This just calls the function passed as argument.
const call = <A, B>(f: (x: A) => B) => (x: A) => f(x)

const a = pipe(1, x => x + 1, call(x => x + 1));
const b = pipe(1, call(x => x + 1), x => x + 1);

I use TypeScript 2.7.1 in the strict mode (including strictFunctionTypes), however the strict mode doesn't seem to matter here. Here is this snippet on TypeScript playground.

It is a problem that I often run into when working with RxJS, since in RxJS there is a similar pipe method and I pass arrow functions to it when using creation operators (like obs => merge(obs, otherObs)). Usually it's easy to work around this problem by specifying the argument type, but I would like to understand the logic behind this. Why is TypeScript able to infer the type in one case but not in the other?

like image 575
Ivan Avatar asked Feb 14 '18 11:02

Ivan


People also ask

Does TypeScript have type inference?

TypeScript infers types of variables when there is no explicit information available in the form of type annotations. Types are inferred by TypeScript compiler when: Variables are initialized. Default values are set for parameters.

Can TypeScript types have functions?

Types of Functions in TypeScript: There are two types of functions in TypeScript: Named Function. Anonymous Function.

What is contextual typing in TypeScript?

contextual typing in typescript is a form of type checking Archives | Agira Technologies.

What would be the best common type inferred by TypeScript?

The best common type algorithm In this case, TypeScript selects the number array type ( number[] ) as the best common type. In this example, TypeScript infers the type for arr to be (RegExp | Date)[] .


1 Answers

This issue is still outstanding, but for now I wanted to post a workaround that I've come up with because I found it very useful in my experience.

Create a utility function like this:

export const call = <A, B>(f: (x: A) => B) => f;

(the same function as in the question, an identity function that takes a unary function as argument), and then whenever you run into trouble with an arrow function in your pipe, try enclosing it with this utility function. Counterintuitively, TS can't handle this:

const a = pipe(1, x => x + 1, call(x => x + 1));

but it can handle this:

const c = pipe(1, call(x => x + 1), call(x => x + 1));

(no idea why). Later when the issue is fixed (go ahead an upvote it: https://github.com/Microsoft/TypeScript/issues/22081), you'll be able to easily find all references to call and remove them.

like image 94
Ivan Avatar answered Sep 22 '22 11:09

Ivan