typescript class to throw error when undefined is passed as a parameter to constructor

Viewed 705

I have a class with a lot of parameters, a simplified version is shown below:

class data {

    ID: string;
    desp: string;

    constructor(con_ID:string,con_desp:string){
        this.ID = con_ID;
        this.desp = con_desp;
    }
}

I am then receiving data from a RESTful call, the body of the call is JSON. It might not have all the parameters requried to create an instance of data. Below is an example of the desp not being passed.

const a = JSON.stringify({ ID: 'bob' });
const b = JSON.parse(a)

If I try to create a new instance of data, it works.

console.log(new data(b['ID'], b['desp']))
>> data { ID: undefined, desp: 'bob' }

How do I reject the construction of the class if a parameter from JSON is undefined?

One method would be to do this for each parameter within the constructor, but I don't think this is the correct solution:

    if (con_ID== undefined){
        throw new Error('con_ID is undefined')
    }
1 Answers

We can utilize class decorators for this. If we return a class from the decorator then the class' constructor will replace the one defined in code. Then we use parameter decorators to store the index of each parameter we wish to check into an array.

const noUndefinedKey = Symbol("noUndefinedKey");
 
const NoUndefined: ParameterDecorator = function (target, key, index) {
    const data = Reflect.getMetadata(noUndefinedKey, target) ?? [];

    data.push(index);

    Reflect.defineMetadata(noUndefinedKey, data, target);
};

const Validate = function (target: { new (...args: any[]): any }) {
    const data = Reflect.getMetadata(noUndefinedKey, target);

    return class extends target {
        constructor(...args: any[]) {
            data.forEach((index: number) => {
                if (typeof args[index] === "undefined") throw new TypeError(`Cannot be undefined.`);
            });

            super(...args);
        }
    }
}

Note that reflect-metadata must be used to use Reflect.getMetadata and Reflect.defineMetadata. Here's how you would use it:

@Validate
class ProtectedFromUndefined {
    constructor(@NoUndefined param: string) {}
}

And try a few things:

//@ts-ignore throws error because undefined was provided
new ProtectedFromUndefined()
//@ts-ignore
new ProtectedFromUndefined(undefined)

// is ok
new ProtectedFromUndefined("")

Playground

Related