Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript parameter type inference failure

I created a bug report for this, but perhaps someone has an idea for a workaround. Basically I want to have some arguments to a function based conditionally on the type of another:

declare function foo<P>(params: { f: (p: P) => void } & P): void;

foo({
  f(params: { bar(x: number): void }) {},
  bar(x) {},
});

So there is a special parameter f which has a function type, and whatever properties that function expects must be included as arguments to foo. Here again the parameter to bar is inferred as any but should be number.

like image 804
Tom Crockett Avatar asked Jul 05 '18 04:07

Tom Crockett


1 Answers

Whenever I see a requirement to have function type that depends on argument values, "use overloads" is the first thing that comes to mind:

declare function foo(params: { tag: 'a', bar(x: number): void }): void;
declare function foo(params: { tag: 'b', bar(x: boolean): void }): void;

foo({ tag: 'a', bar(x) { console.log('x =', x)} }); // (parameter) x: number

Update I assume that in the following code foo and f are React components, and foo exposes f as its property, as well as all the properties that f has.

declare function foo<P>(params: { f: (p: P) => void } & P): void;

foo({
  f(params: { bar(x: number): void }) {},
  bar(x) {},
});

There is a way to rewrite it, it you don't insist that the name of f (which is a property of foo), as well as types of f properties must be declared inline within foo arguments when calling it.

You can declare a type that describes properties of such composed component, it takes two generic parameters: the type of inner component properties, and the name of inner component in outer properties:

type ComposedProps<InnerProps, N extends string> = 
       InnerProps & { [n in N]: (props: InnerProps) => void };  

// Also I assume in reality it does not return `void` but `React.Element`,
//  but I don't think it matters

declare function foo<P>(params: P): void;

// foo generic parameter is given explicitly,
//  which allows to infer (and not repeat) parameter types for `f` and `bar` 
foo<ComposedProps<{bar(x: number): void}, 'f'>>({
  f(params) {}, // params: { bar(x: number): void; }
  bar(x) {}, // (parameter) x: number
});
like image 90
artem Avatar answered Oct 12 '22 13:10

artem