This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a commit to branch typescript
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git


The following commit(s) were added to refs/heads/typescript by this push:
     new ab16b5a  ts: add types for Graph and Tree data structure
ab16b5a is described below

commit ab16b5ae8d0b4bdb7633dfb1a2011650c24ebdaf
Author: pissang <[email protected]>
AuthorDate: Wed Feb 19 22:47:56 2020 +0800

    ts: add types for Graph and Tree data structure
---
 src/data/Graph.ts                   | 706 ++++++++++++++++--------------------
 src/data/Tree.ts                    | 490 +++++++++++--------------
 src/data/helper/createDimensions.ts |   3 +-
 src/model/Model.ts                  |   4 +-
 4 files changed, 532 insertions(+), 671 deletions(-)

diff --git a/src/data/Graph.ts b/src/data/Graph.ts
index b963d1b..c78536c 100644
--- a/src/data/Graph.ts
+++ b/src/data/Graph.ts
@@ -17,522 +17,460 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import {__DEV__} from '../config';
 import * as zrUtil from 'zrender/src/core/util';
-import {enableClassCheck} from '../util/clazz';
+import { Dictionary } from 'zrender/src/core/types';
+import List from './List';
+import Model from '../model/Model';
+import Element from 'zrender/src/Element';
+import { DimensionLoose, ParsedDataValue } from '../util/types';
 
 // id may be function name of Object, add a prefix to avoid this problem.
-function generateNodeKey(id) {
+function generateNodeKey(id: string): string {
     return '_EC_' + id;
 }
-/**
- * @alias module:echarts/data/Graph
- * @constructor
- * @param {boolean} directed
- */
-var Graph = function (directed) {
+
+class Graph {
+    type: 'graph' = 'graph'
+
+    readonly nodes: GraphNode[] = [];
+
+    readonly edges: GraphEdge[] = [];
+
+    data: List
+
+    edgeData: List
+
     /**
      * 是否是有向图
-     * @type {boolean}
-     * @private
      */
-    this._directed = directed || false;
+    private _directed: boolean
 
+    private _nodesMap: Dictionary<GraphNode> = {};
     /**
-     * @type {Array.<module:echarts/data/Graph.Node>}
-     * @readOnly
+     * @type {Object.<string, module:echarts/data/Graph.Edge>}
+     * @private
      */
-    this.nodes = [];
+    private _edgesMap: Dictionary<GraphEdge> = {};
+
+
+    constructor(directed?: boolean) {
+        this._directed = directed || false;
+    }
 
     /**
-     * @type {Array.<module:echarts/data/Graph.Edge>}
-     * @readOnly
+     * If is directed graph
      */
-    this.edges = [];
+    isDirected(): boolean {
+        return this._directed;
+    };
 
     /**
-     * @type {Object.<string, module:echarts/data/Graph.Node>}
-     * @private
+     * Add a new node
      */
-    this._nodesMap = {};
+    addNode(id: string, dataIndex?: number): GraphNode {
+        id = id == null ? ('' + dataIndex) : ('' + id);
+
+        var nodesMap = this._nodesMap;
+
+        if (nodesMap[generateNodeKey(id)]) {
+            if (__DEV__) {
+                console.error('Graph nodes have duplicate name or id');
+            }
+            return;
+        }
+
+        var node = new GraphNode(id, dataIndex);
+        node.hostGraph = this;
+
+        this.nodes.push(node);
+
+        nodesMap[generateNodeKey(id)] = node;
+        return node;
+    };
+
     /**
-     * @type {Object.<string, module:echarts/data/Graph.Edge>}
-     * @private
+     * Get node by data index
      */
-    this._edgesMap = {};
-
+    getNodeByIndex(dataIndex: number): GraphNode {
+        var rawIdx = this.data.getRawIndex(dataIndex);
+        return this.nodes[rawIdx];
+    };
     /**
-     * @type {module:echarts/data/List}
-     * @readOnly
+     * Get node by id
      */
-    this.data;
+    getNodeById(id: string): GraphNode {
+        return this._nodesMap[generateNodeKey(id)];
+    };
 
     /**
-     * @type {module:echarts/data/List}
-     * @readOnly
+     * Add a new edge
      */
-    this.edgeData;
-};
-
-var graphProto = Graph.prototype;
-/**
- * @type {string}
- */
-graphProto.type = 'graph';
-
-/**
- * If is directed graph
- * @return {boolean}
- */
-graphProto.isDirected = function () {
-    return this._directed;
-};
-
-/**
- * Add a new node
- * @param {string} id
- * @param {number} [dataIndex]
- */
-graphProto.addNode = function (id, dataIndex) {
-    id = id == null ? ('' + dataIndex) : ('' + id);
+    addEdge(n1: GraphNode | number | string, n2: GraphNode | number | string, 
dataIndex?: number) {
+        var nodesMap = this._nodesMap;
+        var edgesMap = this._edgesMap;
 
-    var nodesMap = this._nodesMap;
-
-    if (nodesMap[generateNodeKey(id)]) {
-        if (__DEV__) {
-            console.error('Graph nodes have duplicate name or id');
+        // PNEDING
+        if (typeof n1 === 'number') {
+            n1 = this.nodes[n1];
+        }
+        if (typeof n2 === 'number') {
+            n2 = this.nodes[n2];
         }
-        return;
-    }
 
-    var node = new Node(id, dataIndex);
-    node.hostGraph = this;
+        if (!(n1 instanceof GraphNode)) {
+            n1 = nodesMap[generateNodeKey(n1)];
+        }
+        if (!(n2 instanceof GraphNode)) {
+            n2 = nodesMap[generateNodeKey(n2)];
+        }
+        if (!n1 || !n2) {
+            return;
+        }
 
-    this.nodes.push(node);
+        var key = n1.id + '-' + n2.id;
+        // PENDING
+        if (edgesMap[key]) {
+            return;
+        }
 
-    nodesMap[generateNodeKey(id)] = node;
-    return node;
-};
+        var edge = new GraphEdge(n1, n2, dataIndex);
+        edge.hostGraph = this;
 
-/**
- * Get node by data index
- * @param  {number} dataIndex
- * @return {module:echarts/data/Graph~Node}
- */
-graphProto.getNodeByIndex = function (dataIndex) {
-    var rawIdx = this.data.getRawIndex(dataIndex);
-    return this.nodes[rawIdx];
-};
-/**
- * Get node by id
- * @param  {string} id
- * @return {module:echarts/data/Graph.Node}
- */
-graphProto.getNodeById = function (id) {
-    return this._nodesMap[generateNodeKey(id)];
-};
+        if (this._directed) {
+            n1.outEdges.push(edge);
+            n2.inEdges.push(edge);
+        }
+        n1.edges.push(edge);
+        if (n1 !== n2) {
+            n2.edges.push(edge);
+        }
 
-/**
- * Add a new edge
- * @param {number|string|module:echarts/data/Graph.Node} n1
- * @param {number|string|module:echarts/data/Graph.Node} n2
- * @param {number} [dataIndex=-1]
- * @return {module:echarts/data/Graph.Edge}
- */
-graphProto.addEdge = function (n1, n2, dataIndex) {
-    var nodesMap = this._nodesMap;
-    var edgesMap = this._edgesMap;
-
-    // PNEDING
-    if (typeof n1 === 'number') {
-        n1 = this.nodes[n1];
-    }
-    if (typeof n2 === 'number') {
-        n2 = this.nodes[n2];
-    }
+        this.edges.push(edge);
+        edgesMap[key] = edge;
 
-    if (!Node.isInstance(n1)) {
-        n1 = nodesMap[generateNodeKey(n1)];
-    }
-    if (!Node.isInstance(n2)) {
-        n2 = nodesMap[generateNodeKey(n2)];
-    }
-    if (!n1 || !n2) {
-        return;
-    }
+        return edge;
+    };
 
-    var key = n1.id + '-' + n2.id;
-    // PENDING
-    if (edgesMap[key]) {
-        return;
-    }
+    /**
+     * Get edge by data index
+     */
+    getEdgeByIndex(dataIndex: number): GraphEdge {
+        var rawIdx = this.edgeData.getRawIndex(dataIndex);
+        return this.edges[rawIdx];
+    };
+    /**
+     * Get edge by two linked nodes
+     */
+    getEdge(n1: string | GraphNode, n2: string | GraphNode): GraphEdge {
+        if (n1 instanceof GraphNode) {
+            n1 = n1.id;
+        }
+        if (n2 instanceof GraphNode) {
+            n2 = n2.id;
+        }
 
-    var edge = new Edge(n1, n2, dataIndex);
-    edge.hostGraph = this;
+        var edgesMap = this._edgesMap;
 
-    if (this._directed) {
-        n1.outEdges.push(edge);
-        n2.inEdges.push(edge);
-    }
-    n1.edges.push(edge);
-    if (n1 !== n2) {
-        n2.edges.push(edge);
-    }
+        if (this._directed) {
+            return edgesMap[n1 + '-' + n2];
+        }
+        else {
+            return edgesMap[n1 + '-' + n2]
+                || edgesMap[n2 + '-' + n1];
+        }
+    };
 
-    this.edges.push(edge);
-    edgesMap[key] = edge;
+    /**
+     * Iterate all nodes
+     */
+    eachNode<Ctx>(
+        cb: (this: Ctx, node: GraphNode, idx: number) => void,
+        context?: Ctx
+    ) {
+        var nodes = this.nodes;
+        var len = nodes.length;
+        for (var i = 0; i < len; i++) {
+            if (nodes[i].dataIndex >= 0) {
+                cb.call(context, nodes[i], i);
+            }
+        }
+    };
 
-    return edge;
-};
+    /**
+     * Iterate all edges
+     */
+    eachEdge<Ctx>(
+        cb: (this: Ctx, edge: GraphEdge, idx: number) => void,
+        context?: Ctx
+    ) {
+        var edges = this.edges;
+        var len = edges.length;
+        for (var i = 0; i < len; i++) {
+            if (edges[i].dataIndex >= 0
+                && edges[i].node1.dataIndex >= 0
+                && edges[i].node2.dataIndex >= 0
+            ) {
+                cb.call(context, edges[i], i);
+            }
+        }
+    };
 
-/**
- * Get edge by data index
- * @param  {number} dataIndex
- * @return {module:echarts/data/Graph~Node}
- */
-graphProto.getEdgeByIndex = function (dataIndex) {
-    var rawIdx = this.edgeData.getRawIndex(dataIndex);
-    return this.edges[rawIdx];
-};
-/**
- * Get edge by two linked nodes
- * @param  {module:echarts/data/Graph.Node|string} n1
- * @param  {module:echarts/data/Graph.Node|string} n2
- * @return {module:echarts/data/Graph.Edge}
- */
-graphProto.getEdge = function (n1, n2) {
-    if (Node.isInstance(n1)) {
-        n1 = n1.id;
-    }
-    if (Node.isInstance(n2)) {
-        n2 = n2.id;
-    }
+    /**
+     * Breadth first traverse
+     * Return true to stop traversing
+     */
+    breadthFirstTraverse<Ctx>(
+        cb: (this: Ctx, node: GraphNode, fromNode: GraphNode) => boolean,
+        startNode: GraphNode | string,
+        direction: 'none' | 'in' | 'out',
+        context?: Ctx
+    ) {
+        if (!(startNode instanceof GraphNode)) {
+            startNode = this._nodesMap[generateNodeKey(startNode)];
+        }
+        if (!startNode) {
+            return;
+        }
 
-    var edgesMap = this._edgesMap;
+        var edgeType: 'inEdges' | 'outEdges' | 'edges' = direction === 'out'
+            ? 'outEdges' : (direction === 'in' ? 'inEdges' : 'edges');
 
-    if (this._directed) {
-        return edgesMap[n1 + '-' + n2];
-    }
-    else {
-        return edgesMap[n1 + '-' + n2]
-            || edgesMap[n2 + '-' + n1];
-    }
-};
+        for (var i = 0; i < this.nodes.length; i++) {
+            this.nodes[i].__visited = false;
+        }
 
-/**
- * Iterate all nodes
- * @param  {Function} cb
- * @param  {*} [context]
- */
-graphProto.eachNode = function (cb, context) {
-    var nodes = this.nodes;
-    var len = nodes.length;
-    for (var i = 0; i < len; i++) {
-        if (nodes[i].dataIndex >= 0) {
-            cb.call(context, nodes[i], i);
+        if (cb.call(context, startNode, null)) {
+            return;
         }
-    }
-};
 
-/**
- * Iterate all edges
- * @param  {Function} cb
- * @param  {*} [context]
- */
-graphProto.eachEdge = function (cb, context) {
-    var edges = this.edges;
-    var len = edges.length;
-    for (var i = 0; i < len; i++) {
-        if (edges[i].dataIndex >= 0
-            && edges[i].node1.dataIndex >= 0
-            && edges[i].node2.dataIndex >= 0
-        ) {
-            cb.call(context, edges[i], i);
+        var queue = [startNode];
+        while (queue.length) {
+            var currentNode = queue.shift();
+            var edges = currentNode[edgeType];
+
+            for (var i = 0; i < edges.length; i++) {
+                var e = edges[i];
+                var otherNode = e.node1 === currentNode
+                    ? e.node2 : e.node1;
+                if (!otherNode.__visited) {
+                    if (cb.call(context, otherNode, currentNode)) {
+                        // Stop traversing
+                        return;
+                    }
+                    queue.push(otherNode);
+                    otherNode.__visited = true;
+                }
+            }
         }
-    }
-};
+    };
 
-/**
- * Breadth first traverse
- * @param {Function} cb
- * @param {module:echarts/data/Graph.Node} startNode
- * @param {string} [direction='none'] 'none'|'in'|'out'
- * @param {*} [context]
- */
-graphProto.breadthFirstTraverse = function (
-    cb, startNode, direction, context
-) {
-    if (!Node.isInstance(startNode)) {
-        startNode = this._nodesMap[generateNodeKey(startNode)];
-    }
-    if (!startNode) {
-        return;
-    }
+    // TODO
+    // depthFirstTraverse(
+    //     cb, startNode, direction, context
+    // ) {
 
-    var edgeType = direction === 'out'
-        ? 'outEdges' : (direction === 'in' ? 'inEdges' : 'edges');
+    // };
 
-    for (var i = 0; i < this.nodes.length; i++) {
-        this.nodes[i].__visited = false;
-    }
+    // Filter update
+    update() {
+        var data = this.data;
+        var edgeData = this.edgeData;
+        var nodes = this.nodes;
+        var edges = this.edges;
 
-    if (cb.call(context, startNode, null)) {
-        return;
-    }
+        for (var i = 0, len = nodes.length; i < len; i++) {
+            nodes[i].dataIndex = -1;
+        }
+        for (var i = 0, len = data.count(); i < len; i++) {
+            nodes[data.getRawIndex(i)].dataIndex = i;
+        }
 
-    var queue = [startNode];
-    while (queue.length) {
-        var currentNode = queue.shift();
-        var edges = currentNode[edgeType];
+        edgeData.filterSelf(function (idx) {
+            var edge = edges[edgeData.getRawIndex(idx)];
+            return edge.node1.dataIndex >= 0 && edge.node2.dataIndex >= 0;
+        });
 
+        // Update edge
+        for (var i = 0, len = edges.length; i < len; i++) {
+            edges[i].dataIndex = -1;
+        }
+        for (var i = 0, len = edgeData.count(); i < len; i++) {
+            edges[edgeData.getRawIndex(i)].dataIndex = i;
+        }
+    };
+
+    /**
+     * @return {module:echarts/data/Graph}
+     */
+    clone() {
+        var graph = new Graph(this._directed);
+        var nodes = this.nodes;
+        var edges = this.edges;
+        for (var i = 0; i < nodes.length; i++) {
+            graph.addNode(nodes[i].id, nodes[i].dataIndex);
+        }
         for (var i = 0; i < edges.length; i++) {
             var e = edges[i];
-            var otherNode = e.node1 === currentNode
-                ? e.node2 : e.node1;
-            if (!otherNode.__visited) {
-                if (cb.call(context, otherNode, currentNode)) {
-                    // Stop traversing
-                    return;
-                }
-                queue.push(otherNode);
-                otherNode.__visited = true;
-            }
+            graph.addEdge(e.node1.id, e.node2.id, e.dataIndex);
         }
-    }
-};
+        return graph;
+    };
 
-// TODO
-// graphProto.depthFirstTraverse = function (
-//     cb, startNode, direction, context
-// ) {
+    static Node: typeof GraphNode
+    static Edge: typeof GraphEdge
 
-// };
+}
 
-// Filter update
-graphProto.update = function () {
-    var data = this.data;
-    var edgeData = this.edgeData;
-    var nodes = this.nodes;
-    var edges = this.edges;
 
-    for (var i = 0, len = nodes.length; i < len; i++) {
-        nodes[i].dataIndex = -1;
-    }
-    for (var i = 0, len = data.count(); i < len; i++) {
-        nodes[data.getRawIndex(i)].dataIndex = i;
-    }
+class GraphNode {
 
-    edgeData.filterSelf(function (idx) {
-        var edge = edges[edgeData.getRawIndex(idx)];
-        return edge.node1.dataIndex >= 0 && edge.node2.dataIndex >= 0;
-    });
+    id: string
 
-    // Update edge
-    for (var i = 0, len = edges.length; i < len; i++) {
-        edges[i].dataIndex = -1;
-    }
-    for (var i = 0, len = edgeData.count(); i < len; i++) {
-        edges[edgeData.getRawIndex(i)].dataIndex = i;
-    }
-};
+    inEdges: GraphEdge[] = []
 
-/**
- * @return {module:echarts/data/Graph}
- */
-graphProto.clone = function () {
-    var graph = new Graph(this._directed);
-    var nodes = this.nodes;
-    var edges = this.edges;
-    for (var i = 0; i < nodes.length; i++) {
-        graph.addNode(nodes[i].id, nodes[i].dataIndex);
-    }
-    for (var i = 0; i < edges.length; i++) {
-        var e = edges[i];
-        graph.addEdge(e.node1.id, e.node2.id, e.dataIndex);
-    }
-    return graph;
-};
+    outEdges: GraphEdge[] = []
 
+    edges: GraphEdge[] = [];
 
-/**
- * @alias module:echarts/data/Graph.Node
- */
-function Node(id, dataIndex) {
-    /**
-    * @type {string}
-    */
-    this.id = id == null ? '' : id;
+    hostGraph: Graph
 
-    /**
-    * @type {Array.<module:echarts/data/Graph.Edge>}
-    */
-    this.inEdges = [];
-    /**
-    * @type {Array.<module:echarts/data/Graph.Edge>}
-    */
-    this.outEdges = [];
-    /**
-    * @type {Array.<module:echarts/data/Graph.Edge>}
-    */
-    this.edges = [];
-    /**
-     * @type {module:echarts/data/Graph}
-     */
-    this.hostGraph;
+    dataIndex: number = -1
 
-    /**
-     * @type {number}
-     */
-    this.dataIndex = dataIndex == null ? -1 : dataIndex;
-}
+    // Used in traverse of Graph
+    __visited: boolean
+
+    constructor(id?: string, dataIndex?: number) {
+        this.id = id == null ? '' : id;
 
-Node.prototype = {
+        this.inEdges = [];
 
-    constructor: Node,
+        this.outEdges = [];
+
+        this.edges = [];
+
+        this.hostGraph;
+
+        this.dataIndex = dataIndex == null ? -1 : dataIndex;
+    }
 
     /**
      * @return {number}
      */
-    degree: function () {
+    degree() {
         return this.edges.length;
-    },
+    }
 
     /**
      * @return {number}
      */
-    inDegree: function () {
+    inDegree() {
         return this.inEdges.length;
-    },
+    }
 
     /**
     * @return {number}
     */
-    outDegree: function () {
+    outDegree() {
         return this.outEdges.length;
-    },
+    }
 
-    /**
-     * @param {string} [path]
-     * @return {module:echarts/model/Model}
-     */
-    getModel: function (path) {
+    // TODO: TYPE Same type with Model#getModel
+    getModel(path: string | string[]): Model {
         if (this.dataIndex < 0) {
             return;
         }
         var graph = this.hostGraph;
         var itemModel = graph.data.getItemModel(this.dataIndex);
 
-        return itemModel.getModel(path);
+        return itemModel.getModel(path as [string]);
     }
-};
+}
 
-/**
- * 图边
- * @alias module:echarts/data/Graph.Edge
- * @param {module:echarts/data/Graph.Node} n1
- * @param {module:echarts/data/Graph.Node} n2
- * @param {number} [dataIndex=-1]
- */
-function Edge(n1, n2, dataIndex) {
 
+class GraphEdge {
     /**
      * 节点1,如果是有向图则为源节点
-     * @type {module:echarts/data/Graph.Node}
      */
-    this.node1 = n1;
-
+    node1: GraphNode
     /**
      * 节点2,如果是有向图则为目标节点
-     * @type {module:echarts/data/Graph.Node}
      */
-    this.node2 = n2;
+    node2: GraphNode
 
-    this.dataIndex = dataIndex == null ? -1 : dataIndex;
-}
+    dataIndex: number = -1
+
+    hostGraph: Graph
 
-/**
- * @param {string} [path]
- * @return {module:echarts/model/Model}
- */
-Edge.prototype.getModel = function (path) {
-    if (this.dataIndex < 0) {
-        return;
+    constructor(n1: GraphNode, n2: GraphNode, dataIndex?: number) {
+        this.node1 = n1;
+        this.node2 = n2;
+        this.dataIndex = dataIndex == null ? -1 : dataIndex;
     }
-    var graph = this.hostGraph;
-    var itemModel = graph.edgeData.getItemModel(this.dataIndex);
 
-    return itemModel.getModel(path);
-};
+    getModel(path: string | string[]): Model {
+        if (this.dataIndex < 0) {
+            return;
+        }
+        var graph = this.hostGraph;
+        var itemModel = graph.edgeData.getItemModel(this.dataIndex);
+
+        return itemModel.getModel(path as [string]);
+    }
+}
 
-var createGraphDataProxyMixin = function (hostName, dataName) {
+type GetDataName<Host> = Host extends GraphEdge ? 'edgeData' : 'data';
+
+function createGraphDataProxyMixin<Host extends GraphEdge | GraphNode>(
+    hostName: 'hostGraph',
+    dataName: GetDataName<Host>
+) {
     return {
         /**
-         * @param {string=} [dimension='value'] Default 'value'. can be 'a', 
'b', 'c', 'd', 'e'.
-         * @return {number}
+         * @param Default 'value'. can be 'a', 'b', 'c', 'd', 'e'.
          */
-        getValue: function (dimension) {
+        getValue: function (this: Host, dimension: DimensionLoose): 
ParsedDataValue {
             var data = this[hostName][dataName];
             return data.get(data.getDimension(dimension || 'value'), 
this.dataIndex);
         },
 
-        /**
-         * @param {Object|string} key
-         * @param {*} [value]
-         */
-        setVisual: function (key, value) {
+        setVisual: function (this: Host, key: string | Dictionary<any>, 
value?: any) {
             this.dataIndex >= 0
-                && this[hostName][dataName].setItemVisual(this.dataIndex, key, 
value);
+                && this[hostName][dataName].setItemVisual(this.dataIndex, key 
as string, value);
         },
 
-        /**
-         * @param {string} key
-         * @return {boolean}
-         */
-        getVisual: function (key, ignoreParent) {
+        getVisual: function (this: Host, key: string, ignoreParent?: boolean) {
             return this[hostName][dataName].getItemVisual(this.dataIndex, key, 
ignoreParent);
         },
 
-        /**
-         * @param {Object} layout
-         * @return {boolean} [merge=false]
-         */
-        setLayout: function (layout, merge) {
+        setLayout: function (this: Host, layout: any, merge?: boolean) {
             this.dataIndex >= 0
                 && this[hostName][dataName].setItemLayout(this.dataIndex, 
layout, merge);
         },
 
-        /**
-         * @return {Object}
-         */
-        getLayout: function () {
+        getLayout: function (this: Host) {
             return this[hostName][dataName].getItemLayout(this.dataIndex);
         },
 
-        /**
-         * @return {module:zrender/Element}
-         */
-        getGraphicEl: function () {
+        getGraphicEl: function (this: Host): Element {
             return this[hostName][dataName].getItemGraphicEl(this.dataIndex);
         },
 
-        /**
-         * @return {number}
-         */
-        getRawIndex: function () {
+        getRawIndex: function (this: Host) {
             return this[hostName][dataName].getRawIndex(this.dataIndex);
         }
     };
 };
 
-zrUtil.mixin(Node, createGraphDataProxyMixin('hostGraph', 'data'));
-zrUtil.mixin(Edge, createGraphDataProxyMixin('hostGraph', 'edgeData'));
 
-Graph.Node = Node;
-Graph.Edge = Edge;
+interface GraphEdge extends ReturnType<typeof createGraphDataProxyMixin> {};
+interface GraphNode extends ReturnType<typeof createGraphDataProxyMixin> {};
+
+zrUtil.mixin(GraphEdge, createGraphDataProxyMixin('hostGraph', 'data'));
+zrUtil.mixin(GraphNode, createGraphDataProxyMixin('hostGraph', 'edgeData'));
+
+Graph.Node = GraphNode;
+Graph.Edge = GraphEdge;
 
-enableClassCheck(Node);
-enableClassCheck(Edge);
 
 export default Graph;
diff --git a/src/data/Tree.ts b/src/data/Tree.ts
index 61f24cb..1726a5c 100644
--- a/src/data/Tree.ts
+++ b/src/data/Tree.ts
@@ -17,12 +17,8 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 /**
  * Tree data structure
- *
- * @module echarts/data/Tree
  */
 
 import * as zrUtil from 'zrender/src/core/util';
@@ -30,81 +26,58 @@ import Model from '../model/Model';
 import linkList from './helper/linkList';
 import List from './List';
 import createDimensions from './helper/createDimensions';
+import { DimensionLoose, ParsedDataValue } from '../util/types';
+import { Dictionary } from 'zrender/src/core/types';
+
+type TreeTraverseOrder = 'preorder' | 'postorder';
+type TreeTraverseCallback<Ctx> = (this: Ctx, node: TreeNode) => boolean;
+type TreeTraverseOption = {
+    order?: TreeTraverseOrder
+    attr?: 'children' | 'viewChildren'
+};
 
-/**
- * @constructor module:echarts/data/Tree~TreeNode
- * @param {string} name
- * @param {module:echarts/data/Tree} hostTree
- */
-var TreeNode = function (name, hostTree) {
-    /**
-     * @type {string}
-     */
-    this.name = name || '';
+interface TreeNodeData {
+    name?: string
+    value?: any
+    children?: TreeNodeData[]
+}
 
-    /**
-     * Depth of node
-     *
-     * @type {number}
-     * @readOnly
-     */
-    this.depth = 0;
+class TreeNode {
+    name: string
 
-    /**
-     * Height of the subtree rooted at this node.
-     * @type {number}
-     * @readOnly
-     */
-    this.height = 0;
+    depth: number = 0
 
-    /**
-     * @type {module:echarts/data/Tree~TreeNode}
-     * @readOnly
-     */
-    this.parentNode = null;
+    height: number = 0
 
+    parentNode: TreeNode
     /**
      * Reference to list item.
      * Do not persistent dataIndex outside,
      * besause it may be changed by list.
      * If dataIndex -1,
      * this node is logical deleted (filtered) in list.
-     *
-     * @type {Object}
-     * @readOnly
      */
-    this.dataIndex = -1;
+    dataIndex: number = -1
 
-    /**
-     * @type {Array.<module:echarts/data/Tree~TreeNode>}
-     * @readOnly
-     */
-    this.children = [];
+    children: TreeNode[] = []
 
-    /**
-     * @type {Array.<module:echarts/data/Tree~TreeNode>}
-     * @pubilc
-     */
-    this.viewChildren = [];
+    viewChildren: TreeNode[] = []
 
-    /**
-     * @type {moduel:echarts/data/Tree}
-     * @readOnly
-     */
-    this.hostTree = hostTree;
-};
+    isExpand: boolean = false
 
-TreeNode.prototype = {
+    readonly hostTree: Tree<Model, any, any>    // TODO: TYPE TreeNode use 
generic?
 
-    constructor: TreeNode,
+    constructor(name: string, hostTree: Tree<Model, any, any>) {
+        this.name = name || '';
 
+        this.hostTree = hostTree;
+    }
     /**
      * The node is removed.
-     * @return {boolean} is removed.
      */
-    isRemoved: function () {
+    isRemoved(): boolean {
         return this.dataIndex < 0;
-    },
+    }
 
     /**
      * Travel this subtree (include this node).
@@ -117,16 +90,22 @@ TreeNode.prototype = {
      *        function () { ... }
      *    ); // postorder
      *
-     * @param {(Object|string)} options If string, means order.
-     * @param {string=} options.order 'preorder' or 'postorder'
-     * @param {string=} options.attr 'children' or 'viewChildren'
-     * @param {Function} cb If in preorder and return false,
+     * @param options If string, means order.
+     * @param options.order 'preorder' or 'postorder'
+     * @param options.attr 'children' or 'viewChildren'
+     * @param cb If in preorder and return false,
      *                      its subtree will not be visited.
-     * @param {Object} [context]
      */
-    eachNode: function (options, cb, context) {
+    eachNode<Ctx>(options: TreeTraverseOrder, cb: TreeTraverseCallback<Ctx>, 
context?: Ctx): void
+    eachNode<Ctx>(options: TreeTraverseOption, cb: TreeTraverseCallback<Ctx>, 
context?: Ctx): void
+    eachNode<Ctx>(cb: TreeTraverseCallback<Ctx>, context?: Ctx): void
+    eachNode<Ctx>(
+        options: TreeTraverseOrder | TreeTraverseOption | 
TreeTraverseCallback<Ctx>,
+        cb?: TreeTraverseCallback<Ctx> | Ctx,
+        context?: Ctx
+    ) {
         if (typeof options === 'function') {
-            context = cb;
+            context = cb as Ctx;
             cb = options;
             options = null;
         }
@@ -136,25 +115,27 @@ TreeNode.prototype = {
             options = {order: options};
         }
 
-        var order = options.order || 'preorder';
-        var children = this[options.attr || 'children'];
+        var order = (options as TreeTraverseOption).order || 'preorder';
+        var children = this[(options as TreeTraverseOption).attr || 
'children'];
 
         var suppressVisitSub;
-        order === 'preorder' && (suppressVisitSub = cb.call(context, this));
+        order === 'preorder' && (suppressVisitSub = (cb as 
TreeTraverseCallback<Ctx>).call(context as Ctx, this));
 
         for (var i = 0; !suppressVisitSub && i < children.length; i++) {
-            children[i].eachNode(options, cb, context);
+            children[i].eachNode(
+                options as TreeTraverseOption,
+                cb as TreeTraverseCallback<Ctx>,
+                context
+            );
         }
 
-        order === 'postorder' && cb.call(context, this);
-    },
+        order === 'postorder' && (cb as 
TreeTraverseCallback<Ctx>).call(context, this);
+    }
 
     /**
      * Update depth and height of this subtree.
-     *
-     * @param  {number} depth
      */
-    updateDepthAndHeight: function (depth) {
+    updateDepthAndHeight(depth: number) {
         var height = 0;
         this.depth = depth;
         for (var i = 0; i < this.children.length; i++) {
@@ -165,13 +146,9 @@ TreeNode.prototype = {
             }
         }
         this.height = height + 1;
-    },
+    }
 
-    /**
-     * @param  {string} id
-     * @return {module:echarts/data/Tree~TreeNode}
-     */
-    getNodeById: function (id) {
+    getNodeById(id: string): TreeNode {
         if (this.getId() === id) {
             return this;
         }
@@ -181,13 +158,9 @@ TreeNode.prototype = {
                 return res;
             }
         }
-    },
+    }
 
-    /**
-     * @param {module:echarts/data/Tree~TreeNode} node
-     * @return {boolean}
-     */
-    contains: function (node) {
+    contains(node: TreeNode): boolean {
         if (node === this) {
             return true;
         }
@@ -197,13 +170,13 @@ TreeNode.prototype = {
                 return res;
             }
         }
-    },
+    }
 
     /**
-     * @param {boolean} includeSelf Default false.
-     * @return {Array.<module:echarts/data/Tree~TreeNode>} order: [root, 
child, grandchild, ...]
+     * @param includeSelf Default false.
+     * @return order: [root, child, grandchild, ...]
      */
-    getAncestors: function (includeSelf) {
+    getAncestors(includeSelf?: boolean): TreeNode[] {
         var ancestors = [];
         var node = includeSelf ? this : this.parentNode;
         while (node) {
@@ -212,38 +185,27 @@ TreeNode.prototype = {
         }
         ancestors.reverse();
         return ancestors;
-    },
+    }
 
-    /**
-     * @param {string|Array=} [dimension='value'] Default 'value'. can be 0, 
1, 2, 3
-     * @return {number} Value.
-     */
-    getValue: function (dimension) {
+    getValue(dimension: DimensionLoose): ParsedDataValue {
         var data = this.hostTree.data;
         return data.get(data.getDimension(dimension || 'value'), 
this.dataIndex);
-    },
+    }
 
-    /**
-     * @param {Object} layout
-     * @param {boolean=} [merge=false]
-     */
-    setLayout: function (layout, merge) {
+    setLayout(layout: any, merge?: boolean) {
         this.dataIndex >= 0
             && this.hostTree.data.setItemLayout(this.dataIndex, layout, merge);
-    },
+    }
 
     /**
      * @return {Object} layout
      */
-    getLayout: function () {
+    getLayout(): any {
         return this.hostTree.data.getItemLayout(this.dataIndex);
-    },
+    }
 
-    /**
-     * @param {string} [path]
-     * @return {module:echarts/model/Model}
-     */
-    getModel: function (path) {
+    // TODO: TYPE Same type with Model#getModel
+    getModel(path: readonly string[] | string) {
         if (this.dataIndex < 0) {
             return;
         }
@@ -254,22 +216,19 @@ TreeNode.prototype = {
         if (!levelModel && (this.children.length === 0 || 
(this.children.length !== 0 && this.isExpand === false))) {
             leavesModel = this.getLeavesModel();
         }
-        return itemModel.getModel(path, (levelModel || leavesModel || 
hostTree.hostModel).getModel(path));
-    },
-
-    /**
-     * @return {module:echarts/model/Model}
-     */
-    getLevelModel: function () {
+        return itemModel.getModel(
+            path as [string],
+            (levelModel || leavesModel || hostTree.hostModel).getModel(path as 
[string])
+        );
+    }
+    // TODO: TYPE More specific model
+    getLevelModel(): Model {
         return (this.hostTree.levelModels || [])[this.depth];
-    },
+    }
 
-    /**
-     * @return {module:echarts/model/Model}
-     */
-    getLeavesModel: function () {
+    getLeavesModel(): Model {
         return this.hostTree.leavesModel;
-    },
+    }
 
     /**
      * @example
@@ -278,42 +237,35 @@ TreeNode.prototype = {
      *      'color': color
      *  });
      */
-    setVisual: function (key, value) {
+    setVisual(key: string, value: any): void
+    setVisual(obj: Dictionary<any>): void
+    setVisual(key: string | Dictionary<any>, value?: any) {
         this.dataIndex >= 0
-            && this.hostTree.data.setItemVisual(this.dataIndex, key, value);
-    },
+            && this.hostTree.data.setItemVisual(this.dataIndex, key as string, 
value);
+    }
 
     /**
      * Get item visual
      */
-    getVisual: function (key, ignoreParent) {
+    getVisual(key: string, ignoreParent?: boolean): any {
         return this.hostTree.data.getItemVisual(this.dataIndex, key, 
ignoreParent);
-    },
+    }
 
-    /**
-     * @public
-     * @return {number}
-     */
-    getRawIndex: function () {
+    getRawIndex(): number {
         return this.hostTree.data.getRawIndex(this.dataIndex);
-    },
+    }
 
-    /**
-     * @public
-     * @return {string}
-     */
-    getId: function () {
+    getId(): string {
         return this.hostTree.data.getId(this.dataIndex);
-    },
+    }
 
     /**
      * if this is an ancestor of another node
      *
-     * @public
-     * @param {TreeNode} node another node
-     * @return {boolean} if is ancestor
+     * @param node another node
+     * @return if is ancestor
      */
-    isAncestorOf: function (node) {
+    isAncestorOf(node: TreeNode): boolean {
         var parent = node.parentNode;
         while (parent) {
             if (parent === this) {
@@ -322,72 +274,46 @@ TreeNode.prototype = {
             parent = parent.parentNode;
         }
         return false;
-    },
+    }
 
     /**
      * if this is an descendant of another node
      *
-     * @public
-     * @param {TreeNode} node another node
-     * @return {boolean} if is descendant
+     * @param node another node
+     * @return if is descendant
      */
-    isDescendantOf: function (node) {
+    isDescendantOf(node: TreeNode): boolean {
         return node !== this && node.isAncestorOf(this);
     }
 };
 
-/**
- * @constructor
- * @alias module:echarts/data/Tree
- * @param {module:echarts/model/Model} hostModel
- * @param {Array.<Object>} levelOptions
- * @param {Object} leavesOption
- */
-function Tree(hostModel, levelOptions, leavesOption) {
-    /**
-     * @type {module:echarts/data/Tree~TreeNode}
-     * @readOnly
-     */
-    this.root;
+class Tree<HostModel extends Model, LevelOption, LeavesOption> {
 
-    /**
-     * @type {module:echarts/data/List}
-     * @readOnly
-     */
-    this.data;
+    type: 'tree' = 'tree'
 
-    /**
-     * Index of each item is the same as the raw index of coresponding list 
item.
-     * @private
-     * @type {Array.<module:echarts/data/Tree~TreeNode}
-     */
-    this._nodes = [];
+    root: TreeNode
 
-    /**
-     * @private
-     * @readOnly
-     * @type {module:echarts/model/Model}
-     */
-    this.hostModel = hostModel;
+    data: List
 
-    /**
-     * @private
-     * @readOnly
-     * @type {Array.<module:echarts/model/Model}
-     */
-    this.levelModels = zrUtil.map(levelOptions || [], function (levelDefine) {
-        return new Model(levelDefine, hostModel, hostModel.ecModel);
-    });
+    hostModel: HostModel
 
-    this.leavesModel = new Model(leavesOption || {}, hostModel, 
hostModel.ecModel);
-}
+    levelModels: Model<LevelOption>[]
 
-Tree.prototype = {
+    leavesModel: Model<LeavesOption>
 
-    constructor: Tree,
+    private _nodes: TreeNode[]
 
-    type: 'tree',
+    constructor(hostModel: HostModel, levelOptions: LevelOption[], 
leavesOption: LeavesOption) {
+        this.levelModels = zrUtil.map(levelOptions || [], function 
(levelDefine) {
+            return new Model(levelDefine, hostModel, hostModel.ecModel);
+        });
 
+        this.leavesModel = new Model<LeavesOption>(
+            leavesOption || {} as LeavesOption,
+            hostModel,
+            hostModel.ecModel
+        );
+    }
     /**
      * Travel this subtree (include this node).
      * Usage:
@@ -399,38 +325,37 @@ Tree.prototype = {
      *        function () { ... }
      *    ); // postorder
      *
-     * @param {(Object|string)} options If string, means order.
-     * @param {string=} options.order 'preorder' or 'postorder'
-     * @param {string=} options.attr 'children' or 'viewChildren'
-     * @param {Function} cb
-     * @param {Object}   [context]
-     */
-    eachNode: function (options, cb, context) {
-        this.root.eachNode(options, cb, context);
-    },
+     * @param options If string, means order.
+     * @param options.order 'preorder' or 'postorder'
+     * @param options.attr 'children' or 'viewChildren'
+     * @param cb
+     * @param context
+     */
+    eachNode<Ctx>(options: TreeTraverseOrder, cb: TreeTraverseCallback<Ctx>, 
context?: Ctx): void
+    eachNode<Ctx>(options: TreeTraverseOption, cb: TreeTraverseCallback<Ctx>, 
context?: Ctx): void
+    eachNode<Ctx>(cb: TreeTraverseCallback<Ctx>, context?: Ctx): void
+    eachNode<Ctx>(
+        options: TreeTraverseOrder | TreeTraverseOption | 
TreeTraverseCallback<Ctx>,
+        cb?: TreeTraverseCallback<Ctx> | Ctx,
+        context?: Ctx
+    ) {
+        this.root.eachNode(options as TreeTraverseOption, cb as 
TreeTraverseCallback<Ctx>, context);
+    }
 
-    /**
-     * @param {number} dataIndex
-     * @return {module:echarts/data/Tree~TreeNode}
-     */
-    getNodeByDataIndex: function (dataIndex) {
+    getNodeByDataIndex(dataIndex: number): TreeNode {
         var rawIndex = this.data.getRawIndex(dataIndex);
         return this._nodes[rawIndex];
-    },
+    }
 
-    /**
-     * @param {string} name
-     * @return {module:echarts/data/Tree~TreeNode}
-     */
-    getNodeByName: function (name) {
-        return this.root.getNodeByName(name);
-    },
+    getNodeById(name: string): TreeNode {
+        return this.root.getNodeById(name);
+    }
 
     /**
      * Update item available by list,
      * when list has been performed options like 'filterSelf' or 'map'.
      */
-    update: function () {
+    update() {
         var data = this.data;
         var nodes = this._nodes;
 
@@ -441,98 +366,95 @@ Tree.prototype = {
         for (var i = 0, len = data.count(); i < len; i++) {
             nodes[data.getRawIndex(i)].dataIndex = i;
         }
-    },
+    }
 
     /**
      * Clear all layouts
      */
-    clearLayouts: function () {
+    clearLayouts() {
         this.data.clearItemLayouts();
     }
-};
-
-/**
- * data node format:
- * {
- *     name: ...
- *     value: ...
- *     children: [
- *         {
- *             name: ...
- *             value: ...
- *             children: ...
- *         },
- *         ...
- *     ]
- * }
- *
- * @static
- * @param {Object} dataRoot Root node.
- * @param {module:echarts/model/Model} hostModel
- * @param {Object} treeOptions
- * @param {Array.<Object>} treeOptions.levels
- * @param {Array.<Object>} treeOptions.leaves
- * @return module:echarts/data/Tree
- */
-Tree.createTree = function (dataRoot, hostModel, treeOptions, beforeLink) {
-
-    var tree = new Tree(hostModel, treeOptions.levels, treeOptions.leaves);
-    var listData = [];
-    var dimMax = 1;
-
-    buildHierarchy(dataRoot);
-
-    function buildHierarchy(dataNode, parentNode) {
-        var value = dataNode.value;
-        dimMax = Math.max(dimMax, zrUtil.isArray(value) ? value.length : 1);
-
-        listData.push(dataNode);
-
-        var node = new TreeNode(dataNode.name, tree);
-        parentNode
-            ? addChild(node, parentNode)
-            : (tree.root = node);
 
-        tree._nodes.push(node);
 
-        var children = dataNode.children;
-        if (children) {
-            for (var i = 0; i < children.length; i++) {
-                buildHierarchy(children[i], node);
+    /**
+     * data node format:
+     * {
+     *     name: ...
+     *     value: ...
+     *     children: [
+     *         {
+     *             name: ...
+     *             value: ...
+     *             children: ...
+     *         },
+     *         ...
+     *     ]
+     * }
+     */
+    static createTree<T extends TreeNodeData, HostModel extends Model, 
LevelOption, LeavesOption>(
+        dataRoot: T,
+        hostModel: HostModel,
+        treeOptions?: {levels: LevelOption[], leaves: LeavesOption},
+        beforeLink?: (data: List) => void
+    ) {
+
+        var tree = new Tree(hostModel, treeOptions.levels, treeOptions.leaves);
+        var listData: TreeNodeData[] = [];
+        var dimMax = 1;
+
+        buildHierarchy(dataRoot);
+
+        function buildHierarchy(dataNode: TreeNodeData, parentNode?: TreeNode) 
{
+            var value = dataNode.value;
+            dimMax = Math.max(dimMax, zrUtil.isArray(value) ? value.length : 
1);
+
+            listData.push(dataNode);
+
+            var node = new TreeNode(dataNode.name, tree);
+            parentNode
+                ? addChild(node, parentNode)
+                : (tree.root = node);
+
+            tree._nodes.push(node);
+
+            var children = dataNode.children;
+            if (children) {
+                for (var i = 0; i < children.length; i++) {
+                    buildHierarchy(children[i], node);
+                }
             }
         }
-    }
 
-    tree.root.updateDepthAndHeight(0);
+        tree.root.updateDepthAndHeight(0);
 
-    var dimensionsInfo = createDimensions(listData, {
-        coordDimensions: ['value'],
-        dimensionsCount: dimMax
-    });
+        var dimensionsInfo = createDimensions(listData, {
+            coordDimensions: ['value'],
+            dimensionsCount: dimMax
+        });
 
-    var list = new List(dimensionsInfo, hostModel);
-    list.initData(listData);
+        var list = new List(dimensionsInfo, hostModel);
+        list.initData(listData);
 
-    linkList({
-        mainData: list,
-        struct: tree,
-        structAttr: 'tree'
-    });
+        linkList({
+            mainData: list,
+            struct: tree,
+            structAttr: 'tree'
+        });
 
-    tree.update();
+        tree.update();
 
-    beforeLink && beforeLink(list);
+        beforeLink && beforeLink(list);
 
-    return tree;
-};
+        return tree;
+    }
+
+}
 
 /**
  * It is needed to consider the mess of 'list', 'hostModel' when creating a 
TreeNote,
  * so this function is not ready and not necessary to be public.
- *
- * @param {(module:echarts/data/Tree~TreeNode|Object)} child
  */
-function addChild(child, node) {
+function addChild(child: TreeNode, node: TreeNode) {
     var children = node.children;
     if (child.parentNode === node) {
         return;
diff --git a/src/data/helper/createDimensions.ts 
b/src/data/helper/createDimensions.ts
index ae187f3..8de100f 100644
--- a/src/data/helper/createDimensions.ts
+++ b/src/data/helper/createDimensions.ts
@@ -45,7 +45,8 @@ export type CreateDimensionsParams = {
  * @param opt.encodeDefaulter Make default encode if user not specified.
  */
 export default function (
-    source: Source | List,
+    // TODO: TYPE completeDimensions type
+    source: Source | List | any[],
     opt?: CreateDimensionsParams
 ): DataDimensionInfo[] {
     opt = opt || {};
diff --git a/src/model/Model.ts b/src/model/Model.ts
index 6f8480e..4646e3d 100644
--- a/src/model/Model.ts
+++ b/src/model/Model.ts
@@ -248,8 +248,8 @@ function doGet(obj: ModelOption, pathArr: readonly 
string[], parentModel?: Model
         }
     }
     if (obj == null && parentModel) {
-        // TODO At most 3 items array.
-        obj = parentModel.get(pathArr as [string, string, string]);
+        // TODO At most 3 items array. support string[]?
+        obj = parentModel.get(pathArr as [string]);
     }
     return obj;
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to