Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the type of the <svg> element in typescript [duplicate]

I am implementing the following class:

export class BbSVGElement extends HTMLElement implements HTMLCanvasElement { ... }

The problem is that I can't find the type of the <svg> element in typescript: <span> elements are HTMLSpanElement, <canvas> elements are HTMLCanvasElement, etc.

I ask because to implement a simple task such as the height() function, I need to get the bounding box of the element. According to this stack overflow answer, this is the right way to do it:

height(): number { return this.getBBox().height}

But since this refers to an HTMLElement, of course Property 'getBBox' does not exist on type 'BbSVGElement'.ts(2339)

So I tried with castings of all kinds; things like:

return (<SVGElement> this).getBBox().width
return (<SVGSVGElement> this).getBBox().width

of course I could do something like

return (<any> this).getBBox().width

or

return this.offsetWidth // <-- This property here comes from HTMLElement

But I'd rather not if possible.

So: What would be the right casting to do? Or would there be another option I missed?

like image 733
Marche Remi Avatar asked Dec 31 '22 22:12

Marche Remi


1 Answers

What is the type of the <svg> element in TypeScript?

The interface type of <svg> elements in an *.svg document (i.e. the SVG DOM) is SVGElement. The type of an <svg> element within a HTML document (i.e. the HTML DOM) is actually a prototype-less object that implements both HTMLElement and SVGElement!

So in TypeScript, you can represent an <svg> element in the DOM by using an intersection type (which represents a type that combines all members of two or more types, compared to a union type which represents a type that combines shared members of two or more types).

Like so:

type SvgInHtml = HTMLElement & SVGElement;

const svgElement: SvgInHtml = document.createElement('svg') as SvgInHtml;

const svgHeight = svgElement.getBBox().height; // Using `SVGElement.getBBox()`.
const htmlWidth = svgElement.offsetWidth;      // Using `HTMLElement.offsetWidth`

I see you want to represent this using a derived class - but that is incorrect (as class inheritance only allows a single parent class), instead either use a non-encapsulated type SvgInHtml reference, or encapsulate it in an object or new class (with no superclass):

type SvgInHtml = HTMLElement & SVGElement;

class MySvgWrapper {

    constructor( private readonly svgElement: SvgInHtml ) {
    }

    get height(): number {
        return this.svgElement.getBBox().height;
    }
}

(This is my original answer I posted before I understood your problem correctly - I'm keeping it here and accessible because I feel it may still be useful for other people who find this question but have a different problem to yours):

The base SVG-DOM interface for all (most?) SVG elements is SVGElement which is documented on MDN.

Assuming you're using TypeScript 3.x or later which has the SVG-DOM interfaces in lib.dom.d.ts (see the content of my answer below the line) then you need to change your class declaration to this:

export class MySVGElement extends SVGElement {
    
}

Since TypeScript 3.x, TypeScript's "standard library" of typings for the HTML DOM includes the SVG DOM interfaces in lib/lib.dom.d.ts, so you shouldn't need to download, import, or add anything - just use SVGElement (and its derived interfaces) directly.

If you open the current TypeScript lib.dom.d.ts in GitHub you can search for "SVGElement" (and any other SVG-DOM interface, like SVGGraphicsElement) and see it's declared in there:

e.g.:

/** SVG elements whose primary purpose is to directly render graphics into a group. */
interface SVGGraphicsElement extends SVGElement, SVGTests {
    readonly transform: SVGAnimatedTransformList;
    getBBox(options?: SVGBoundingBoxOptions): DOMRect;
    getCTM(): DOMMatrix | null;
    getScreenCTM(): DOMMatrix | null;
    addEventListener<K extends keyof SVGElementEventMap>(type: K, listener: (this: SVGGraphicsElement, ev: SVGElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
    addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
    removeEventListener<K extends keyof SVGElementEventMap>(type: K, listener: (this: SVGGraphicsElement, ev: SVGElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
    removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}

If you get any errors relating to unknown/undefined types then please post that as a new StackOverflow question.

like image 125
Dai Avatar answered Jan 10 '23 12:01

Dai