如何为 Leaflet 插件添加 Typescript 定义 [英] How do I add Typescript definitions for Leaflet plugins
问题描述
我想在 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屋!