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

最佳答案

第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。我们不想要那个!仅当我们提供打字时, typescript 才有用。
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对象以对其进行配置。实际上,我们需要定义自己的选项。 typescript - 如何为Leaflet插件添加Typescript定义-LMLPHP
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)
        }
    }
}

现在,代码完成情况看起来更好:typescript - 如何为Leaflet插件添加Typescript定义-LMLPHP

但是,我欺骗了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开始)删除 namespace 声明:
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,并测试一切仍然有效。

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

09-19 19:50