Repository navigation
Error when destructuring with literal initializers as fallback #26235
Description
Activity
- changed the title
[-]Error when destructuring literals as fallback[/-][+]Error when destructuring with literal initializers as fallback[/+]on Aug 6, 2018 RyanCavanaugh commented
on Aug 8, 2018 MemberMore actionsI would argue TS should even accept let { color } = {};
Should we allow
let { colour } = { color: "red" }(note the spelling mismatch)? If not, what's the distinguishing principle?Reacted by Max Nanasy and ChanaFair point, I concede that I was making "a principled point", while I recognize the strength of TypeScript is often in making pragmatic choices.
Reacted by Max NanasyOops, wrong button.
Reacted by Chris Hut and Prokop TunelSo can I expect Typescript 3.2.1 will not report the below snippet
let { subject, topic, subTopic } = value || {};
as below error
[ts] Initializer provides no value for this binding element and the binding element has no default value. [2525]
reference: playground
Since many es syntax are reported as error by typescript, we very restricted by the way we code 😞
7 remaining items
The issue seems to present itself when destructuring from a nested object as well
interface Props { innerObject?: { name?: string; email?: string; }; } // won't let name and email default to undefined // Initializer provides no value for this binding element and the binding element has no default value. export const nestedDestructure1 = (props: Props) => { const { innerObject: { name, email } = {} } = props; console.log(name, email); }; // no errors, but is a little annoying export const nestedDestructure2 = (props: Props) => { const { innerObject: { name = undefined, email = undefined } = {} } = props; console.log(name, email); }; // no errors, but is a little annoying export const nestedDestructure3 = (props: Props) => { const { innerObject = {} } = props; const { name, email } = innerObject; console.log(name, email); };
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:
export const nestedDestructure1 = ({innerObject: {name, email} = {}}: Props) => { console.log(name, email); };
Reacted by Ganapathy S, anca, Drew Showalter, Fardad Mohammadi, corsali-albert and AelitaAnother simple obvious example how it's wrong:
type State = { open?: boolean; } class MyComponent extends React.Component<{}, State> { render() { const { open } = this.state || {}; ~~~~ Initializer provides no value for this binding element and the binding element has no default value. TS2525 return <span>{open}</span>; } }
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.
Ryan Cavanaugh (@RyanCavanaugh) brought up this example:
let { colour } = { color: "red" }; ~~~~~~ Inconsistent British spelling of colour
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.Reacted by Jeremy Bernier and Joep- addedFixedA PR has been merged for this issueA PR has been merged for this issue
on Jun 2, 2019 - added a commit that references this issue
on Nov 13, 2019 - added a commit that references this issue
on Nov 21, 2019 - added a commit that references this issue
on Nov 26, 2019 I have this problem with typescript 4.
ignacio-nacho-barbano commented
on Feb 4, 2021 More actionsI think this is the simplest solution, did the same on my project.
Reacted by David Nunez and corsali-albertRan into this when upgrading from TS 4.4 to 4.6.
const errorCode = 99 const { title, subtitle } = (() => { // TS2525 on the deconstructed `subtitle` field switch (errorCode) { case 99: return { title: 'Account Not Found', subtitle: 'foo, } default: return { title: 'An unknown error occurred', } } })()I was able to resolve the
TS2525: Initializer provides no value for this binding element and the binding element has no default value.error by explicitly returningsubtitle: undefined:default: return { title: 'An unknown error occurred', subtitle: undefined, }Seems like a bug to me.
The issue seems to present itself when destructuring from a nested object as well
interface Props { innerObject?: { name?: string; email?: string; }; } // won't let name and email default to undefined // Initializer provides no value for this binding element and the binding element has no default value. export const nestedDestructure1 = (props: Props) => { const { innerObject: { name, email } = {} } = props; console.log(name, email); }; // no errors, but is a little annoying export const nestedDestructure2 = (props: Props) => { const { innerObject: { name = undefined, email = undefined } = {} } = props; console.log(name, email); }; // no errors, but is a little annoying export const nestedDestructure3 = (props: Props) => { const { innerObject = {} } = props; const { name, email } = innerObject; console.log(name, email); };
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:
export const nestedDestructure1 = ({innerObject: {name, email} = {}}: Props) => { console.log(name, email); };
Still an issue in
v5.0.4.
UpvotingRyanCavanaugh commented
on May 12, 2023 MemberMore actionsPlease log a new bug if you think you have found an issue; maintainers are not necessarily reading comments on 5-year-old closed issues
- locked as resolved and limited conversation to collaborators
on May 12, 2023
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
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 acceptlet { 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