Typescript: Inferring types: curried vs not curried

Created on 4 Apr 2017  路  4Comments  路  Source: microsoft/TypeScript

TypeScript Version: 2.2.2

This possible typings of props function from ramda for two arguments:

I wonder why if second object argument is not curried TS can not infer types in results:

declare function props<P1 extends string, P2 extends string, T1, T2>
  (ps: [P1, P2], obj: {[K in P1]: T2} & {[K in P2]: T2}): [T1, T2];

props(['a', 'b'], {a: 1, b: '2'})[0] => // can not infer `number` type
props(['a', 'b'], {a: 1, b: '2'})[1] => // can not infer `string` type

And when curried it can:

// this is inferred
declare function props<P1 extends string, P2 extends string>
  (ps: [P1, P2]): <T1, T2>(obj: {[K in P1]: T1} & {[K2 in P2]: T2}) 
  => [T1, T2];

props(['a', 'b'])({a: 1, b: '2'})[0] => // this can infer `number` type
props(['a', 'b'])({a: 1, b: '2'})[1] => // this can infer `string` type

Is TS going to make it work in future?

Question

Most helpful comment

Type inference will also work with this declaration:

declare function props<P1 extends string, P2 extends string, O extends {[K in P1 | P2]: any}>
  (ps: [P1, P2], obj: O): [O[P1], O[P2]];

In your first example, the mapped types for obj are additional inference sites for P1 and P2, which results in them being both inferred as 'a' | 'b'. In the curried case, P1 and P2 are fixed as 'a' and 'b', respectively, when the second function gets called, and therefore limit what each of the two intersected types matches.

All 4 comments

Type inference will also work with this declaration:

declare function props<P1 extends string, P2 extends string, O extends {[K in P1 | P2]: any}>
  (ps: [P1, P2], obj: O): [O[P1], O[P2]];

In your first example, the mapped types for obj are additional inference sites for P1 and P2, which results in them being both inferred as 'a' | 'b'. In the curried case, P1 and P2 are fixed as 'a' and 'b', respectively, when the second function gets called, and therefore limit what each of the two intersected types matches.

@PyroVortex
Is it possible for this case to limit ps tuple size to 2? Or prohibit props that are not in obj

So it wouldn't allow:

// should be an error, as 'whatever' is not in {a: 1, b: '2'}`
props(['a', 'b', 'whatever'])({a: 1, b: '2'})[0] 

I run into similar problem:

declare const a: { cb: (arg: number) => void }

declare function test1<T>(a: T, b: T): void;
test1(a, { cb(arg) { } }) // parameter 'arg' implicitly has an 'any' type

declare function test2<T>(a: T, b: () => T): void;
test2(a, () => ({ cb(arg) { } })) // parameter 'arg' implicitly has an 'any' type

declare function test3<T>(a: T): (b: T) => void // curried
test3(a)({ cb(arg) { } }) // (parameter) arg: number

declare function test4<T>(a: T): (b: () => T) => void // curried
test4(a)(() => ({ cb(arg) { } })) // (parameter) arg: number

Looks like there're some related issues: https://github.com/Microsoft/TypeScript/issues/23429, https://github.com/Microsoft/TypeScript/issues/22715 https://github.com/Microsoft/TypeScript/issues/25092

This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow.

Was this page helpful?
0 / 5 - 0 ratings