如何为Leaflet插件添加Typescript定义

时间:2017-06-10 11:34:27

标签: typescript leaflet

我想使用带有Typescript https://github.com/CliffCloud/Leaflet.EasyButton/blob/master/src/easy-button.js的easy-buttons插件,但它没有使用Typescript注释。

3 个答案:

答案 0 :(得分:8)

第1步 - 点亮错误

第一步是按原样使用示例代码而不使用Typescript注释,错误将在VS Code中开始点亮。

// sample.ts
L.easyBar([
  L.easyButton('fa-file', function(btn, map){ }),
  L.easyButton('fa-save', function(btn, map){ }),
  L.easyButton('fa-edit', function(btn, map){ }),
  L.easyButton('fa-dot-circle-o', function(btn, map){ })
]).addTo(map);

我们创建了一个名为'easy-button.d.ts'的文件,并在我们的Typescript文件中引用它。

// sample.ts
import "./easy-button"
L.easyBar([
  L.easyButton('fa-file', function(btn, map){ }),
  L.easyButton('fa-save', function(btn, map){ }),
  L.easyButton('fa-edit', function(btn, map){ }),
  L.easyButton('fa-dot-circle-o', function(btn, map){ })
]).addTo(map);

而easy-button.d.ts中没有任何内容。

// easy-button.d.ts
// empty for now

错误说

error TS2339: Property 'easyBar' does not exist on type 'typeof L'.
error TS2339: Property 'easyButton' does not exist on type 'typeof L'.

这是公平的,因为我们还没有定义这些。

如果您参考easyBareasyButton herehere的定义,您会发现原始Javascript声明中出现了一些奇迹。似乎这两个函数没有任何参数,但实际上它们确实存在。

L.easyButton = function(/* args will pass automatically */){
  var args = Array.prototype.concat.apply([L.Control.EasyButton],arguments);
  return new (Function.prototype.bind.apply(L.Control.EasyButton, args));
};

此函数将在new类上调用L.Control.EasyButton。参数有点神秘,但您可以从this line推断出它们:

initialize: function(icon, onClick, title, id)

第2步 - 添加打字

// easy-button.d.ts
declare namespace L {
    function easyBar();
    function easyButton();
}

现在我们离得更近了:

error TS2346: Supplied parameters do not match any signature of call target

这很明显因为我们提供了2个参数'fa-edit'和回调到easyButton,但我们没有在参数中声明任何参数。我们的第二次尝试现在看起来像这样:

// easy-button.d.ts
declare namespace L {
    function easyBar(buttons: any[]);
    function easyButton(icon: string, onClick: (btn: any, map: any)=>void);
}

现在所有的Typescript警告已经消失了。但还有更多可以做的事情。例如,easyButton实际上需要4个参数。这很容易修复 - 观察可选参数如何具有?后缀:

// easy-button.d.ts
declare namespace L {
    function easyBar(buttons: any[]);
    function easyButton(icon: string, onClick: (btn: any, map: any)=>void, title?: string, id?: string);
}

第3步 - 提供返回值

easyButton方法实际上返回L.Control.EasyButton个实例。目前,Typescript定义意味着easyButton返回类型any。我们不希望这样!打字稿仅在我们提供打字时才有用。

declare namespace L {
    function easyBar(buttons: Control.EasyButton[]): Control.EasyBar;
    function easyButton(icon: string, onClick: (btn: any, map: any)=>void, title?: string, id?: string) : Control.EasyButton;

    namespace Control {
        class EasyButton { };
        class EasyBar { };
    }
}

Typescript再次开始提供有用的警告:

error TS2339: Property 'addTo' does not exist on type 'EasyBar'.

这是因为EasyBar子类L.Control我们需要将该定义带入我们的定义文件中。

declare namespace L {
    function easyBar(buttons: Control.EasyButton[]): Control.EasyBar;
    function easyButton(icon: string, onClick: (btn: any, map: any)=>void, title?: string, id?: string) : Control.EasyButton;

    namespace Control {
        class EasyButton extends L.Control { }
        class EasyBar extends L.Control { }
    }
}

第4步 - 为EasyButton和EasyBar

提供构造函数参数

如果您尝试实例化一个新的EasyButton,代码完成表明您应该传入L.ControlOptions对象来配置它。实际上我们需要定义自己的选项。Code completion

declare namespace L {
    function easyBar(buttons: Control.EasyButton[], options?: EasyBarOptions): Control.EasyBar;
    function easyButton(icon: string, onClick: (btn: any, map: any)=>void, title?: string, id?: string) : Control.EasyButton;

    interface EasyBarOptions {
        position?: ControlPosition
        id?: string
        leafletClasses?: boolean
    }

    interface EasyButtonOptions {
        position?: ControlPosition
        id?: string
        type?: 'replace'|'animate'
        states?: any
        leafletClasses?: boolean
        tagName?: string
    }

    namespace Control {
        class EasyButton extends L.Control {
            constructor(options?: EasyButtonOptions)
        }
        class EasyBar extends L.Control {
            constructor(options?: EasyBarOptions)
        }
    }
}

现在代码完成情况看起来更好:new EasyButton code completion

但是,我在states选项上作弊。我宣称为any。实际上它应该是

    interface EasyButtonOptions {
        position?: ControlPosition
        id?: string
        type?: 'replace'|'animate'
        states?: EasyButtonState[]
        leafletClasses?: boolean
        tagName?: string
    }

    interface EasyButtonState {
        stateName: string
        onClick: () => void
        title: string
        icon: string
    }

第5步 - 添加jsdoc提示

Typescript将为此插件的用户提供有用的评论。以下是我们如何为easyButton

提供文档的示例
   /**
     * Creates a easyButton
     * @param icon e.g. fa-globe
     * @param onClick the button click handler
     * @param label on the button
     * @param an id to tag the button with
     * @example
     * var helloPopup = L.popup().setContent('Hello World!');
     *
     * L.easyButton('fa-globe', function(btn, map){
     *      helloPopup.setLatLng(map.getCenter()).openOn(map);
     *  }).addTo( YOUR_LEAFLET_MAP );
     */
    function easyButton(
        icon: string,
        onClick: (btn: Control.EasyButton, map: L.Map) => void,
        title?: string,
        id?: string): Control.EasyButton;

步骤6进行修改以增加传单类型定义

(从Leaflet 1.2.0开始)删除名称空间声明:

declare namespace L {

并将其替换为模块扩充:

import * as L from 'leaflet'
declare module 'leaflet' {

您的测试代码现在应该如下所示:

import * as L from 'leaflet'
import 'easy-button'

步骤7集成到原始项目源

打开node_modules\leaflet-easybutton\package.json并在style条目下添加以下行:

  "main": "src/easy-button.js",
  "style": "src/easy-button.css",
  "typings": "src/easy-button.d.ts",

将我们的easy-button.d.ts移至node_modules/leaflet-easybutton/src,并测试一切仍然有效。

然后提交拉取请求,以便每个人都可以从工作中受益!

答案 1 :(得分:0)

class EasyButton extends L.Control { constructor(options?: EasyButtonOptions) }

你的新L.EasyButton选择了一个' EasyButtonOptions'构造函数的对象。这与' L.easyButton'例子

L.easyButton有以下选项: function easyButton(icon: string, onClick: (btn: any, map: any)=>void, title?: string, id?: string) : Control.EasyButton;

EasyButtonOptions没有' icon' onClick',' title'或者' id'相反,它需要:

interface EasyButtonOptions { position?: ControlPosition id?: string type?: 'replace'|'animate' states?: EasyButtonState[] leafletClasses?: boolean tagName?: string }

因此,与L.easyButton(' fa fa-icon',()=>' ...'匹配的新L.EasyButton()相当于什么? ,'标题)?

答案 2 :(得分:0)

它实际上现在已经执行了。您应该

<=