Внедрение шаблона D3 "многоразовая диаграмма" в TypeScript
Код в разделе 2 ниже (рабочий пример здесь) основан на коде в разделе 1, но изменен на использование функций стрелок, и он основан на шаблоне Майка Бостока в " На пути к восстанавливаемым диаграммам", а именно, возвращает функцию, которая имеет другие функции на ней.
Если я попытаюсь запустить либо код в разделе 1 или 2 в машинописном тексте (здесь здесь), он говорит, что методы addToChart
и stop
не существуют в типе (selection: any) =>() => void
.
Как я могу получить машинописный текст для распознавания свойств функций (addToChart
и stop
в этом случае), добавленных к возвращенной функции?
секция 1
const mychart = function (){
let stop = false;
const chart = function(selection){
function tick(){
console.log("tick");
}
return tick;
};
// Adding a function to the returned
// function as in Bostock reusable chart pattern
chart.addToChart = function(value){
console.log("addToChart");
return chart;
};
chart.stop = function(){
return stop = true;
}
return chart;
}
const a = mychart();
const tick = a();
tick(); //logs tick
a.addToChart(); //logs "addToChart"
раздел 2
const mychart = () => {
let stop = false;
const chart = (selection) => {
function tick(){
console.log("tick");
}
return tick;
};
chart.addToChart = (value) => {
console.log("addToChart");
return chart;
};
chart.stop = () => {
return stop = true;
}
return chart;
}
const a = mychart();
const tick = a();
tick(); //logs tick
a.addToChart(); //logs "addToChart"
Ответы
Ответ 1
Вы можете определить гибридный тип, то есть интерфейс, описывающий как сигнатуру функции, так и ее свойства. Учитывая ваш код, это может быть примерно так:
interface IChart {
(selection: any): any;
// Use overloading for D3 getter/setter pattern
addToChart(): string; // Getter
addToChart(value: string): IChart; // Setter
}
Так как вам следует избегать any
чумы, это может потребовать некоторой дополнительной доработки, но этого должно быть достаточно, чтобы вы начали. Кроме того, чтобы получить шаблон D3-ish getter/setter, вы можете перегрузить функцию addToChart
в объявлении интерфейса.
Интеграция этого интерфейса как типа в шаблоне повторного использования кода становится довольно простой:
const mychart = (): IChart => {
// Private value exposed via closure
let value: string|undefined;
const chart = <IChart>((selection) => {
// Private logic
});
// Public interface
// Implementing a D3-style getter/setter.
chart.addToChart = function(val?: string): any {
return arguments.length ? (value = val, chart) : value;
};
return chart;
}
const chart = mychart();
console.log(chart.addToChart()) // --> undefined
chart.addToChart("Add"); // Sets private value to "Add".
console.log(chart.addToChart()) // --> "Add"
Взгляните на {
//Private value exposed via closure
let value: string|undefined;
const chart = ((selection) => {
//Private logic
});
//Public interface
//Implementing a D3-style getter/setter.
chart.addToChart = function(val?: string): any {
return arguments.length? (value = val, chart): value;
};
return chart;
}
const chart = mychart();
console.log(chart.addToChart()) //%20--> undefined
chart.addToChart("Add"); //Sets private value to "Add".
console.log(chart.addToChart()) //%20--> "Add" rel=noreferrer>демонстрационную версию исполняемой {
//Private value exposed via closure
let value: string|undefined;
const chart = ((selection) => {
//Private logic
});
//Public interface
//Implementing a D3-style getter/setter.
chart.addToChart = function(val?: string): any {
return arguments.length? (value = val, chart): value;
};
return chart;
}
const chart = mychart();
console.log(chart.addToChart()) //%20--> undefined
chart.addToChart("Add"); //Sets private value to "Add".
console.log(chart.addToChart()) //%20--> "Add" rel=noreferrer>игровой площадки.
Ответ 2
Мне было интересно, можете ли вы использовать интерфейс/класс:
interface IChart {
constructor: Function;
addToChart?: (number) => Chart;
stop: () => boolean;
}
class Chart implements IChart {
private _stop = false;
constructor( selection ) {
// content of tick funciton here
}
public addToChart = function (n: number) {
return this;
}
public stop = function () {
return this._stop = true;
}
}
let mychart = function () {
let stop = false;
let chartNew: Chart = new Chart(1);
return chartNew;
};
Ответ 3
Вы можете использовать Object.assign
для создания гибридного типа (функция, которая имеет дополнительные свойства), без необходимости определять выделенный интерфейс. Вы можете определить функции внутри оригинала отдельно, так что вы можете иметь несколько подписей для каждой функции, и вы даже можете ввести this
параметр, если хотите получить доступ к объекту через this
вместо chart
let mychart = function () {
let isStopped = false;
let value = "";
type Chart = typeof chart;
// Complex method with multiple signatures
function addToChart(): string
function addToChart(newValue: string): Chart
function addToChart(newValue?: string): string | Chart {
if(newValue != undefined){
value = newValue;
chart.stop()
return chart;
}else{
return value;
}
}
// We can specify the type for this if we want to use this
function stop(this: Chart) {
isStopped = true;
return this; // instead of chart, either is usable
}
var methods = {
addToChart,
stop,
// inline function, we can return chart, but if we reference the Chart type explicitly the compiler explodes
stop2() {
isStopped = true;
return chart;
}
};
let chart = Object.assign(function (selection) {
function tick() {
}
return tick;
}, methods);
return chart;
};
let d = mychart();
d("");
d.addToChart("").addToChart();
d.addToChart();
d.stop();
d.stop().addToChart("").stop2().stop()
Заметки
-
Хотя intelisense работает так, как ожидалось, если вы наведите курсор мыши на d
и посмотрите на тип, он значительно уродливее, чем ручная версия.
-
Я определил methods
отдельно и не Object.assign
потому что компилятор запутался, если я это сделаю.
-
Если вы не хотите использовать this
внутри методов, вам не нужно вводить this
явно. Я показал, как использовать его, просто для полноты картины, используя график может быть проще и это гарантирует, что мы не имеем дело с кем - то проходя неправильно this
.
-
Хотя приведенный выше пример работает, есть определенные случаи, когда компилятор отказывается от вывода и mychart
возврат mychart
как любой. Один из таких случаев - это когда мы ссылаемся на Chart
внутри функции, определенной в объекте, назначенном methods