Hur lägger jag till Typescript-definitioner för Leaflet-plugins
Jag skulle vilja använda easy-buttons-plugin med Typescript https://github.com/CliffCloud/Leaflet.EasyButton/blob/master/src/easy-button.js men det levereras inte med Typescript-anteckningar.
Svar #1
Steg 1 - lys upp felen
Det första steget är att använda exempel koden som den är utan TypeScript-anteckningar, och felen kommer att börja dyka upp i 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);
Till vilket vi skapar en fil kallad 'easy-button.d.ts' och hänvisar till den i vår Typescript-fil.
// 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);
Och det finns inget i easy-button.d.ts
// easy-button.d.ts
// empty for now
Felet säger
error TS2339: Property 'easyBar' does not exist on type 'typeof L'.
error TS2339: Property 'easyButton' does not exist on type 'typeof L'.
Vilket är rättvist eftersom vi inte har definierat dessa än.
Om du hänvisar till definitionen av easyBar och easyButton här och här, kommer du att se att det sker lite magi i de ursprungliga Javascript-deklarationerna. Det verkar som att dessa två funktioner inte tar några argument, men i verkligheten gör de det.
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));
};
Denna funktion kommer att anropa new på L.Control.EasyButton-klassen. Parametrarna är något kryptiska men du kan härleda dem från denna rad som ger:
initialize: function(icon, onClick, title, id)
Steg 2 - lägg till typerna
// easy-button.d.ts
declare namespace L {
function easyBar();
function easyButton();
}
och nu är vi lite närmare:
error TS2346: Supplied parameters do not match any signature of call target
och det är ganska uppenbart eftersom vi levererade 2 parametrar 'fa-edit' och en callback till easyButton men vi deklarerade inga i våra argument. Vårt andra försök ser nu ut så här:
// easy-button.d.ts
declare namespace L {
function easyBar(buttons: any[]);
function easyButton(icon: string, onClick: (btn: any, map: any)=>void);
}
och nu har alla Typescript-varningar försvunnit. Men det finns mer som kan göras. För det första tar easyButton faktiskt 4 argument. Det är lätt att åtgärda - notera hur valfria argument har suffixet ?:
// 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);
}
Steg 3 - ange returvärden
Metoden easyButton returnerar faktiskt en instans av L.Control.EasyButton. För närvarande antyder Typescript-definitionen att easyButton returnerar typen any. Vi vill inte ha det! Typescript är bara användbart när vi tillhandahåller typer.
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 börjar ge användbara varningar igen:
error TS2339: Property 'addTo' does not exist on type 'EasyBar'.
Detta är eftersom EasyBar är en underklass av L.Control att vi behöver ta in den definitionen i vår definitionsfil.
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 { }
}
}
Steg 4 - ange konstruktörsargument till EasyButton och EasyBar
Om du försöker skapa en ny EasyButton föreslår kodkomplettering att du bör skicka in ett L.ControlOptions-objekt för att konfigurera detta. Faktiskt behöver vi definiera våra egna alternativ.
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)
}
}
}
Saker ser bättre ut nu vid kodkomplettering:
Men jag fuskade med states-alternativet. Jag deklarerade det som any. I verkligheten borde det vara
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
}
Steg 5 - lägg till jsdoc-ledtrådar
Typescript kommer att ge användbara kommentarer till användare av detta plugin. Här är ett exempel på hur vi kan tillhandahålla dokumentation för 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;
Steg 6 Gör ändringar för att utöka definitioner av broschyrtyper
(Från och med Leaflet 1.2.0) Ta bort namnrymdeförklaringen:
declare namespace L {
och ersätt det med modulförstärkning:
import * as L from 'leaflet'
declare module 'leaflet' {
din testkod bör nu se ut så här:
import * as L from 'leaflet'
import 'easy-button'
Steg 7 integrera i de ursprungliga projektkällorna
Öppna node_modules\leaflet-easybutton\package.json och lägg till följande rad under style-posten:
"main": "src/easy-button.js",
"style": "src/easy-button.css",
"typings": "src/easy-button.d.ts",
Flytta vår easy-button.d.ts till node_modules/leaflet-easybutton/src, och testa att allt fortfarande fungerar.
Skicka sedan in en pull-begäran så att alla kan dra nytta av arbetet!
Svar #2
Det gör det faktiskt nu. Du borde bara
import * as L from 'leaflet';
import 'leaflet-easybutton/src/easy-button';

