Pascal Pascal - 1 month ago 8
TypeScript Question

object.assign does not work on different types with typescript

Just imagine I have 2 html tables next to each other. Each table has an array of unassigned pupils or assigned pupils for a certain test. With 2 buttons you can move those checkbox-selected pupils to the other html table.

The pupils in both html tables have nearly the same properties.

When I use Object.Assign then I get an compile error:

properties gradeScorePairs is missing in type {} and UnAssignedPupil


To fix that error I just copy/paste the same properties on both objects...

Thats a workaround but not solution for me. I use Typescript 2 and transpile to ES5.

What possibilities do I have with object.assign and different types for source/target to solve this problem?

AssignedPupil.ts

import { GradeScorePair } from './gradeScorePair';
export class AssignedPupil {

constructor(obj: any) {
this.id = obj.id;
this.name = obj.firstName + " " + obj.lastName;
}

id: number;
name: string;
gradeScorePairs: GradeScorePair[];
isSelected: boolean;
selectedGradeScorePair: GradeScorePair;
}


UnAssignedPupil.ts

import { GradeScorePair } from './gradeScorePair';
export class UnAssignedPupil {

constructor(obj: any) {
this.id = obj.id;
this.name = obj.firstName + " " + obj.lastName;
}

id: number;
name: string;
// gradeScorePairs: GradeScorePair[]; // don`t need it on the unassigned html table
isSelected: boolean;
// selectedGradeScorePair: GradeScorePair; // don`t nee it on the unassigned html table
}

unassignPupil() {
var i = this.assignedPupils.length;
while (i--) {
var p = this.assignedPupils[i];
if (p.isSelected) {
let assignedPupil: AssignedPupil = this.assignedPupils.splice(i, 1)[0];
let unAssignedPupil: UnAssignedPupil = Object.assign({}, assignedPupil);
unAssignedPupil.isSelected = false;
this.unAssignedPupils.push(unAssignedPupil);
}
}
}

assignPupil() {
var i = this.unAssignedPupils.length;
while (i--) {
var p = this.unAssignedPupils[i];
if (p.isSelected) {
let pupilToAssign: UnAssignedPupil = this.unAssignedPupils.splice(i, 1)[0];
let assignedPupil: AssignedPupil = Object.assign({}, pupilToAssign);

// Difference are these 2 lines of code
assignedPupil.gradeScorePairs = this.gradeScorePairs;
assignedPupil.selectedGradeScorePair = null;

assignedPupil.isSelected = false;
this.assignedPupils.push(assignedPupil);
}
}
}

Answer

You can tell the compiler that this empty object is of type AssignedPupil:

let assignedPupil: AssignedPupil = Object.assign({} as AssignedPupil, pupilToAssign);

That will clear the error.

The thing is, that when doing that you won't really have an instance of class AssignedPupil, you'll have an object with the same properties.
You won't have errors because of the duck typing nature of typescript.

If you intend to use that class as a data object only then you're fine (but why not just using interfaces?), but if you plan to have class methods then the variable assignedPupil won't have those.


Edit

An example:

class Point {
    constructor(public x: number, public y: number) { }

    distance(other: Point) {
        return Math.sqrt(Math.pow((this.x - other.x), 2) + Math.pow((this.y - other.y), 2));
    }

    toString() {
        return `(${this.x}, ${this.y})`;
    }
}

let a1 = new Point(0, 0);
console.log(a1.x); // 0
console.log(a1); // Point {x: 0, y: 0}
console.log(a1.toString()); // (0, 0)

let a2 = Object.assign({} as Point, a1);
console.log(a2.x); // 0
console.log(a2); // Object {x: 0, y: 0}
console.log(a2.toString()); // [object Object]

console.log(a1.distance(a2)); // 0
console.log(a2.distance(a1)); // Uncaught TypeError: a2.distance is not a function

(code in playground)