TypeScript: Error when destructuring with literal initializers as fallback
TypeScript Version: 3.0.1
Destructuring with a fallback empty object literal is a common practice, especially in function arguments (which works), but not always:
Code
type Options = { color?: string; width?: number; };
function A({ color, width }: Options = {}) {
//
}
function B(options: Options) {
options = options || {};
let { color, width } = options;
}
function C(options: Options) {
let { color, width } = options || {};
}
Expected behavior: All three functions type-check and behave the same.
Actual behavior: “Initializer provides no value for this binding element” Error.
Which is simply incorrect, ES spec clearly defines the value when an object key is missing as undefined. Furthermore, I would argue TS should even accept let { color } = {}; though it’s obviously not as common/important.
Related Issues: Issue #4598 - Improved checking of destructuring with literal initializers fixed the same problem, but only for destructuring function arguments (example A).
Playground Link: here
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 18
- Comments: 17 (3 by maintainers)
Commits related to this issue
- chore(TS): add correct default value Although this works identical, TS will complain otherwise, see https://github.com/microsoft/TypeScript/issues/26235 — committed to algolia/instantsearch by Haroenv 5 years ago
- refactor(utils): migrate remaining utils to TypeScript (#4196) * chore(TS): convert geo-search util * chore(TS): migrate prepareTemplateProps * chore(TS): migrate src/lib/show-more * chore: ... — committed to algolia/instantsearch by Haroenv 5 years ago
- refactor(utils): migrate remaining utils to TypeScript (#4196) * chore(TS): convert geo-search util * chore(TS): migrate prepareTemplateProps * chore(TS): migrate src/lib/show-more * chore: ... — committed to algolia/instantsearch by Haroenv 5 years ago
works, so I would expect the shortened
let { color, width } = options || {};to work too.A type-safe workaround you can use is
(value || {}) as Partial<NonNullable<typeof value>>.The issue seems to present itself when destructuring from a nested object as well
I’d really expect
nameandemailto be typed asstring | undefinedin thenestedDestructure1example, but that isn’t the case.This issue has been fixed for function params with #4598, so this would work in that situation:
playground codesandbox
https://github.com/microsoft/TypeScript/issues/26235#issuecomment-452955161
I think this is the simplest solution, did the same on my project.
Another simple obvious example how it’s wrong:
Destructuring from
**** || {}is a very common pattern, and if TS can’t simply fail on it.Even if there’s deep theoretical correctness reasoning how and why it’s consistent with {whatever}, this pattern is common enough to have a hard-coded special casing.
@RyanCavanaugh brought up this example:
Which basically says TypeScript helpfully highlights undeclared members for fear of typos, which is totally reasonable!
However, when destructuring from
{}, there is no risk of typos. The actual error in this case protects against misspelling of a field that DOES NOT EXIST.Oops, wrong button.
Should we allow
let { colour } = { color: "red" }(note the spelling mismatch)? If not, what’s the distinguishing principle?Fair point, I concede that I was making “a principled point”, while I recognize the strength of TypeScript is often in making pragmatic choices.