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?
@jsonfreeman @ahejlsberg Function return type widening strikes again. Any ideas on a way to catch this?
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
interface A {
a: string;
b: number
}
let f: () => A = () => { // Error here
// do some stuff
1 + 1 == 42;
// return a value
return {
a: "test"
};
};
then I get the error Type '() => { a: string; }' is not assignable to type '() => A'. on the line let f: () => A, whereas I would have expected an error at the return statement. Instead, I would have to write
let f: () => A = (): A => {
// do some stuff
1 + 1 == 42;
// return a value
return { // Error here
a: "test"
};
};
to 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
let a: Test = myMutator((): Test => ({
notAProperty : "woot?"
}));
then you would also get the extra-properties error.
Thanks!
what is the reasoning behind not contextually typing the return type of function expressions as the exact type from the context
This _does_ happen.
interface StringCallback {
(s: string): void;
}
var x: () => StringCallback = () => {
return s => s.length; // s: string
}
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.
Oh boy. This does look like a familiar problem. I agree that it's worth addressing #241 broadly.
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).
This just bit me too.
interface HasX { x: number; }
const toX: () => HasX = () => ({ x: 0, y: 0 });
(Would also have been solved by #12936)
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 the error is that there is no compile error when there should be.
@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.
The fix for that might have been #16047.
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)
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.
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
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 type U elements with extra properties. Is there any plan to address this? Typing the return value of map'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!
type V = {
foo: string
}
arr.map<V>( el => {
return {
foo: string
bar: 24 // No Error!
}
}
arr.map( (el): V => {
return {
foo: string
bar: 24 // Error!
}
}
Most helpful comment
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)