Repository navigation
Proposal: strict flag to prevent uninferrable generic types from being inferred as {} #27288
Description
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)
Activity
ajafff commentedon Sep 23, 2018
In the meantime you can use a lint rule to detect such cases: https://github.com/fimbullinter/wotan/blob/master/packages/mimir/docs/no-inferred-empty-object.md
ajafff commentedon Sep 23, 2018
IIRC there was a PR for such a flag, but it was not accepted for whatever reason. I cannot find it anymore
ajafff commentedon Sep 23, 2018
I found it by accident: Duplicate of #5254, relevant comment: #5254 (comment)
bcherny commentedon Sep 23, 2018
Klaus Meinhardt (@ajafff) Great find! My issue is a proposed solution for the same problem raised in #5254. And it looks like Daniel Rosenwasser (@DanielRosenwasser) already started working on it a while back e21f7cf.
If TypeScript authors feel like it's a good idea, I'd love to continue Daniel's work with a fully fleshed out PR.
evmar commentedon Oct 8, 2018
I think we (Google) would be interested in using this. bcherny's description about it makes errors with strictFunctionTypes non-local is exactly our experience.
RyanCavanaugh commentedon Oct 9, 2018
Daniel will see what the impact of such a flag would be
bcherny commentedon Feb 8, 2019
It's exciting to see this committed for 3.4! One more use case, for record-keeping:
OliverJAsh commentedon Feb 12, 2019
As an aside, why is
{}used as a fallback instead ofunknown?jack-williams commentedon Feb 12, 2019
Oliver Joseph Ash (@OliverJAsh) relevant comment here.
OliverJAsh commentedon Feb 13, 2019
Another use case: when using a
pipeorcomposefunction, generics are sometimes lost.I run into this very often when trying to compose my React HOCs:
IIUC, this particular use case concerns #12838 and #10247.
However, with the option proposed here, TypeScript would at least alert the developer to change the code to workaround this problem, to something like:
vkrol commentedon May 31, 2019
Can it be closed now?
https://github.com/microsoft/TypeScript/wiki/Breaking-Changes#generic-type-parameters-are-implicitly-constrained-to-unknown
#30637
RyanCavanaugh commentedon May 31, 2019
We didn't quite implement this flag, but the new behavior of
unknownfixes the vast majority of problems associated with{}. Combined withstrictFunctionTypesand not writing generic functions which can be called with zero inference sites in the first place, I think this effectively fixed.