typescript - 表示类型中的模块模式

时间:2017-05-25 12:43:22

标签: javascript typescript d3.js

我有以下代码,我尝试强类型,以便更容易维护。

但是对于菜单变量,我收到以下错误:

[ts]
Type '(x: number, y: number) => void' is not assignable to type 'ContextMenu'.
  Property 'items' is missing in type '(x: number, y: number) => void'.



import * as d3 from "d3";
import "d3-selection-multi";

interface ContextMenu {
    (x: number, y: number) : void;
    items(items?: string[]): string[] | this;
    remove(): void;
}

export function contextMenu(): ContextMenu {
    var height,
        width, 
        margin = 0.1, // fraction of width
        items = [], 
        rescale: boolean = false, 
        style = {
            'rect': {
                'mouseout': {
                    "fill": 'rgb(244,244,244)', 
                    "stroke": 'white', 
                    "strokeWidth": '1px'
                }, 
                'mouseover': {
                    "fill": 'rgb(200,200,200)'
                }
            }, 
            'text': {
                'fill': 'steelblue', 
                'font-size': '13'
            }
        }; 

    var menu: ContextMenu = function (x:number, y:number) {
        menu.remove();
        scaleItems();

        // Draw the menu
        d3.selectAll('svg.chart')
            .append('g').attr('class', 'context-menu')
            .selectAll('tmp')
            .data(items).enter()
            .append('g').attr('class', 'menu-entry')
            .style('cursor', 'pointer')
            .on('mouseover', function() {
                d3.select(this).select('rect').styles((<any>style).rect.mouseover) })
            .on('mouseout', function() {
                d3.select(this).select('rect').styles((<any>style).rect.mouseout) });

        d3.selectAll('.menu-entry')
            .append('rect')
            .attr('x', x)
            .attr('y', (d, i) => y + (i * height))
            .attr('width', width)
            .attr('height', height)
            .styles((<any>style).rect.mouseout);

        d3.selectAll('.menu-entry')
            .append('text')
            .text((d: string) => d)
            .attr('x', x)
            .attr('y', (d, i) => y + (i * height))
            .attr('dy', height - margin / 2)
            .attr('dx', margin)
            .styles((<any>style).text);

        // Other interactions
        d3.select('body')
            .on('click', function() {
                d3.select('.context-menu').remove();
            });
    }

    menu.remove = function() {
        d3.selectAll(".context-menu").remove();
    };

    menu.items = function(_?) {
        return (!arguments.length) 
        ? items 
        :(items = _, rescale = true, menu);
    }

    // Automatically set width, height, and margin;
    function scaleItems() {
        if (!rescale) {
            return;
        }
        d3.selectAll('svg').selectAll('tmp')
            .data(items).enter()
            .append('text')
            .text(d => d)
            .styles(<any>style.text)
            .attr('x', -1000)
            .attr('y', -1000)
            .attr('class', 'tmp');

        var z = d3.selectAll('.tmp')
            .nodes()
            .map((x:any) => x.getBBox());

        width = d3.max(z.map(x => x.width));
        margin = margin * width;
        width =  width + 2 * margin;
        height = d3.max(z.map(x => x.height + margin / 2 ));

        // cleanup
        d3.selectAll('.tmp').remove();
        rescale = false;
    }
    return menu;
}

如何编译代码但保持相同代码风格的惯用D3?

1 个答案:

答案 0 :(得分:1)

可悲的是,在您的情况下,没有惯用的扩展功能的方法。唯一的后备是将菜单功能转换为val myValue = f() val myList: List<Any> if (myValue != null) { myList = listOf(myValue) } else { myList = emptyList() }

any

TypeScript有另一个功能调用“名称空间合并”以适应扩展函数文字。

var menu: ContextMenu = function (x:number, y:number) {
    // ....
} as any

但是,function menu () {} namespace menu { export function remove() {} } menu.remove() // compiles 只能出现在模块的顶层或嵌套在另一个命名空间中。你不能在函数闭包中声明它。因此,在这种情况下,您必须以任何方式回退到namespace