如何为 Leaflet 插件添加 Typescript 定义 [英] How do I add Typescript definitions for Leaflet plugins

查看:42
本文介绍了如何为 Leaflet 插件添加 Typescript 定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 Typescript 中使用 easy-buttons 插件

声明命名空间 L {功能easyBar(按钮:Control.EasyButton[],选项?:EasyBarOptions):Control.EasyBar;function easyButton(icon: string, onClick: (btn: any, map: any)=>void, title?: string, id?: string) : Control.EasyButton;接口 EasyBarOptions {位置?:控制位置id?: 字符串leafletClasses?: 布尔值}接口 EasyButtonOptions {位置?:控制位置id?: 字符串类型?:替换"|动画"州?:任何leafletClasses?: 布尔值标记名?:字符串}命名空间控制 {类 EasyButton 扩展 L.Control {构造函数(选项?:EasyButtonOptions)}类 EasyBar 扩展 L.Control {构造函数(选项?:EasyBarOptions)}}}

现在代码完成看起来更好:

但是,我在 states 选项上作弊.我将其声明为 any.其实应该是这样的

 接口 EasyButtonOptions {位置?:控制位置id?: 字符串类型?:替换"|动画"状态?:EasyButtonState[]leafletClasses?: 布尔值标记名?:字符串}接口 EasyButtonState {状态名称:字符串onClick: () =>空白标题:字符串图标:字符串}

第 5 步 - 添加 jsdoc 提示

Typescript 将为该插件的用户提供有用的评论.这是我们如何为 easyButton

提供文档的示例

/*** 创建一个简单的按钮* @param 图标,例如地球仪* @param onClick 按钮点击处理程序* @param 按钮上的标签* @param 用于标记按钮的 id* @例子* var helloPopup = L.popup().setContent('Hello World!');** L.easyButton('fa-globe', function(btn, map){* helloPopup.setLatLng(map.getCenter()).openOn(map);* }).addTo( YOUR_LEAFLET_MAP );*/功能简单按钮(图标:字符串,onClick:(btn:Control.EasyButton,地图:L.Map)=>空白,标题?:字符串,id?: string): Control.EasyButton;

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

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

声明命名空间 L {

并将其替换为模块扩充:

import * as L from 'leaflet'声明模块传单"{

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

import * as L from 'leaflet'导入简易按钮"

第 7 步集成到原始项目源中

打开 node_modulesleaflet-easybuttonpackage.json 并在 style 条目下方添加以下行:

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

将我们的 easy-button.d.ts 移动到 node_modules/leaflet-easybutton/src 中,并测试一切是否仍然有效.

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

I would like to use easy-buttons plugin with Typescript https://github.com/CliffCloud/Leaflet.EasyButton/blob/master/src/easy-button.js and but it doesn't come with Typescript annotations.

解决方案

Step 1 - light up the errors

The first step is to use the sample code as-is without Typescript annotations, and the errors will start lighting up in 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);

To which we create a file called 'easy-button.d.ts' and refer to it in our Typescript file.

// 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);

And there's nothing in easy-button.d.ts

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

The error says

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

Which is fair enough because we haven't defined these yet.

If you refer to definition of easyBar and easyButton here and here, you will find there's a bit of magic occuring in the original Javascript declarations. It appears that these two functions don't take any arguments, but in reality they do.

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));
};

This function is going to call new on the L.Control.EasyButton class. The parameters are somewhat cryptic but you can infer them from this line that gives:

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

Step 2 - add the typings

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

and now we are a bit closer:

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

and that's quite obvious because we supplied 2 parameters 'fa-edit' and a callback to easyButton but we didn't declare any in our arguments. Our second attempt now looks like this:

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

and now all the Typescript warnings have gone away. But there's more that can be done. For one, easyButton actually takes 4 arguments. That's easy to fix - observe how optional arguments have a ? suffix:

// 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);
}

Step 3 - provide return values

The easyButton method actually returns an L.Control.EasyButton instance. Currently, the Typescript definition implies the easyButton returns type any. We don't want that! Typescript is helpful only when we provide typings.

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 starts providing useful warnings again:

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

This is because EasyBar subclasses L.Control we need to bring that definition into our definition file.

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 { }
    }
}

Step 4 - provide constructor arguments to EasyButton and EasyBar

If you try to instantiate a new EasyButton, code completion suggests that you should pass in a L.ControlOptions object to configure this. Actually we need to define our own options.

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)
        }
    }
}

Things look better now on code completion:

However, I cheated on the states option. I declared that as any. In actuality it ought to be

    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
    }

Step 5 - add jsdoc hints

Typescript will provide helpful comments to users of this plugin. Here's an example of how we might provide documentation for 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;

Step 6 Make modifications to augment leaflet types definitions

(Starting with Leaflet 1.2.0) Remove the namespace declaration:

declare namespace L {

and replace it with module augmentation:

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

your test code should now read like this:

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

Step 7 integrate into the original project sources

Open up node_modulesleaflet-easybuttonpackage.json and add the following line below the style entry:

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

Move our easy-button.d.ts into node_modules/leaflet-easybutton/src, and test that everything still works.

Then submit a pull request so that every one can benefit from the work!

这篇关于如何为 Leaflet 插件添加 Typescript 定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆