Skip to content

Spread operator fails for object type generics #26412

Closed

Description

@opiation

TypeScript Version: 3.0

Search Terms:
generic, spread, object types, spread types, spread operator

Code

function shallowCopy<T extends object>(state: T): T {
  return {
    ...state
  }
}

Expected behavior:
Should compile as only state's properties are being shallow-copied to the returned result and thus meets the T type.

Actual behavior:
Does not compile with the following error message:

> Spread types may only be created from object types.

If T extends object, is it not then an object type? If not, what type or interface can be defined that is an object type and is not exactly {} or object?

Possible ways forward:

  • If the compiler does not properly recognize T extends object as an object type, perhaps this should be implemented to better support spread/rest operators in modern ecmascript syntax.
  • If T extends object is a recognized object type, the compiler error should be amended to something like "Spread types may only be created from the object type in generics"
  • Silently lock and close this issue adding documentation that states "The spread operator does not support generics extending object"

Playground Link: Contrived example

Related Issues:
#13288 - While this seems like the use case was addressed by Ryan Cavanaugh (@RyanCavanaugh) , the resulting workaround suggests far more explicit typing than should be required by the compiler to infer the types in the example provided. While closing that PR might have been the protocol, it seemed more effectively to silence the community request for spread operator support with generics extending from object.

Activity

  1. ghost added on Aug 13, 2018
  2. ghost added
    BugA bug in TypeScript
    on Aug 13, 2018
  3. simonbuchan commented on Aug 16, 2018

    @simonbuchan

    Just digging through those links, #10727 (comment) suggests that there wasn't a specific reason for this to not work originally, but by #20013 it was decided that this was correct to reject this.

    On the one hand, the reasoning there, essentially that own/enumerable requirement for spread isn't modeled by TS, is also an issue even without generics so allowing this usage isn't introducing a new type-safety problem; on the other, the problem is much larger with generics, as the heuristic TS uses for non-generic spreads (which is just strip methods, right?) doesn't work. Is this why #10727 was recently tagged as Revisit?

  4. alamothe commented on Aug 17, 2018

    @alamothe

    Came here because of:

    type A = {
    	a: string
    	b: number
    }
    
    function f<B extends object>(x: A & B) {
    	const {a, b, ...other} = x // does not work
    }
  5. RyanCavanaugh commented on Oct 9, 2018

    @RyanCavanaugh
    Member

    Silently lock and close this issue

    :extremely long sigh:

  6. Cryrivers commented on Oct 16, 2018

    @Cryrivers

    Reading #20013, I understand that ...T is not assignable T. But may I know the challenge for getting a correctish type inference?

    For example:

    type MyObj = {
      a: string,
      b: number,
      c: boolean,
    }
    
    function foo<T extends MyObj>(value: T) {
      const { a, ...otherValues } = value;
    }

    is it possible to infer otherValues as Omit<MyObj, 'a'> & AnyObject, so we can at least make it type-safe if we access properties in otherValues that belongs to MyObj?

    Regarding #20013, is it possible to have a helper type RemovePrototype<T> or something?

  7. xiaodeaux commented on Dec 7, 2018

    @xiaodeaux

    This bug is probably related to #8856 somehow.

  8. JoshuaKGoldberg commented on Mar 9, 2021

    @JoshuaKGoldberg
    Contributor

    This no longer gives an error in the latest version: playground link with collected examples from linked issues. Time to close the issue as resolved?

  9. mnpenner commented on Mar 9, 2021

    @mnpenner

    Josh Ghoulberg 👻 (@JoshuaKGoldberg) Isn't that the problem? It should give a compile-time error ideally.

    shallowClone(new C()) doesn't have the method m().

  10. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions