4

So I'm using d3.js with typescript and angular2, and I'm trying to extend the prototype property of selections in ngOnInit() like:

import { Component, Input, Injectable } from '@angular/core';
import { Http, HTTP_BINDINGS, Response } from '@angular/http';
import { bootstrap } from 'angular2/platform/browser';
import { RouteParams } from '@angular/router-deprecated';
import { Router } from '@angular/router-deprecated';
import { Observable } from 'rxjs/Rx';
import 'rxjs/Rx';
import * as d3 from 'd3';

import { DataService } from './data.service';

@Component({
    selector: 'measure',

    templateUrl: 'app/new-container.html',
    styleUrls: ['app/new-container.css']
})

export class NewContainer {
    public measures;
    public dimensions;
    public dimensionsAlias;

    public id: string;

    constructor(private router: Router, private routeParams: RouteParams, private _dataService: DataService) {}

    ngOnInit() {
        if (this.routeParams.get('id') != null) {
            this.id = this.routeParams.get('id');
        }

        /* deleting previous svg canvas to make room for the new one */
        var s = d3.selectAll('svg');
        s.remove();

        d3.selection.prototype.moveToFront = function() {
            return this.each(function(){
                this.parentNode.appendChild(this);
            });
        }

        d3.selection.prototype.moveToBack = function() {
            return this.each(function() {
                var firstChild = this.parentNode.firstChild;
                if( firstChild ) {
                    this.parentNode.insertBefore(this, firstChild);
                }
            });
        }

        this.getAll();
    }

    /* gets data from json files */
    getAll() {
        this._dataService.getAll().subscribe(
            data => { 
                this.measures = data[0]; 
                this.dimensions = data[1];
                this.dimensionsAlias = data[2];
            },
            err => { 
                console.error(err); 
            },
            () => {
                /* this is where I draw everything and use the moveToFront 
                   and moveToBack properties on selections */
                this.draw(); 
            }
        );
    }

The problem is that typescript is throwing an error saying:

error TS2339: Property 'moveToFront' does not exist on type 'Selection<any>'.

error TS2339: Property 'moveToBack' does not exist on type 'Selection<any>'.

These errors are being thrown, but extending the prototype successfully adds the moveToFront and moveToBack properties to selections since doing something like:

d3.select("#container").moveToFront();

works and moves that element to the front. So it works, but an error is still being thrown where I extended the prototype and every time I call moveToFront as well.

Does anyone know how to fix this error? Thanks.

2 Answers 2

2

Typescript is type safe language and it will not let you modify a property on prototype if it is not public (not exposed by type definition for purejs lib). In your case it is a new method which is not available on prototype of d3.selection (d.ts file).

That being said typescripts checks are at transpile time only as transpiled file is JS that doesn't have a type system. So your additional property is found and code works as expected.

Another beauty of ts is it allow generation of transpiled file even if there are error to bypass intentional breakage.

Now let's make ts compiler happy to not give these error

 d3.selection.prototype['moveToFront'] = function {} // may need cast protytype to any. 
Sign up to request clarification or add additional context in comments.

4 Comments

That worked for removing the error from where I extended the prototype but I'm still getting the error when I use "moveToFront". Like when I do d3.select("#container").moveToFront(), the error is still being thrown. Do you know why? Thanks for the help though!
I forgot to mention I was getting the same error every time I used moveToFront when I selected anything and those errors are still being thrown.
Indeed. As again ts won't see the method at usage. You can cast you object as any and then call moveToFront method. You can also extend definition file for d3.seletion and provide it there. That would be more better approach.
For reference of writing /extending d.ts file peter.grman.at/how-to-write-typescript-definition-files
0

Sometimes type definitions is not totally updated. try edit the definitions for your self. First take a look what type d3.select("#container") will return. If the type is compatible, verify if the "sendToFront" is defined on that type.

You can manually add this function to definition files.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.