Skip to content

Object spread not mapping optional types correctly with editing values after spreadΒ #54687

Closed
@MrSimmmons

Description

@MrSimmmons

Bug Report

πŸ”Ž Search Terms

Spread, Object, Type, Optional, Interface

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about Interfaces & generics (though this doesnt really fit into any of the FAQs)

⏯ Playground Link

Playground link

πŸ’» Code

interface SourceData {
  id: string;
  // some other data
  start_date: string;
  end_date?: string;
}

interface UseableData {
  id: string;
  // some other data
  start_date: Date;
  end_date?: Date;
}

function transform(props: SourceData): UseableData {
  return { // Error `end_date` is of type `string | Date | undefined`
    ...props,
    start_date: new Date(props.start_date),
    ...(props.end_date && { end_date: new Date(props.end_date) })
  };
}

πŸ™ Actual behavior

I am getting an error on the return statement saying that it is not assignable to type 'UseableData' where end_date is of type string | Date | undefined.
Why I believe this is a bug is because I am adding the Date version of end_date if the optional end_date string exists in the first place. So if end_date doesnt exist, then it would return the undefined type from ...props, and if the end_date string did exist, it would get parsed through the Date class and be added to the return object. So the only possible return type for end_date is Date | undefined which matches that in UseableData

πŸ™‚ Expected behavior

No type error should occur because we are optionally adding the end_date key to the return object only if it has a value

πŸ₯Έ Partial workaround

I'm aware that I can use the ternary operator like so:

{
  end_date: props.end_date ? new Date(props.end_date) : undefined,
}

Which is fine if you are just grabbing values from the returned object. However if you were to call Object.keys() on the returned object with end_date being specifically defined as undefined by the ternary operator, then end_date would appear in its result.
Where as going:

{
  ...(props.end_date && { end_date: new Date(props.end_date) })
}

Wont include end_date in the returned object.

Hence why this is only a partial workaround

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions