Description
Suggestion
π Search Terms
contextually context-sensitive generic inference unused parameter method object literal
β Viability 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. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
β Suggestion
When generic inference is gathering candidates, context-sensitive expressions should still be considered if it's possible to do so without observing a type parameter from the call.
π Motivating Example
declare function callIt<T>(obj: {
produce: (n: number) => T,
consume: (x: T) => void
}): void;
// Works
callIt({
produce: () => 0,
consume: n => n.toFixed()
});
// Fails for no obvious reason
callIt({
produce: _a => 0,
consume: n => n.toFixed()
});
// Fails for no obvious reason
callIt({
produce() {
return 0;
},
consume: n => n.toFixed()
});
π» Use Cases
Background: expressions can be "context-sensitive" for the purposes of inference -- this is a syntactic property of an expression that tells us whether or not an expression's type could be dependent on the inference of its contextual type. For _a => 0
, this is true because the return expression might depend on _a
(including in indirect ways). For produce() {
, it's context-sensitive because (unlike an arrow function) it takes its parent object type for this
, so could depend on the type parameter via a reference of this
.
When it turns out that the type of the expression is not actually dependent on the inference of the type parameter (which would represent a true circularity, thus more understandably non-working), then this just looks busted for no obvious reason. This is a continuous source of surprise and annoyance. (TODO: link user reports)
Discussion
There are a few implementation strategies we could try, with varying trade-offs
- Proceed to do structural inference on context-sensitive expressions but mark the type parameters symbols as "off limits" with logic to back out. This is likely impractically invasive
- Perform a more robust check to determine "true" context-sensitivity -- for example,
produce: _a => 0
should not be contextually sensitive because the relational target(n: number) => T
does not useT
in a covariant position. There are multiple ways we could do this - Add an optional intermediate pass when inference collected no candidates when ignoring context-sensitive expressions where we go and collect from them anyway. This pass could safely fix parameters since they'd be going to
unknown
. This entire bullet point might be wrong. - As a stopgap, consider function expressions to not be context-sensitive if all of their return expressions can be trivially seen to not depend on earlier lines of code (e.g. are literals, literals of literals,
null
, orundefined
). This might fix a surprisingly large class of reports - As a stopgap, consider an object literal method to not be context-sensitive if it doesn't reference
this
. I believe we already have code for this and it would fix another large class of surprises.