Skip to content

Proposal: strict flag to prevent uninferrable generic types from being inferred as {} #27288

Closed
@bcherny

Description

@bcherny

I'm sure there an issue or discussion about this I missed, but I couldn't find it via search.

Search Terms

Generic, infer, default, {}, { }, empty, shape, object

Suggestion

Behind a new strict mode flag --strictGenericBindings, when binding concrete types to generic type parameters, if a type can't be inferred TSC should throw an error at compile time instead of defaulting to {}.

Use Cases

Let's say you have the following Promise:

const p = new Promise(resolve => resolve(42)) // Promise<{}>
p.then(r =>
  r + 4 // Error: Operator '+' cannot be applied to types '{}' and '4'.
)

This gives an error when you try to use the Promise's result. This experience isn't ideal, because the error is non-local. Looking at the code that threw that error, it's not obvious why r is {}.

If I try to fix this by adding an explicit annotate when I consume r, it works:

p.then((r: number) => {
  r + 4 // OK
})

But if I have strictFunctionTypes enabled, that breaks because {} isn't assignable to number:

p.then((r: number) => {
  r + 4 // Error: Argument of type '(r: number) => void' is not assignable to parameter of type '(value: {}) => void | PromiseLike<void>'.
})

The real fix is to explicitly bind a type parameter when I consume Promise:

const p = new Promise<number>(resolve => resolve(42)) 
p.then(r => {
  r + 4
})

But unfortunately, the error messages didn't help me get there.

A possible solution to this problem is to throw a helpful error message when a generic type parameter can't be inferred. This can help prevent bugs, and help programmers catch bugs earlier:

const p = new Promise(resolve => resolve(42)) // Error: Unable to infer a generic type for Promise<T> - please bind one explicitly. Eg. new Promise<myType>(...).
p.then(r => {
  r + 4
})

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript / JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. new expression-level syntax)

Related Issues

Metadata

Metadata

Labels

Add a FlagAny problem can be solved by flags, except for the problem of having too many flagsCommittedThe team has roadmapped this issueExperimentA fork with an experimental idea which might not make it into masterSuggestionAn idea for TypeScript

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions