-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Partial inconsistent behavior with undefined types #18823
Comments
The fundemental challenge is that TypeScript's type system sees a property with an interface a {
x?: number;
}
interface b {
x: number | undefined;
}
type c = Partial<{ x: number }>; The subtle runtime behaviour is that: const a = { x: undefined };
const b = { };
console.log(a.x); // undefined
console.log(b.x); // undefined
console.log(typeof a.x === 'undefined'); // true
console.log(typeof b.x === 'undefined'); // true
console.log('x' in a); // true
console.log('x' in b); // false
console.log(a.hasOwnProperty('x')); // true
console.log(b.hasOwnProperty('x')); // false
console.log(Object.create(a).hasOwnProperty('x')); // false I believe the opinion has been that to actually implement the presence of a property in an object as part of the object's structure is really complicated to implement for what is limited value from identifying issues in the code. What sort of issues was this causing in the run-time code/behaviour? |
We switched to strictNullChecks recently, and as such had areas all over the codebase where devs had previously just willy-nilly been using undefined that we locked down to be reliably set. However, we switched to using Partial for setState ages ago, so we have lots of cases of people setting undefined on fields that can't be undefined, which the compiler is not catching. I'm not sure I consider the runtime behavior "subtle" -- when you do something like _.merge (basically what setState does), where you iterate over keys for an object and merge any value associated with it into the master object, there's a huge difference between not having the key present in the type and having it present and set to undefined. The non-partial behavior agrees with this and is a super-helpful compiler validation, we just really need this with the Partial level as well to catch the next series of bugs that we're finding by hand right now. |
And, to be fair, if you type-coerce two different things that overlap, that works too: let z6 = { x: 4, y: undefined, z: 4 } as a; // OK -- x/y are on a, and it doesn't care that z is not I'm only particularly worried about the specific pattern of defining a partial and assigning to it, rather than type-coercing something INTO a Partial. With the existing pattern of type coersion, we accept the ability to "downcast" { x: 4, y: 4, z: 4 } down to { x: number, y: number }. let z7 = { x: 4, y: 4, z: 4 } as { x: number, y: number}; // Works However, I shouldn't be able to do: let z7: { x: number, y: number } = { x: 4, y: 4, z: 4 }; // Properly errors .. which I correctly can't. |
Relevant discussion in #13195 The type system treats a missing (or optional) property as We have talked about it in the past and the main problem is higher order relationships has assumptions that can not be satisfied in this scheme, namely that closing in favor of #13195 |
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed. |
We're trying to figure out the right way to deal with potential react issues with typings. We've recently migrated our enormous codebase over to using TS 2.6 and enabled strictNullChecks (after working through over 7 thousand compile errors!) and found that we still have some bugs due to typescript allowing nulls to make their way into state through what feels to me like a bug. Someone complained about a similar problem with #12793 but it sounds like the behavior they were talking about could be argued about. However, I have what feels more like a contradiction in the compiler.
In this example (z5), x is on the interface, but explicitly doesn't allow undefined. Why is it okay to have this "missing parameter" that's outside the interface, but I can't add z:4 to the same Partial? If you switched to allowing z: 4 on the Partial interface, that'd make it a lot less useful, but that would at least be consistent; but the very correct behavior here (and what the common usage of Partial tends to be) feels like banning x: undefined.
The text was updated successfully, but these errors were encountered: