如何为Leaflet插件添加Typescript定义 [英] How do I add Typescript definitions for Leaflet plugins
问题描述
我想将Easy-buttons插件与Typescript https://github.com/CliffCloud/Leaflet.EasyButton/blob/master/src/easy-button.js ,但它没有Typescript注释.
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.
推荐答案
步骤1-点亮错误
第一步是按原样使用没有Typescript注释的示例代码,错误将开始在VS Code中显示.
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);
我们为此创建了一个名为"easy-button.d.ts"的文件,并在我们的Typescript文件中对其进行引用.
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);
easy-button.d.ts中没有任何内容
And there's nothing in 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'.
这很公平,因为我们还没有定义它们.
Which is fair enough because we haven't defined these yet.
如果您引用easyBar
和easyButton
的定义此处和在这里,您会发现原始Javascript声明中发生了一些不可思议的事情.似乎这两个函数没有任何参数,但实际上它们有任何参数.
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));
};
此函数将在L.Control.EasyButton
类上调用new
.这些参数有些含糊,但您可以从这行给出:
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)
第2步-添加类型
// 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
这很明显,因为我们提供了两个参数'fa-edit'和easyButton
的回调,但是我们没有在参数中声明任何内容.现在我们的第二次尝试是这样的:
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);
}
,现在所有的Typescript警告均已消失.但是还有更多的事情可以做.例如,easyButton
实际上接受4个参数.这很容易解决-观察可选参数如何具有?
后缀:
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);
}
第3步-提供返回值
easyButton
方法实际上返回一个L.Control.EasyButton
实例.当前,Typescript定义暗含easyButton
返回类型any
.我们不想要那个!仅当我们提供打字时,打字稿才有用.
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再次开始提供有用的警告:
Typescript starts providing useful warnings again:
error TS2339: Property 'addTo' does not exist on type 'EasyBar'.
这是因为EasyBar子类L.Control
,我们需要将该定义带入定义文件中.
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 { }
}
}
步骤4-为EasyButton和EasyBar提供构造函数参数
如果尝试实例化新的EasyButton,则代码完成建议您应传入L.ControlOptions
对象以进行配置.实际上,我们需要定义自己的选项.
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:
但是,我在states
选项上作弊.我声明为any
.实际上应该是
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
}
第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开始)删除名称空间声明:
Step 6 Make modifications to augment leaflet types definitions
(Starting with Leaflet 1.2.0) Remove the namespace declaration:
declare namespace L {
并用模块增强替换它:
import * as L from 'leaflet'
declare module 'leaflet' {
您的测试代码现在应如下所示:
your test code should now read like this:
import * as L from 'leaflet'
import 'easy-button'
第7步集成到原始项目源中
打开node_modules\leaflet-easybutton\package.json
,并在style
条目下方添加以下行:
Step 7 integrate into the original project sources
Open up node_modules\leaflet-easybutton\package.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",
将我们的easy-button.d.ts
移至node_modules/leaflet-easybutton/src
,并测试一切是否正常.
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屋!