Skip to content

missing properties after applying spread operator on instance of abstract type  #32022

Closed

Description

@paulluap

TypeScript Version: 3.5.2

Search Terms:
spread operator type error, Object.assign,
Code

abstract class Base { 
    abstract get a(): string;
    abstract get b(): string;
}
class A extends Base{ 
    a: string
    b: string
}
const a1 : Base = new A() 
const a2 : Base = {...a1}  //reports error: Type '{}' is missing the following properties from type 'Base': a, bts(2739)

const a3 : Base = a1
const a4 : Base = Object.assign({}, a1)

Expected behavior:
code compiles

Actual behavior:
reports error on a2
Type '{}' is missing the following properties from type 'Base': a, bts(2739)

Playground Link:
https://www.typescriptlang.org/play/#src=abstract%20class%20Base%20%7B%20%0D%0A%20%20%20%20abstract%20get%20a()%3A%20string%3B%0D%0A%20%20%20%20abstract%20get%20b()%3A%20string%3B%0D%0A%7D%0D%0Aclass%20A%20extends%20Base%7B%20%0D%0A%20%20%20%20a%3A%20string%0D%0A%20%20%20%20b%3A%20string%0D%0A%7D%0D%0Aconst%20a1%20%3A%20Base%20%3D%20new%20A()%20%0D%0Aconst%20a2%20%3A%20Base%20%3D%20%7B...a1%7D%0D%0Aconst%20a3%20%3A%20Base%20%3D%20a1%0D%0Aconst%20a4%20%3A%20Base%20%3D%20Object.assign(%7B%7D%2C%20a1)

Related Issues:

Activity

  1. jcalz commented on Jun 21, 2019

    @jcalz
    Contributor

    In classes, getters go on the prototype like methods, and so object spread won't copy them:

    class X {
      get x() {
        return "x";
      }
    }
    let y = { ...new X() }; // {}
    console.log(y); // {}

    Since a1 is only known to be Base, I think the a2 behavior is probably working as intended.

    That the a4 behavior differs is likely a design limitation; the return type of Object.assign() is an intersection of its input types, which is incorrect for some edge cases.

    This is related to #10727 and #28234 about how we don't have a real spread type and tend to use intersections in generic cases and Object.assign(), but that spread expressions with known concrete class instance only preserve non-method properties.

  2. fatcerberus commented on Jun 22, 2019

    @fatcerberus

    Yeah, I think this is the same deal as how in C++ if you only have a Base reference and call a method on it, you get the base class implementation unless the method is virtual (which requires a vtable, i.e. runtime dispatch). TS types have no runtime representation so if the compiler sees Base then it has no way to know it’s really dealing with a Derived.

  3. paulluap commented on Jun 24, 2019

    @paulluap
    Author

    I see. Thanks for the replies.

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

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions