You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Don't widen return types of function expressions #241
(context elided: widening of function expression return types)
The problem with this widening is observable in type argument inference and no implicit any. For type argument inference, we are prone to infer an any, where we should not:
functionf<T>(x: T,y: T): T{}f(1,null);// returns numberf(()=>1,()=>null);// returns () => any, but should return () => number
So after we get to parity, I propose we do the following. We do not widen function expressions. A function is simply given a function type. However, a function declaration (and a named function expression) introduces a name whose type is the widened form of the type of the function. Very simple to explain and simple to implement.
I’ve been told that this would be a breaking change, because types that used to be any are now more specific types. But here are some reasons why it would be okay:
In the places where you actually need the type to be any (because there are no other inference candidates), you would still get any as a result
In places where there was a better (more specific) type to infer, you’d get the better type.
With the noImplicitAny flag, you’d get fewer errors because there are actually fewer implicit anys
Questions:
Is a principle of design changes going forward to not switch from 'any' to a more precise type because it can be a breaking change?
Going with 'not a breaking change' here because this is unlikely to break working code, but we need to verify this.
Would this manufacture two types?
In essence, we already have two types: The original and the widened type. So by that measure this is not really a change
I've arrived here via #7220. Would appreciate some guidance on whether the following is expected behaviour or not (playground):
interfaceG<K,R>{(v: K): R;}functionF<T>(v: T): T{returnv;}interfaceA{__Type: "A";}interfaceB{__Type: "B";}leta: A;letb: B;a=b;// OK: error as expected, B is not assignable to Ab=a;// OK: error as expected, A is not assignable to Bletx: G<A,B>=F;// How is this possible?
It's the final assignment I cannot understand... because the compiler does not complain.
Hey everyone, I opened #40311 a couple of days ago which I think should fix most of the issues that have brought people here. If you'd like to give it a try, we have a build available over at #40311 (comment).
Reacted by SlurpTheo, Joël Galeran, Gregory Jacobs, Lauchlan Irwin, karlismelderis, Denis Pshenov, Boris Petrov, Joel Aufgang and Minho Lee
Reacted by David Luzar, Jason Kuhrt, James Bromwell, Keegan Lillo, Gregory Jacobs, Giedrius, Denis Pshenov, Boris Petrov, Joel Aufgang, Lauchlan Irwin and 7 more
Hey Daniel Rosenwasser (@DanielRosenwasser), what's the status of your pull request? I suppose I don't have to list any reasons for wanting this feature (there are many discussions), so I'm only curious if there's been any progress recently
interfaceIUser{id: number;}// No errorsconstusers: Array<IUser>=[].map<IUser>(()=>({id: 1,unknownProperty: 'unknownProperty',}));// Property 'id' is missing in type '{ unknownProperty: string; }' but required in type 'IUser'.ts(2741)constusers1: Array<IUser>=[].map<IUser>(()=>({unknownProperty: 'unknownProperty',}));console.log(users);console.log(users1);
Reacted by Brian Kilrain, Michał Pietraszko, Bruledamien, RuneSP, Marcin Ciarka, Ben Stovold, Jacob Bandes-Storch, Cody Gordon, Andreas Bergmaier and Richard Torres
AFAIK there is no real workaround (except explicitly putting the return type on the function which defeats the purpose of putting the type in the first place). Is this issue still on the radar of the TypeScript team?
Reacted by Maksim Popov, Piotr Witek, Marcus Blättermann, Michael Cheng, Ella Lai, Sasha Koss, Bart, Connor Manning, Marko Andlar, Tiago Bento and 5 more
typeMyResponse={my: string;response: string;};constmyFunc1=(param: string): MyResponse=>{return{my: 'my',response: 'response',extra: 'extra',// error here, as 'extra' is not part of 'MyResponse'};};
❌ However, no error is raised in the following cases:
When assigning the object to a variable first:
constmyFunc2=(param: string): MyResponse=>{constmyResponse={my: 'my',response: 'response',extra: 'extra',// no error here, even though 'extra' is not part of 'MyResponse'};returnmyResponse;};
When using object spread:
constmyFunc3=(param: string): MyResponse=>{constmyResponse={my: 'my',response: 'response',extra: 'extra',// no error here either};return{ ...myResponse};};
In both scenarios, TypeScript is allowing extra properties without raising an error even though it knows the final shape is inconsistent with the type annotations.
Extra properties are allowed in typescript. Excess property checks only apply to the type in effect at the point of declaration (if any). This is generally considered a linting feature to catch typos, not a type correctness feature.
Outside that specific scenario, extra properties are not an error. Consider if you wanted to assign a value {a: A, B: B} to a variable typed {a: A}. The value satisfies the type.
When the constant declaration is a step removed from where the value is used, EPC does not come into play.
Activity
Correct contextual typing with union types
DanielRosenwasser commentedon Mar 19, 2015
Bumping this since we ran into a situation with a leaked
anyin our own compiler. Minimal example:mhegazy commentedon Jul 27, 2015
We need an implementation proposal for this one.
JsonFreeman commentedon Mar 22, 2016
What if we just did this:
myitcv commentedon Apr 8, 2016
I've arrived here via #7220. Would appreciate some guidance on whether the following is expected behaviour or not (playground):
It's the final assignment I cannot understand... because the compiler does not complain.
RyanCavanaugh commentedon Apr 8, 2016
During assignment checking, type parameters are replaced with
any. So the effective signature ofFhere is(a: any) => anyJsonFreeman commentedon Apr 8, 2016
Paul Jolly (@myitcv) See #138
43 remaining items
DanielRosenwasser commentedon Sep 2, 2020
Hey everyone, I opened #40311 a couple of days ago which I think should fix most of the issues that have brought people here. If you'd like to give it a try, we have a build available over at #40311 (comment).
mrlubos commentedon May 17, 2021
Hey Daniel Rosenwasser (@DanielRosenwasser), what's the status of your pull request? I suppose I don't have to list any reasons for wanting this feature (there are many discussions), so I'm only curious if there's been any progress recently
jdanielson-indeed commentedon Sep 14, 2021
+1 to consider this or adding it as a compiler flag.
EddyVinck commentedon May 16, 2022
I ran into problems with some
.map()calls, which seems related to the issue described.Is this still being worked on?
folex commentedon May 19, 2022
Hi!
Is return type widening a possible cause for the lack of errors in this case?
Or is it because of the covariance rule on function subtyping?
MaksimKlepikov commentedon May 30, 2022
Happens for
.map()Add lint rule to prefer `map((): T => {})` over `map<T>(() => {})` (#…
Akryum commentedon Oct 26, 2023
This issue is also present when writing GraphQL resolvers:
As we can see in this example TS is not doing its job of catching the type issue introduced by the typo on the optional field name.
Playground | Other example
AFAIK there is no real workaround (except explicitly putting the return type on the function which defeats the purpose of putting the type in the first place). Is this issue still on the radar of the TypeScript team?
yamcodes commentedon Aug 23, 2024
This issue seems to be the cause of this error:
What are the action items here?
ernestostifano commentedon Sep 21, 2024
Having same issue as Yam Borodetsky (@yamcodes)
alvis commentedon Oct 6, 2024
Ryan Cavanaugh (@RyanCavanaugh) Daniel Rosenwasser (@DanielRosenwasser) I also noticed that when using object spread in the return statement of a function, no error is reported for additional properties that don’t exist in the target type.
For example:
✅ Type error is correctly raised here:
❌ However, no error is raised in the following cases:
In both scenarios, TypeScript is allowing extra properties without raising an error even though it knows the final shape is inconsistent with the type annotations.
snarbies commentedon Oct 7, 2024
Extra properties are allowed in typescript. Excess property checks only apply to the type in effect at the point of declaration (if any). This is generally considered a linting feature to catch typos, not a type correctness feature.
Outside that specific scenario, extra properties are not an error. Consider if you wanted to assign a value
{a: A, B: B}to a variable typed{a: A}. The value satisfies the type.When the constant declaration is a step removed from where the value is used, EPC does not come into play.