I have been contemplating the best method and potential impacts of referencing constants from outside a class within the same file.
The issue arose when I was creating a basic class that would throw an error if an invalid parameter was passed:
export class Digit {
private _value: number = null;
constructor(value: number) {
this._value = value;
}
set value(value: number) {
if (isNaN(value)) { throw new Error('Received NaN as digit.'); }
this._value = value;
}
get value() {
return this._value;
}
}
The setter for value wouldn't trigger in the constructor since the class must be instantiated first.
To keep things straightforward, I wanted this example class to only hold a valid digit or nothing at all, so initializing it with null on its value wasn't ideal. This led me to modify the code as follows:
export class Digit {
private _value: number = null;
constructor(value: number) {
if (isNaN(value)) { throw new Error('Received NaN as digit.'); }
this._value = value;
}
set value(value: number) {
if (isNaN(value)) { throw new Error('Received NaN as digit.'); }
this._value = value;
}
get value() {
return this._value;
}
}
This solution worked, but it involved repeating the validation for each field. Imagine having to validate 10 fields or more!
So I considered two alternatives:
1- Restructure the code to include a validation function within the class
export class Digit {
private _value: number = null;
private readonly validateValue = function (value: number): number {
if (isNaN(value)) { throw new Error('Received NaN as digit.'); }
return value;
};
constructor(value: number) {
this._value = this.validateValue(value);
}
set value(value: number) {
this._value = this.validateValue(value);
}
get value() {
return this._value;
}
}
I like this approach because all the logic is encapsulated within the class. However, the downside is that external entities can still access this logic if they disregard the private scope. Additionally, adding more fields and validations can clutter the class quickly, making it harder to focus on fixing specific behaviors while ensuring correct instance values.
2- Move the validation function outside of the class, but within the same file
const validateValue = function (value: number): number {
if (isNaN(value)) { throw new Error('Received NaN as digit.'); }
return value;
};
export class Digit {
private _value: number = null;
constructor(value: number) {
this._value = validateValue(value);
}
set value(value: number) {
this._value = validateValue(value);
}
get value() {
return this._value;
}
}
The benefit of this approach is being able to focus directly on the class code, knowing that the values are already validated without them cluttering the class. It also prevents accidental omission of a private scope to access validators.
If the validations become extensive, a helper function could be created for the class, and the validations could be moved out of the file (though I'm unsure if this is considered good practice).
However, I'm curious about the implications of using such external declarations outside of the class.
What would be the most suitable approach for this issue? Are both solutions viable, or is there a better way?