实施D3"可重复使用的图表" TypeScript中的模式

时间:2017-06-29 23:20:17

标签: javascript typescript d3.js

下面的第2部分中的代码(工作示例here)基于第1部分中的代码,但更改为使用箭头功能,它基于Mike Bostock' Toward Resusable Charts中的模式,即返回一个具有其他功能的函数。

如果我尝试在typecript(demo here)中运行第1部分或第2部分中的代码,则表示类型{{1}上的方法addToChartstop不存在}。

如何让typescript识别添加到返回函数的函数属性(本例中为(selection: any) => () => voidaddToChart)?

第1部分

stop

第2节

const mychart = function (){
  let stop = false;
  const chart = function(selection){
    function tick(){
      console.log("tick");
    }
    return tick;
  };

  // Adding a function to the returned 
  // function as in Bostock's reusable chart pattern
  chart.addToChart = function(value){ 
    console.log("addToChart");
    return chart;
  };

  chart.stop = function(){
    return stop = true;
  }

  return chart;
}

const a = mychart();
const tick = a();
tick(); //logs tick
a.addToChart(); //logs "addToChart"

3 个答案:

答案 0 :(得分:7)

您可以定义hybrid type,即描述函数签名及其属性的接口。鉴于您的代码,它可能是这样的:

interface IChart {
    (selection: any): any;
    // Use overloading for D3 getter/setter pattern
    addToChart(): string;               // Getter
    addToChart(value: string): IChart;  // Setter
}

既然你应该像瘟疫一样避免any这可能需要进一步改进,但它应该足以让你开始。此外,为了允许 D3-ish getter / setter模式,您可以在接口声明中overload addToChart函数。

将此接口集成为可重用代码模式中的类型现在变得非常简单:

const mychart = (): IChart => {

  // Private value exposed via closure
  let value: string|undefined;

  const chart = <IChart>((selection) => {
    // Private logic
  });

  // Public interface
  // Implementing a  D3-style getter/setter.
  chart.addToChart = function(val?: string): any {
    return arguments.length ? (value = val, chart) : value;
  };

  return chart;
} 

const chart = mychart();

console.log(chart.addToChart())   // --> undefined       
chart.addToChart("Add");          // Sets private value to "Add".
console.log(chart.addToChart())   // --> "Add"       

查看可执行文件playground demo

答案 1 :(得分:3)

我想知道你是否可以使用interface / class:

interface IChart {
    constructor: Function;
    addToChart?: (number) => Chart;
    stop: () => boolean;
}

class Chart implements IChart {

    private _stop = false;
    constructor( selection ) {
        // content of tick funciton here
    }

    public addToChart = function (n: number) {
        return this;
    }
    public stop = function () {
        return this._stop = true;
    }

}

let mychart = function () {
    let stop = false;
    let chartNew: Chart = new Chart(1);
    return chartNew;
}; 

答案 2 :(得分:1)

您可以使用Object.assign创建混合类型(具有额外属性的函数),而无需定义专用接口。您可以单独定义原始内部的函数,因此每个函数可以有多个签名,如果要通过this而不是{{1}访问对象,甚至可以键入this参数}}

chart

备注

  1. 虽然知识产权按预期工作,但如果您将鼠标悬停在let mychart = function () { let isStopped = false; let value = ""; type Chart = typeof chart; // Complex method with multiple signatures function addToChart(): string function addToChart(newValue: string): Chart function addToChart(newValue?: string): string | Chart { if(newValue != undefined){ value = newValue; chart.stop() return chart; }else{ return value; } } // We can specify the type for this if we want to use this function stop(this: Chart) { isStopped = true; return this; // instead of chart, either is usable } var methods = { addToChart, stop, // inline function, we can return chart, but if we reference the Chart type explicitly the compiler explodes stop2() { isStopped = true; return chart; } }; let chart = Object.assign(function (selection) { function tick() { } return tick; }, methods); return chart; }; let d = mychart(); d(""); d.addToChart("").addToChart(); d.addToChart(); d.stop(); d.stop().addToChart("").stop2().stop() 上并查看该类型,则会比手工制作版本更加丑陋。

  2. 我单独定义d而不是methods内联,因为如果我这样做,编译器会感到困惑。

  3. 如果您不想在方法中使用Object.assign,则无需明确键入this。我展示了如何使用它,只是为了完整性,使用图表可能更容易,它确保我们不必处理传递错误this的人。

  4. 虽然上面的示例有效,但在某些情况下,编译器会放弃推理并将this的返回值设置为任意。一个这样的情况是我们在分配给mychart的对象中定义的函数内引用Chart