-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Type inference in function body of optionalArg || {} is {} #39434
Comments
This is right behaviour and is an artifact of subtype reduction for conditionals. The empty object type is a super type of In contrast, I can see why this is confusing. The intent is that The fact that it narrows to One workaround might be declaring Another workaround is to abuse type DataObject = { datafield: string }
function test(arg?: DataObject): void {
const empty: { missing?: undefined } = {};
const data = arg || empty;
typeof data // {}
if ("datafield" in data) {
typeof data // DataObject
}
} |
@jack-williams Thank you for the explanation, and for pointing to the related issues.
Thanks for pointing that out; I had some intuition about {} as an object literal (and strict checks) and perhaps I was over-extending that intuition to its inferred type.
I had found a workaround declaring: const data: DataObject | {} = arg || {} If I understand correctly then, I'm still a little unsure about how this one works:
For instance if I extend the example slightly: type DataObject = { datafield: string; extrafield: number } // add common field here
function test(arg?: DataObject): void {
const data = arg || { extrafield: 1 };
typeof data // DataObject | { extrafield: number }
if ("datafield" in data) {
typeof data // DataObject
}
} The type |
Yep, and while
Object literals (with fields) create an object literal type with a special 'fresh' flag. Freshness tells TypeScript that the object literal is closed, so the fresh object type If I were to add a layer of indirection the freshness is lost, and the types are reduced as expected. type DataObject = { datafield: string; extrafield: number } // add common field here
function test(arg?: DataObject): void {
const data = arg || ({ extrafield: 4 } as { extrafield: number });
typeof data // { extrafield: number }
if ("datafield" in data) {
typeof data // never
}
} |
@jack-williams Thanks again for the explanations, that clears things up nicely. Especially this point clarified a lot (even if it is an implementation detail):
Closing this one now 👍 |
TypeScript Version: 3.7.5, 3.8.2, 3.8.3, 3.9.2, 4.0.0-beta (ts playground versions)
Search Terms: optional arguments, logical or, empty object
Code
Expected behavior:
Actual behavior:
Only observed for empty object - making the following change shows expected behavior:
Playground Link:
https://www.typescriptlang.org/play?ts=3.9.2#code/C4TwDgpgBAIghsOB5ARgKwgY2FAvFAbygBME4AzASwgBtiAuKAZ2ACdKA7AcygF8AocgFcO2SgHsOUYBBYAKOKy4B+RvESoM2AJSMAbuMrFC-KGaiZJLEmTxRFPAD6PCfANynzoSOPI3EUAD0gYQC5lCUfnIARKSIVLTE0RFScXDaJuHh3hC+-nBBIRwQehCsnmYCAoIiYpLSssAATApKqrBkmljAulAGRpnmlhzWaXYOUM6u4sAAFmUJdIwAjO4V0uC5fmPBHRro3ZPTcwvUS1DLYeaRUDFpi0kp+RkE69mbeTsh6sgH2OtVIA
Related Issues:
The text was updated successfully, but these errors were encountered: