Closed
Description
This might be a feature, but it sure is odd. The excess property check is not working on callbacks, e.g.:
interface Test {
name?: string;
}
function myMutator(fn: () => Test) {
return fn();
}
/* Not an error */
let a: Test = myMutator(() => ({
notAProperty : "woot?"
}));
/* This gives error */
function test(): Test {
return {
notAProperty : "what"
}
}
/* This gives error */
let b: Test = {
notAProperty : "huh"
};
Is there a trick to enforce the excess property check in situations like the one above?
Activity
RyanCavanaugh commentedon Mar 16, 2016
@JsonFreeman @ahejlsberg Function return type widening strikes again. Any ideas on a way to catch this?
T18970237136 commentedon Mar 16, 2016
Hi,
sorry to ask a noobish question, but what is the reasoning behind not contextually typing the return type of function expressions as the exact type from the context (at least if it is not
any
or something like that)?E.g. if I have
then I get the error
Type '() => { a: string; }' is not assignable to type '() => A'.
on the linelet f: () => A
, whereas I would have expected an error at thereturn
statement. Instead, I would have to writeto get the error at the
return
statement (and I would also get errors on declaring extra properties).This seems to also happen at #7538.
E.g. in the example from @Ciantic, if you write
then you would also get the extra-properties error.
Thanks!
RyanCavanaugh commentedon Mar 16, 2016
This does happen.
The problem is that extra property checking doesn't occur as a result of contextual typing, it happens during regular assignability when the object type is "fresh". When we widen the return type of the return expressions to produce the return type of the function, the freshness is lost and there's no checking of extra properties.
DanielRosenwasser commentedon Mar 17, 2016
#241 is the widening issue for the record.
JsonFreeman commentedon Mar 22, 2016
Oh boy. This does look like a familiar problem. I agree that it's worth addressing #241 broadly.
RyanCavanaugh commentedon May 9, 2016
Ideally this would be an error. Unfortunately it turns out to be very difficult to fix this without possibly having consequences in terms of runaway recursion and/or performance (see also discussion notes at #8228).
We're still tracking the root cause (widening the types of function expressions return types) at #241. If more symptoms of this behavior become apparent we'll look at addressing this again, but as it stands it's too complex to fix this relative to the improvement in behavior (which can be worked around with a type annotation).
Ciantic commentedon Aug 26, 2017
My original issue seems to error out these days, I tried opening my old code in playground. @Andy-MS's example does not cause error.
So part of this was fixed?
Ciantic commentedon Aug 30, 2017
@Andy-MS yes, I know, but my original comment contains a code that behaved similarly: It used to not show an error, but now that I tried the compiler gives an error. So partially this is fixed, my original issue seems to be not there anymore.
However you have discovered a different case from my original.
jezzgoodwin commentedon Sep 1, 2017
Hi, I noticed that this issue was closed over a year ago due to complexity in creating a fix. I wondered if this is still the case?
In the codebase I'm working on, we're using generics to specify expected types returned from callbacks. At the moment, excess properties are allowed through (usually spelling mistakes), which causes us hassle from time to time. (Please ask for code examples if required)
njgraf512 commentedon Mar 6, 2018
Hi, any help on this issue would be appreciated. Am I correct that this issue causes the generic in TypeScript's type definitions for
map
, for example, to not enforce types correctly.Looking at this, I would think that it would enforce that the return value is an array of type
U
; however, the compiler is perfectly happy to allow an array of typeU
elements with extra properties. Is there any plan to address this? Typing the return value ofmap
's callback does work to enforce the types as shown below, but I have to think there should be some overload to handle this? Any help appreciated!