patterntypescriptMinor
Emulating constructor overloading in Typescript
Viewed 0 times
typescriptemulatingconstructoroverloading
Problem
I'm writing a Matrix class, and I want to be able to construct a Matrix in two ways:
In most languages, the obvious solution would be to overload the constructor, having 1 version of it that takes an array, and another that takes the dimensions and fill value.
Unfortunately, since JavaScript, which Typescript compiles into, doesn't support function overloading, neither does Typescript. The best I could do was declare the signatures for each of the constructor versions, then manually deal with the values internally. This is the constructor that I ended up with:
Is this the safest way to achieve this? I'm mainly looking for suggestions on setting up the constructor, but any critique will be appreciated.
For context, and to make the code complete, here's the full module. Note, I'm in the middle of switching from using
- Supply a 2D array that the Matrix can use internally
- Supply the dimensions of the Matrix, and the value to fill each cell with.
In most languages, the obvious solution would be to overload the constructor, having 1 version of it that takes an array, and another that takes the dimensions and fill value.
Unfortunately, since JavaScript, which Typescript compiles into, doesn't support function overloading, neither does Typescript. The best I could do was declare the signatures for each of the constructor versions, then manually deal with the values internally. This is the constructor that I ended up with:
export class Matrix {
private dimensions: DimensionsType;
private rows: T[][];
constructor(arr: T[][]);
constructor(width: number, height: number, defaultFill: T);
constructor(arrOrWidth: T[][] | number, height?: number, defaultFill?: T) {
if (arrOrWidth instanceof Array) {
if (Matrix.verifyRowLengths(arrOrWidth)) {
this.rows = arrOrWidth;
} else {
throw new Error("All rows must be the same length!");
}
} else {
if (height !== undefined && defaultFill !== undefined) {
this.rows = Matrix.createMatrixArray(height, arrOrWidth, defaultFill);
} else {
throw new Error(
"If you don't pass an array, you must specify the dimensions and fill value.");
}
}
this.dimensions = Matrix.calcArrayDimensions(this.rows);
}
...Is this the safest way to achieve this? I'm mainly looking for suggestions on setting up the constructor, but any critique will be appreciated.
For context, and to make the code complete, here's the full module. Note, I'm in the middle of switching from using
Solution
A few of thoughts:
-
You don't need to declare types for the arguments in the implementation of your constructor, just the overloads (which precede the implementation).
-
I wouldn't use a separate object for the height and width, why not declare them as private members?
-
Consider throwing TypeError instead of plain Error if your constructor can't figure the arguments out.
-
Consider replacing
-
Optimize ordering of the overload detection logic to avoid duplicate code. In doing this you can mutate the arguments and their types.
Here's a rough outline
Finally...
e.g.
-
You don't need to declare types for the arguments in the implementation of your constructor, just the overloads (which precede the implementation).
-
I wouldn't use a separate object for the height and width, why not declare them as private members?
-
Consider throwing TypeError instead of plain Error if your constructor can't figure the arguments out.
-
Consider replacing
private static verifyRowLengths() with a more general health-test method named checkInvariants(). There are more tests it can do for you, e.g. are the types of all elements the same?-
Optimize ordering of the overload detection logic to avoid duplicate code. In doing this you can mutate the arguments and their types.
Here's a rough outline
export class Matrix {
private width: number;
private height: number;
private rows: T[][];
constructor(arr: T[][]);
constructor(width: number, height: number, defaultFill: T);
constructor(a1, a2?, a3?) {
if (typeof a1 === 'number' && typeof a2 == 'number') {
a1 = Matrix.createRows( a1, a2 )
}
if (a1 instanceof Array) {
this.rows = a1;
//...
} else {
throw new TypeError("Unxpected arguments to Matrix constructor")
}
this.checkInvariants()
}
private static createRows(width: number, height: number) : T[][] {
return null;
}
private checkInvariants() : void {
// ...
}
}Finally...
- Consider public static methods which return a new Matrix instead of constructors overloads.
e.g.
export class Matrix {
...
public static diagonal( size: number ) Matrix { // ...
}Code Snippets
export class Matrix<T> {
private width: number;
private height: number;
private rows: T[][];
constructor(arr: T[][]);
constructor(width: number, height: number, defaultFill: T);
constructor(a1, a2?, a3?) {
if (typeof a1 === 'number' && typeof a2 == 'number') {
a1 = Matrix.createRows( a1, a2 )
}
if (a1 instanceof Array) {
this.rows = a1;
//...
} else {
throw new TypeError("Unxpected arguments to Matrix constructor")
}
this.checkInvariants()
}
private static createRows<T>(width: number, height: number) : T[][] {
return null;
}
private checkInvariants() : void {
// ...
}
}export class Matrix<T> {
...
public static diagonal<T>( size: number ) Matrix<T> { // ...
}Context
StackExchange Code Review Q#124849, answer score: 7
Revisions (0)
No revisions yet.