第一步是使用未注释的示例代码,错误将开始在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);
// 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'.
这很合理,因为我们还没有定义它们。
如果您参考easyBar
和easyButton
的定义此处和此处, 您会发现在原始的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));
};
L.Control.EasyButton
类上调用 new
。参数有些晦涩,但你可以从 this line 推断出它们的含义。initialize: function(icon, onClick, title, id)
// easy-button.d.ts
declare namespace L {
function easyBar();
function easyButton();
}
现在我们更接近了一些:
error TS2346: Supplied parameters do not match any signature of call target
很显然,这是因为我们向easyButton
提供了2个参数'fa-edit'和回调函数,但是我们没有在参数中声明它们。我们的第二次尝试现在看起来像这样:
// easy-button.d.ts
declare namespace L {
function easyBar(buttons: any[]);
function easyButton(icon: string, onClick: (btn: any, map: any)=>void);
}
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);
}
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'.
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 { }
}
}
如果您尝试实例化一个新的EasyButton,代码补全会建议您传递一个L.ControlOptions
对象来配置它。实际上,我们需要定义自己的选项。
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)
}
}
}
然而,我在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
}
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;
(从 Leaflet 1.2.0 开始) 删除命名空间声明:
declare namespace L {
import * as L from 'leaflet'
declare module 'leaflet' {
你的测试代码现在应该像这样:
import * as L from 'leaflet'
import 'easy-button'
打开 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
,并测试一切是否正常工作。
然后提交一个拉取请求,这样每个人都可以从这项工作中受益!
import * as L from 'leaflet';
import 'leaflet-easybutton/src/easy-button';
类EasyButton扩展自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
}
因此,new L.EasyButton()的等价物是什么,以便与L.easyButton('fa fa-icon', () => '..', 'title)相匹配?
easyButton
函数,另一个是 EasyButton
类。类构造函数必须调用 new L.EasyButton(...)
,而 easyButton 函数将为您提供一个新的 EasyButton 实例。希望我已经解释得足够清楚了。 :) - Chui Tey
L.Control
如何既是类又是命名空间?),但这些正是我需要的示例,可以帮助我为另一个 Leaflet 插件编写类型。 - HighCommander4