diff --git a/d3-selection/d3-selection-tests.ts b/d3-selection/d3-selection-tests.ts index c8110dc8d2..c4f7f20963 100644 --- a/d3-selection/d3-selection-tests.ts +++ b/d3-selection/d3-selection-tests.ts @@ -77,7 +77,7 @@ let body: d3Selection.Selection = // with Group element of type BaseType and datum of type 'any' The parent element is of type 'null' with datum of type 'undefined' let baseTypeEl2: d3Selection.Selection = d3Selection.select(baseTypeEl.node()); -// let body2: d3Selection.Selection = d3Selection.select(baseTypeEl.node()); // Fails, group element types not of compatible for baseTypeEl +// let body2: d3Selection.Selection = d3Selection.select(baseTypeEl.node()); // fails as baseTypeEl.node() is of type cannot be assigned to HTMLElement let body3: d3Selection.Selection = d3Selection.select(body.node()); // element types match, but datum is of type 'any' as it cannot be inferred from .node() @@ -91,8 +91,8 @@ let body4: d3Selection.Selection = // TODO: The below are related to github issue #2 (BaseType choice) -// d3Selection.select(xDoc); // Note: Creates error because BaseType for selection is Element (-> e.g. classList property missing) -// d3Selection.select(xWindow); // Note: Creates error because BaseType for selection is Element (-> e.g. classList property missing) +d3Selection.select(xDoc); +// d3Selection.select(xWindow); // Window cannot does not match type BaseType = Element | EnterElement // test top-level selectAll() ------------------------------------------------------------- @@ -118,7 +118,7 @@ let divElements: d3Selection.Selection = d3Selection.selectAll(baseTypeElements.nodes()); -// let divElements2: d3Selection.Selection = d3Selection.selectAll(baseTypeElements.nodes()); // Fails, group elements types not of compatible for baseTypeElements +// let divElements2: d3Selection.Selection = d3Selection.selectAll(baseTypeElements.nodes()); // fails, group elements types not of compatible for baseTypeElements let divElements3: d3Selection.Selection = d3Selection.selectAll(divElements.nodes()); // element types match, but datum is of type 'any' as it cannot be inferred from .nodes() @@ -139,10 +139,7 @@ let circleSelection: d3Selection.Selection argument - -// NB: The below line compiles with target ES5, but not with target ES6!!! - -// let documentLinks: d3Selection.Selection = d3Selection.selectAll(document.links); // fails, as HTMLCollectionOf cannot be passed to NodeListOf +let documentLinks: d3Selection.Selection = d3Selection.selectAll(document.links); @@ -226,7 +223,7 @@ gElementsOldData = svgEl.selectAll(function () { return this.querySelectorAll('g'); // this of type SVGSVGElement by type inference }); -gElementsOldData = svgEl.selectAll(function (d, i, group) { // fails, because Datum type is not compatible as selectAll defaults to 'any', but gElementsOldData expects CircleDatum +gElementsOldData = svgEl.selectAll(function (d, i, group) { console.log('Get Element width using "this": ', this.width.baseVal.value); // 'this' type is SVGSVGElement console.log('Width in datum:', d.width); // type of d is SVGDatum if (group.length > 1) { @@ -236,7 +233,7 @@ gElementsOldData = svgEl.selectAll(function (d, i, gro }); // gElementsOldData = svgEl.selectAll(function() { // fails, because Datum type is not compatible as selectAll defaults to 'any', but gElementsOldData expects CircleDatum -// return this.querySelectorAll('g'); +// return this.querySelectorAll('g'); // }); // gElementsOldData = svgEl.selectAll(function() { // fails, return type HTMLAnchorElement is not compatible with SVGGElement expected by selectAll-typing @@ -551,9 +548,7 @@ circles2 = circles2 // returned update selection has the same type parameters as // TODO: Related to BaseType Choice issue -// let enterElements: d3Selection.Selection; ,fails as current BaseType is Element - -let enterElements: d3Selection.Selection; +let enterElements: d3Selection.Selection; enterElements = circles2.enter(); // enter selection @@ -585,7 +580,7 @@ exitCircles // exitCircles // .style('opacity', function (d) { // console.log('Circle Radius exit node: ', this.r.baseVal.value); -// return d.color === 'green' ? 1 : 0; // fails, as data type is defaulted to {}. If datum access is required, this should trigger the thought to type .exit<...> +// return d.color === 'green' ? 1 : 0; // fails, as data type is defaulted to {}. If datum access is required, this should trigger the thought to type .exit<...> // }); // MERGE ENTER + UPDATE ------------------------------------------------------------------ @@ -794,7 +789,7 @@ circles = circles.call(enforceMinRadius, 40); // check chaining return type by r // on(...) ------------------------------------------------------------------------------- -let listener: (this: HTMLBodyElement, datum: BodyDatum, index: number, group: Array | NodeListOf) => void; +let listener: (this: HTMLBodyElement, datum: BodyDatum, index: number, group: Array | ArrayLike) => void; // returns 'this' selection body = body.on('click', listener); // check chaining return type by re-assigning diff --git a/d3-selection/index.d.ts b/d3-selection/index.d.ts index a9e43db7cb..20c9ad190b 100644 --- a/d3-selection/index.d.ts +++ b/d3-selection/index.d.ts @@ -12,18 +12,23 @@ * without 'd3-selection' trying to use properties internally which would otherwise not * be supported. */ -export type BaseType = Element; - +export type BaseType = Element | EnterElement; +// export type BaseType = any; // Alternative, very permissive BaseType specification for edge cases +export interface ArrayLike { + length: number; + item(index: number): T; + [index: number]: T; +} // TODO: Review Use for enter() -export type EnterElement = { +export interface EnterElement { ownerDocument: Document; namespaceURI: string; appendChild(newChild: Node): Node; insertBefore(newChild: Node, refChild: Node): Node; querySelector(selectors: string): Element; - querySelectorAll(selectors: string): NodeListOf + querySelectorAll(selectors: string): NodeListOf; } /** @@ -65,9 +70,9 @@ export type CustomEventParameters = { export interface TransitionLike { selection(): Selection; on(type: string, listener: null): TransitionLike; - on(type: string, listener: (this: GElement, datum: Datum, index: number, group: Array | NodeListOf) => any): TransitionLike; + on(type: string, listener: (this: GElement, datum: Datum, index: number, group: Array | ArrayLike) => any): TransitionLike; tween(name: string, tweenFn: null): TransitionLike; - tween(name: string, tweenFn: (this: GElement, datum?: Datum, i?: number, group?: GElement[] | NodeListOf) => ((t: number) => void)): TransitionLike; + tween(name: string, tweenFn: (this: GElement, datum?: Datum, i?: number, group?: GElement[] | ArrayLike) => ((t: number) => void)): TransitionLike; } @@ -85,7 +90,7 @@ export function selectAll(): Selection; // _gr export function selectAll(selector: null): Selection; // _groups are set to empty array, first generic type is set to null by convention export function selectAll(selector: string): Selection; export function selectAll(nodes: GElement[]): Selection; -export function selectAll(nodes: NodeListOf): Selection; +export function selectAll(nodes: ArrayLike): Selection; @@ -95,50 +100,50 @@ interface Selection(selector: string): Selection; select(selector: null): Selection; // _groups are set to empty array, first generic type is set to null by convention - select(selector: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => DescElement): Selection; + select(selector: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => DescElement): Selection; selectAll(): Selection; // _groups are set to empty array, first generic type is set to null by convention selectAll(selector: null): Selection; // _groups are set to empty array, first generic type is set to null by convention selectAll(selector: string): Selection; - selectAll(selector: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => (Array | NodeListOf)): Selection; + selectAll(selector: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => (Array | ArrayLike)): Selection; // Modifying ------------------------------- attr(name: string): string; attr(name: string, value: null): Selection; attr(name: string, value: string | number | boolean): Selection; - attr(name: string, value: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => (string | number | boolean)): Selection; + attr(name: string, value: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => (string | number | boolean)): Selection; classed(name: string): boolean; classed(name: string, value: boolean): Selection; - classed(name: string, value: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => boolean): Selection; + classed(name: string, value: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => boolean): Selection; style(name: string): string; style(name: string, value: null): Selection; style(name: string, value: string | number | boolean, priority?: null | 'important'): Selection; - style(name: string, value: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => (string | number | boolean), priority?: null | 'important'): Selection; + style(name: string, value: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => (string | number | boolean), priority?: null | 'important'): Selection; property(name: string): any; - property(name: string, value: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => any): Selection; + property(name: string, value: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => any): Selection; property(name: string, value: null): Selection; property(name: string, value: any): Selection; text(): string; text(value: string | number | boolean): Selection; - text(value: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => (string | number | boolean)): Selection; + text(value: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => (string | number | boolean)): Selection; html(): string; html(value: string): Selection; - html(value: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => string): Selection; + html(value: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => string): Selection; append(type: string): Selection; - append(type: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => ChildElement): Selection; + append(type: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => ChildElement): Selection; insert(type: string, before: string): Selection; - insert(type: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => ChildElement, before: string): Selection; - insert(type: string, before: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => BaseType): Selection; - insert(type: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => ChildElement, - before: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => BaseType): Selection; + insert(type: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => ChildElement, before: string): Selection; + insert(type: string, before: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => BaseType): Selection; + insert(type: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => ChildElement, + before: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => BaseType): Selection; /** * Removes the selected elements from the document. @@ -149,7 +154,7 @@ interface Selection): Selection; filter(selector: string): Selection; - filter(selector: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => boolean): Selection; + filter(selector: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => boolean): Selection; @@ -166,23 +171,23 @@ interface Selection; - datum(value: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => NewDatum): Selection; + datum(value: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => NewDatum): Selection; datum(value: NewDatum): Selection; data(): Datum[]; data( data: Array, - key?: (this: GElement | PElement, datum?: Datum | NewDatum, index?: number, group?: Array | NodeListOf) => string + key?: (this: GElement | PElement, datum?: Datum | NewDatum, index?: number, group?: Array | ArrayLike) => string ): Selection; data( - data: (this: PElement, datum?: PDatum, index?: number, group?: Array | NodeListOf) => Array, - key?: (this: GElement | PElement, datum?: Datum | NewDatum, index?: number, group?: Array | NodeListOf) => string + data: (this: PElement, datum?: PDatum, index?: number, group?: Array | ArrayLike) => Array, + key?: (this: GElement | PElement, datum?: Datum | NewDatum, index?: number, group?: Array | ArrayLike) => string ): Selection; // TODO: Enter Selection returns GElements of type EnterNode, which do not meet the minimum interface of BaseType = Element // HACK: Keep enter() selection 'as-if' they are of type GElement, while overly permissive, this may be of little practical relevance, // given that the normal next step is an .append(...), which would address the matter - enter(): Selection; + enter(): Selection; // TODO: Review this: The type Datum on the exit items is actually of the type prior to calling data(...), as by definition, no new data of type NewDatum exists for these // elements. Due to the chaining, .data(...).exit(...), however, the definition would imply that the exit group elements have assumed the NewDatum type. @@ -191,17 +196,17 @@ interface Selection | NodeListOf) => void; + on(type: string): (this: GElement, datum: Datum, index: number, group: Array | ArrayLike) => void; on(type: string, listener: null): Selection; - on(type: string, listener: (this: GElement, datum: Datum, index: number, group: Array | NodeListOf) => void, capture?: boolean): Selection; + on(type: string, listener: (this: GElement, datum: Datum, index: number, group: Array | ArrayLike) => void, capture?: boolean): Selection; dispatch(type: string, parameters?: CustomEventParameters): Selection; - dispatch(type: string, parameters?: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => CustomEventParameters): Selection; + dispatch(type: string, parameters?: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => CustomEventParameters): Selection; // Control Flow ---------------------- - each(valueFn: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => void): Selection; + each(valueFn: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => void): Selection; call(func: (selection: Selection, ...args: any[]) => void, ...args: any[]): Selection; @@ -263,9 +268,9 @@ export function touches(container: ContainerElement, touches?: TouchList): Array export interface Local { - get(node: BaseType): any; - remove(node: BaseType): boolean; - set(node: BaseType, value: any): BaseType; + get(node: Element): any; + remove(node: Element): boolean; + set(node: Element, value: any): Element; /** * Obtain a string with the internally assigned property name for the local * which is used to store the value on a node @@ -343,7 +348,7 @@ export function window(DOMNode: Window | Document | Element): Window; * * @param elementName Name of the element to be added */ -export function creator(elementName: string): (this: BaseType) => NewGElement; +export function creator(elementName: string): (this: BaseType) => NewGElement; /** * Returns a closure structure which can be invoked in the 'this' context @@ -351,12 +356,12 @@ export function creator(elementName: string): (thi * * @param selector A valid selector string */ -export function matcher(selector: string): (this: BaseType) => boolean; +export function matcher(selector: string): (this: BaseType) => boolean; // ---------------------------------------------------------------------------- // selector.js and selectorAll.js related functions // ---------------------------------------------------------------------------- -export function selector(selector: string): (this: BaseType) => DescElement +export function selector(selector: string): (this: BaseType) => DescElement -export function selectorAll(selector: string): (this: BaseType) => NodeListOf; +export function selectorAll(selector: string): (this: BaseType) => NodeListOf; diff --git a/d3-transition/d3-transition-tests.ts b/d3-transition/d3-transition-tests.ts index b972488a27..e7531edf02 100644 --- a/d3-transition/d3-transition-tests.ts +++ b/d3-transition/d3-transition-tests.ts @@ -7,6 +7,7 @@ */ import { + ArrayLike, selection, select, selectAll, @@ -250,7 +251,7 @@ exitTransition = exitTransition.styleTween('fill', function (d, i, group) { return interpolateRgb(c, d.color); // datum type is CircleDatum }); -let tweenFnAccessor: (this: SVGCircleElement, datum?: CircleDatum, i?: number, group?: SVGCircleElement[] | NodeListOf) => ((t: number) => void); +let tweenFnAccessor: (this: SVGCircleElement, datum?: CircleDatum, i?: number, group?: SVGCircleElement[] | ArrayLike) => ((t: number) => void); // chainable @@ -286,7 +287,7 @@ exitTransition.remove(); // Test Event Handling // -------------------------------------------------------------------------- -let listener: (this: SVGCircleElement, datum: CircleDatum, index: number, group: Array | NodeListOf) => void; +let listener: (this: SVGCircleElement, datum: CircleDatum, index: number, group: Array | ArrayLike) => void; // returns 'this' transition enterTransition = enterTransition.on('end', listener); // check chaining return type by re-assigning @@ -309,7 +310,7 @@ enterTransition = enterTransition.on('end', null); // check chaining return type // each() ------------------------------------------------------------------------------- -// each(valueFn: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => void): Transition; +// each(valueFn: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => void): Transition; // returns 'this' transition enterTransition = enterTransition.each(function (d, i, group) { // check chaining return type by re-assigning diff --git a/d3-transition/index.d.ts b/d3-transition/index.d.ts index 0f12c56186..73fe85387d 100644 --- a/d3-transition/index.d.ts +++ b/d3-transition/index.d.ts @@ -3,7 +3,7 @@ // Definitions by: Alex Ford , Boris Yankov , Tom Wanzek // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -import { BaseType, Selection } from 'd3-selection'; +import { ArrayLike, BaseType, Selection } from 'd3-selection'; /** * Extend interface 'Selection' by declaration merging with 'd3-selection' @@ -26,14 +26,14 @@ export interface Transition(selector: string): Transition; - select(selector: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => DescElement): Transition; + select(selector: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => DescElement): Transition; // NB: while the empty selections (null or undefined selector) are defined on the underlying object, they should not be exposed in the type definition API // as they are meaningless on transitions.) // selectAll(): Transition; // _groups are set to empty array, first generic type is set to undefined by convention // selectAll(selector: null): Transition; // _groups are set to empty array, first generic type is set to undefined by convention selectAll(selector: string): Transition; - selectAll(selector: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => (Array | NodeListOf)): Transition; + selectAll(selector: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => (Array | ArrayLike)): Transition; selection(): Selection; transition(): Transition; @@ -42,38 +42,38 @@ export interface Transition; attr(name: string, value: string | number | boolean): Transition; - attr(name: string, value: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => (string | number | boolean)): Transition; - attrTween(name: string, tweenFn: (this: GElement, datum?: Datum, i?: number, group?: GElement[] | NodeListOf) => ((t: number) => (string | number | boolean))): Transition; + attr(name: string, value: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => (string | number | boolean)): Transition; + attrTween(name: string, tweenFn: (this: GElement, datum?: Datum, i?: number, group?: GElement[] | ArrayLike) => ((t: number) => (string | number | boolean))): Transition; style(name: string, value: null): Transition; style(name: string, value: string | number | boolean, priority?: null | 'important'): Transition; - style(name: string, value: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => (string | number | boolean), priority?: null | 'important'): Transition; - styleTween(name: string, tweenFn: (this: GElement, datum?: Datum, i?: number, group?: GElement[] | NodeListOf) => ((t: number) => (string | number | boolean)), priority?: null | 'important'): Transition; + style(name: string, value: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => (string | number | boolean), priority?: null | 'important'): Transition; + styleTween(name: string, tweenFn: (this: GElement, datum?: Datum, i?: number, group?: GElement[] | ArrayLike) => ((t: number) => (string | number | boolean)), priority?: null | 'important'): Transition; text(value: null): Transition; text(value: string | number | boolean): Transition; - text(value: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => (string | number | boolean)): Transition; + text(value: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => (string | number | boolean)): Transition; - tween(name: string): (this: GElement, datum?: Datum, i?: number, group?: GElement[] | NodeListOf) => ((t: number) => void); + tween(name: string): (this: GElement, datum?: Datum, i?: number, group?: GElement[] | ArrayLike) => ((t: number) => void); tween(name: string, tweenFn: null): Transition; - tween(name: string, tweenFn: (this: GElement, datum?: Datum, i?: number, group?: GElement[] | NodeListOf) => ((t: number) => void)): Transition; + tween(name: string, tweenFn: (this: GElement, datum?: Datum, i?: number, group?: GElement[] | ArrayLike) => ((t: number) => void)): Transition; remove(): Transition; merge(other: Transition): Transition; filter(filter: string): Transition; - filter(filter: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => boolean): Transition; + filter(filter: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => boolean): Transition; // Event Handling ------------------- - on(type: string): (this: GElement, datum: Datum, index: number, group: Array | NodeListOf) => any; + on(type: string): (this: GElement, datum: Datum, index: number, group: Array | ArrayLike) => any; on(type: string, listener: null): Transition; - on(type: string, listener: (this: GElement, datum: Datum, index: number, group: Array | NodeListOf) => any): Transition; + on(type: string, listener: (this: GElement, datum: Datum, index: number, group: Array | ArrayLike) => any): Transition; // Control Flow ---------------------- - each(valueFn: (this: GElement, datum?: Datum, index?: number, group?: Array | NodeListOf) => void): Transition; + each(valueFn: (this: GElement, datum?: Datum, index?: number, group?: Array | ArrayLike) => void): Transition; call(func: (transition: Transition, ...args: any[]) => any, ...args: any[]): Transition;