1

I have a class I can't change, simplified version shown. I got an array of variations on the class. I need to add the rest of the class to each member of the array before I can use them.

class ValueType {
    name: string;
    size: number = 5;

    constructor(source?: {
        name?: string;
        size?: number;
    }) {
        if (source) {
           Object.assign(this, source);
        }
    }
};

So this can be done by adding the class to each member of the array individually, either of these two ways, but that, with a big array, is 'messy code' IMHO.

let test1 = [new ValueType({
    name: 'john',
})];

console.log('tv1 is:', test1[0].size);  // 5

// OR

let test2 = [{
    name: 'paul',
    ...new ValueType()
}];

console.log('tv2 is:', test2[0].size);  // 5

Here's my attempt at an declarative way, but it doesn't work. It doesn't allow you get the default value of the un-updated items.

let test3: Partial<ValueType>[] = [{
    name: 'tom',
}];

console.log('tv3 is:', test3[0].size); // undefined

Finally, here's an imperative approach that achieves what I want, but I can't help but think that TypeScript has a better way of doing this with types, Utility Types, ANDs, ORs, etc!

let test4: ValueType[] = [{
    name: 'tom',
}].map(value => Object.assign(value, new ValueType()));

console.log('tv4 is:', test4[0].size);
Scala Enthusiast
  • 399
  • 8
  • 18
  • What does `an array of variations on the class` mean? What does `add the rest of the class` mean and why do you need to add it? – cefn Mar 11 '23 at 11:38
  • 1
    Typescript offers static type analysis but not much in actual runtime utilities. The code you are sharing expects a certain runtime behaviour so you need to look for what Javascript has to offer in terms of utility. The problem with `test3` in particular is that you created an object that is compatible with type `ValueType` but is not an instance of class `ValueType` . In Typescript a class defines both the class itself as well as a type that matches the class. I think your 4th approach is probably as good as you can get, though I would only run `new ValueType()` once – apokryfos Mar 11 '23 at 11:39
  • 1
    @cefn - point taken, I tried to give lots of examples to show what I meant. Basically my object has 30+ properties, each with a default value, I'm getting back from the server an array of a few 1000 items, each of which has 3 or 4 properties. These items are to be converted into and array of the object, just with the 3 or 4 properties overriding the defaults in the object. Hope that helps? – Scala Enthusiast Mar 11 '23 at 12:16

1 Answers1

1

Typescript doesnt adds codes to JS Objects at runtime. So to achieve what do you want, you need to declare an factory method (check for Factory Pattern).

So, in your exemple, the factory will receive data as params, and return the instance desired. You can declare a factory function or an static factory method inside your class.

Example

Factory as Function

function factoryValueType(data: Partial<ValueType>): ValueType {
    return new ValueType(data);
}

Factory as Static Method

class ValueType {
    name: string;
    size: number = 5;

    constructor(source?: {
        name?: string;
        size?: number;
    }) {
        if (source) {
            Object.assign(this, source);
        }
    }

    static fabricate(data): ValueType {
        return new ValueType(data);
    }
}

Declaritive use to initialize an Array

valueTypeList: ValueType[] = [
    ValueType.fabricate({name: 'John'}), // use factory as static method
    factoryValueType({name: 'Doe'}), // use factory as function
]

You can utilize this to transform json objects

valueTypeList: ValueType[] = jsonList.map(obj => factoryValueType(obj));

Using factory pattern is cleaner and safier, you can debug and create test for your factories. Just remeber that there is no unique solution, this examples abouve is just one of them.

Riheldo Melo
  • 114
  • 5