Skip to content

Type inference fails to apply to functions as args #29775

Closed
@ericanderson

Description

@ericanderson

TypeScript Version: 3.2 and up (possibly older ones too)

Code

declare class Component<P> { 
  constructor(props: P);
}
    
interface ComponentClass<P = {}> {
  new (props: P): Component<P>;
}

type CreateElementChildren<P> =
  P extends { children?: infer C }
  ? C extends any[]
    ? C
    : C[]
  : unknown;

declare function createElement<P extends {}>(
  type: ComponentClass<P>,
  ...children: CreateElementChildren<P>
): any;

declare function createElement2<P extends {}>(
  type: ComponentClass<P>,
  child: CreateElementChildren<P>
): any;
        
// To be sure the optional isn't the problem:
class InferFunctionTypes extends Component<{children: (foo: number) => string}> {}
createElement(InferFunctionTypes, (foo) => "" + foo); // ERROR! Parameter 'foo' implicitly has an 'any' type.
// In the above, P is properly infered on the createElement, per hover tooltip:
// function createElement<{ children: (foo: number) => string; }>(type: ComponentClass<{ children: (foo: number) => string; }>, ...children: ((foo: number) => string)[]): any 

// If you don't expect P to be infered, it works properly:
createElement<{ children: (foo: number) => string }>(InferFunctionTypes, foo => "" + foo);

// Is unrelated to varargs
createElement2(InferFunctionTypes, [(foo) => "" + foo]); // ERROR! Parameter 'foo' implicitly has an 'any' type.

Expected behavior:

Typescript should be able to infer that foo is a number.

Actual behavior:

Parameter 'foo' implicitly has an 'any' type.

Playground Link:
http://www.typescriptlang.org/play/#src=declare%20class%20Component%3CP%3E%20%7B%20%0A%20%20constructor(props%3A%20P)%3B%0A%7D%0A%20%20%20%20%0Ainterface%20ComponentClass%3CP%20%3D%20%7B%7D%3E%20%7B%0A%20%20new%20(props%3A%20P)%3A%20Component%3CP%3E%3B%0A%7D%0A%0Atype%20CreateElementChildren%3CP%3E%20%3D%0A%20%20P%20extends%20%7B%20children%3F%3A%20infer%20C%20%7D%0A%20%20%3F%20C%20extends%20any%5B%5D%0A%20%20%20%20%3F%20C%0A%20%20%20%20%3A%20C%5B%5D%0A%20%20%3A%20unknown%3B%0A%0Adeclare%20function%20createElement%3CP%20extends%20%7B%7D%3E(%0A%20%20type%3A%20ComponentClass%3CP%3E%2C%0A%20%20...children%3A%20CreateElementChildren%3CP%3E%0A)%3A%20any%3B%0A%0Adeclare%20function%20createElement2%3CP%20extends%20%7B%7D%3E(%0A%20%20type%3A%20ComponentClass%3CP%3E%2C%0A%20%20child%3A%20CreateElementChildren%3CP%3E%0A)%3A%20any%3B%0A%20%20%20%20%20%20%20%20%0A%2F%2F%20To%20be%20sure%20the%20optional%20isn't%20the%20problem%3A%0Aclass%20InferFunctionTypes%20extends%20Component%3C%7Bchildren%3A%20(foo%3A%20number)%20%3D%3E%20string%7D%3E%20%7B%7D%0AcreateElement(InferFunctionTypes%2C%20(foo)%20%3D%3E%20%22%22%20%2B%20foo)%3B%20%2F%2F%20ERROR!%20Parameter%20'foo'%20implicitly%20has%20an%20'any'%20type.%0A%2F%2F%20In%20the%20above%2C%20P%20is%20properly%20infered%20on%20the%20createElement%2C%20per%20hover%20tooltip%3A%0A%2F%2F%20function%20createElement%3C%7B%20children%3A%20(foo%3A%20number)%20%3D%3E%20string%3B%20%7D%3E(type%3A%20ComponentClass%3C%7B%20children%3A%20(foo%3A%20number)%20%3D%3E%20string%3B%20%7D%3E%2C%20...children%3A%20((foo%3A%20number)%20%3D%3E%20string)%5B%5D)%3A%20any%20%0A%0A%2F%2F%20If%20you%20don't%20expect%20P%20to%20be%20infered%2C%20it%20works%20properly%3A%0AcreateElement%3C%7B%20children%3A%20(foo%3A%20number)%20%3D%3E%20string%20%7D%3E(InferFunctionTypes%2C%20foo%20%3D%3E%20%22%22%20%2B%20foo)%3B%0A%0A%2F%2F%20Is%20unrelated%20to%20varargs%0AcreateElement2(InferFunctionTypes%2C%20%5B(foo)%20%3D%3E%20%22%22%20%2B%20foo%5D)%3B%0A

Related Issues:

Metadata

Metadata

Assignees

Labels

FixedA PR has been merged for this issueNeeds InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions