Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object assign with inheritance not working in typescript

The code was working when i was using it inside Angular project, then I decided to move to React and the code is working incorrectly.

class A {
  constructor(...parts: Partial<A>[]) {
    Object.assign(this, ...parts);
  }
}

class B extends A {
  id: string;
  constructor(...parts: Partial<B>[]) {
    super(...parts);

  }
}

const a = new B({ id: '123' });
console.log(a);

The output of console log is B {id: undefined} and i expect it to be B {id: '123'}

Here is tsconfig:

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": false,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",
    "strict": false
  },
  "include": [
    "src"
  ]
}

Here is the versions:

+ @types/[email protected]
+ [email protected]
+ @types/[email protected]
+ [email protected]
+ @types/[email protected]
+ [email protected]
+ [email protected]
+ @types/[email protected]
+ [email protected]

Minimum steps to reproduce:

  • create-react-app test --typescript
  • add code to App.tsx
  • run and look into console

UPDATED:

I ended up using the following solution:

class B extends A {
  id: string = this.id;
like image 948
Антон Тишин Avatar asked Mar 04 '19 06:03

Антон Тишин


Video Answer


2 Answers

This is a known problem. It doesn't exist in TypeScript. The cause is that create-react-app TypeScript support uses TypeScript for type checking and Babel for transpilation.

This TS code

class B extends A {
  id: string;
  ...
}

is transformed to this ES.Next code:

class B extends A {
  id;
  ...
}

According to class field proposal, unassigned id field is transformed to this.id = undefined.

In order to use the behaviour that is compliant to TypeScript, the setup needs to be changed to use @babel/plugin-transform-typescript to transpile to ES5 or use TypeScript instead of Babel, e.g. deprecated react-scripts-ts.

like image 115
Estus Flask Avatar answered Sep 27 '22 19:09

Estus Flask


Two possible workarounds:

  1. Move Object.assign to child class
  2. Create factory which will be returning instances of classes:

    export interface Ctor<T> {
        new(...parts: Partial<T>[]): T;
    }

    export class Factory {
        public static Get<T extends object>(ctor: Ctor<T>, ...props: Partial<T>[]): T {
            const res = new ctor();
            Object.assign(res, ...props);
            return res;
        }
    }
like image 36
Антон Тишин Avatar answered Sep 27 '22 20:09

Антон Тишин