2017-05-25 55 views
0

我有下面的代码,我试图使强类型,以便更容易维护。typescript - 在类型中表示模块模式

不过,对于菜单可变我得到以下错误:

[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

不幸的是,在你的情况下没有惯用的方法来扩展功能。唯一的退步是将菜单功能投射到any

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

打字稿具有另一功能调用“命名空间合并”,以适应功能扩展文字。

function menu() {} 
namespace menu { 
    export function remove() {} 
} 
menu.remove() // compiles 

但是,namespace只能出现在模块的顶层或嵌套在另一个名称空间中。你不能在函数闭包中声明它。所以在这种情况下,您必须以任何方式回退到any