Issue
I have a base class, Sprite
, and a subclass Player
. Sprite
's constructor()
takes a few parameters, one of which is the path to an image. Player
should take all parameters except the image path, and should take a new parameter which it will use to determine the image path itself.
The question Is there a way to inherit parameters from a parent class's constructor? led me to ConstructorParameters
:
// Sprite.ts
import SomeVendoredRenderLibrary from "./vendored/SomeVendoredRenderLibrary.js";
export default abstract class Sprite {
private _image: HTMLImageElement;
public constructor(
protected canvasWidth: number,
protected canvasHeight: number,
imagePath: string
) {
this._image = SomeVendoredRenderLibrary.loadImage(imagePath);
}
}
// Player.ts
import Sprite from "./Sprite.js";
export default class Player extends Sprite {
constructor(
type: "modelA" | "modelB",
...args: ConstructorParameters<typeof Sprite>
) {
// this doesn't work!
args.imagePath = `assets/player_${type}.png`;
super(...args);
}
}
Of course, the above code fails because args
is an array and hence can't be assigned by name. Furthermore, ConstructorParameters<typeof Sprite>
requires imagePath
to be passed explicitly, although it would be redundant.
It would of course be possible to explicitly add each required parameter of Sprite
to Player
and its other subclasses, as parameters to constructor()
and arguments to super()
, but this seems like a hacky workaround, as it would require manually updating all subclasses should any changes be made to Sprite
.
What's the canonical way to inherit/passthrough constructor()
parameters from a parent class, while potentially modifying some?
Solution
From the above comments ...
@PeterSeliger yes, I know it's an array, but if I were to add a parameter to
Sprite
that changes the position ofimagePath
, subclasses would be broken. i've edited my question to clarify that. i was hoping there would be a better way? – theoIt doesn't matter whether it's
...args
,args
or passing parameters directly to the function; as long as parameters are reflected asarguments
array the problem of breaking subclasses when changing thearguments
' signature does remain. The only way of preventing the latter is to make use of a single object based parameter which features all of the former parameters as its properties. – Peter Seliger
One could do it by either extending an already existing type ...
import SomeVendoredRenderLibrary from "./vendored/SomeVendoredRenderLibrary.js";
type PlayerOptions = {
canvasWidth: number,
canvasHeight: number,
};
type SpriteOptions = PlayerOptions & {
imagePath: string,
};
class Sprite {
// ... modifier declaration
constructor({ canvasWidth, canvasHeight, imagePath }:SpriteOptions) {
// ... assignments
}
}
class Player extends Sprite {
constructor(
type: "modelA" | "modelB",
options: PlayerOptions,
) {
super({ ...options, imagePath: `assets/player_${ type }.png`});
}
}
... or by defining a single (meta) type for any constructor's single parameter and explicitly omitting properties as suggested by Shalom Peles ...
import SomeVendoredRenderLibrary from "./vendored/SomeVendoredRenderLibrary.js";
type SpriteOptions = {
canvasWidth: number,
canvasHeight: number,
imagePath: string,
};
class Sprite {
// ... modifier declaration
constructor({ canvasWidth, canvasHeight, imagePath }:SpriteOptions) {
// ... assignments
}
}
class Player extends Sprite {
constructor(
type: "modelA" | "modelB",
options: Omit<SpriteOptions, 'imagePath'>
) {
super({ ...options, imagePath: `assets/player_${ type }.png`});
}
}
Answered By - Peter Seliger
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.