Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: Improve generics inference with decorators #2686

Closed
ivogabe opened this issue Apr 9, 2015 · 4 comments
Closed

Suggestion: Improve generics inference with decorators #2686

ivogabe opened this issue Apr 9, 2015 · 4 comments
Labels
Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript

Comments

@ivogabe
Copy link
Contributor

ivogabe commented Apr 9, 2015

There have been a lot of users who find it strange that this would compile:

declare function forEach<U>(array: U[], callback: (item: U) => void);
var strings: string[];
var callback: (item: {}) => void;
forEach(strings, callback); // U is inferred as {}

Suggestion: use the decorator @infer to tell the compiler to infer a generic from that argument.
Example:

declare function forEach<U>(@infer<U> array: U[], callback: (item: U) => void);
var strings: string[];
var callback: (item: {}) => void;
forEach(strings, callback); // U is inferred as string (using only the decorated argument), thus this is an error

You can also add @infer to multiple arguments, like this:

declare function forEachOnTwoArrays<U>(@infer<U> firstArray: U[], @infer<U> secondArray: U[], callback: (item: U) => void);

Adding @infer to all parameters would have the same behavior as having no @infer. Adding @infer is optional, thus not a breaking change.

Rules for @infer:

  • You may only add type parameters of the direct containing function.
  • You can add multiple type parameters like this: @infer<T> @infer<U> (or maybe @infer<T, U>?)
  • Generics that are not referenced from an @infer decorator are inferred using the current behavior
  • Every generic T that is referenced from an @infer<T> decorator is resolved using all parameters that are decorated with @infer<T> of that function.

Note that this syntax is currently not supported, as the compiler expects ( after the generics. An alternative would be to write it as @infer<U>(), but I think @infer<U> is cleaner.

@danquirk
Copy link
Member

danquirk commented Apr 9, 2015

My preference is to try to fix type inference in such a way that this sort of manual direction of the system is not required. We talked about this example a bit and think there's actually a pretty simple solution (need to test the full repercussions though).

Are there any other examples where you'd need this facility aside from this case where a generic function takes some number of callbacks whose parameter type is a supertype of an inference site that isn't a function type?

@danquirk danquirk added the Suggestion An idea for TypeScript label Apr 9, 2015
@ivogabe
Copy link
Contributor Author

ivogabe commented Apr 9, 2015

@danquirk This case can indeed be fixed by a different algorithm, since U is an input to a callback functions. So it could be possible to stop using function inputs for inference. A different use case, which cannot be fixed using that, is about factory methods. Borrowing from #296:

declare function createElement<P>(type: { new(p: P): React.Component<P> }, props: P): ReactElement<P>;
createElement(MyComponent, propsVariable)

Here P should only be inferred from the first argument. This shouldn't compile:

interface Props { foo: boolean }
class MyComponent<Props> extends React.Component<Props> { }
createElement(MyComponent, { });

I think it's hard to fix this example without adding extra type info.

@fdecampredon
Copy link

In fact does not this problem could be easily solved if typescript reintroduced constrain between generics ?
With something like :

declare function createElement<P, U extends P>(type: { new(p: P): React.Component<P> }, props: U): ReactElement<P>;

interface Props { foo: boolean }
class MyComponent<Props> extends React.Component<Props> { }
createElement(MyComponent, { }); // error  {} does not extends Props

@mhegazy mhegazy added the Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. label Dec 9, 2015
@ivogabe
Copy link
Contributor Author

ivogabe commented Dec 9, 2015

I think this can be closed, as #5949 implements the suggestion of @fdecampredon.

@ivogabe ivogabe closed this as completed Dec 9, 2015
@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants