You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2020/03/06 09:10:28 UTC

[incubator-echarts] branch typescript updated: ts: add types for graph

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 f5d5466  ts: add types for graph
f5d5466 is described below

commit f5d5466844a9197fc8e15c1d1d9b7a40d73b1941
Author: pissang <bm...@gmail.com>
AuthorDate: Fri Mar 6 17:10:10 2020 +0800

    ts: add types for graph
---
 src/action/roamHelper.ts                       |  23 +-
 src/chart/effectScatter/EffectScatterSeries.ts |   8 +-
 src/chart/funnel/FunnelSeries.ts               |  10 +-
 src/chart/funnel/funnelLayout.ts               |   8 +-
 src/chart/graph.ts                             |   4 +-
 src/chart/graph/GraphSeries.ts                 | 287 ++++++++--
 src/chart/graph/GraphView.ts                   | 204 ++++---
 src/chart/graph/adjustEdge.ts                  |  25 +-
 src/chart/graph/backwardCompat.ts              |  19 -
 src/chart/graph/categoryFilter.ts              |  12 +-
 src/chart/graph/categoryVisual.ts              |  12 +-
 src/chart/graph/circularLayout.ts              |   8 +-
 src/chart/graph/circularLayoutHelper.ts        |  36 +-
 src/chart/graph/createView.ts                  |  23 +-
 src/chart/graph/edgeVisual.ts                  |  13 +-
 src/chart/graph/forceHelper.ts                 |  82 ++-
 src/chart/graph/forceLayout.ts                 | 106 ++--
 src/chart/graph/graphAction.ts                 |  15 +-
 src/chart/graph/graphHelper.ts                 |  10 +-
 src/chart/graph/simpleLayout.ts                |  13 +-
 src/chart/graph/simpleLayoutHelper.ts          |   8 +-
 src/chart/heatmap/HeatmapSeries.ts             |   2 +-
 src/chart/helper/Line.ts                       | 722 +++++++++++++------------
 src/chart/helper/LineDraw.ts                   | 252 +++++----
 src/chart/helper/LinePath.ts                   | 101 ++--
 src/chart/helper/Symbol.ts                     |   2 +-
 src/chart/line/LineSeries.ts                   |   6 +-
 src/chart/map/MapSeries.ts                     |  12 +-
 src/chart/pictorialBar.ts                      |   2 -
 src/chart/pie/PieSeries.ts                     |  10 +-
 src/chart/radar.ts                             |   2 -
 src/chart/scatter/ScatterSeries.ts             |   6 +-
 src/chart/themeRiver.ts                        |   2 -
 src/chart/tree.ts                              |   2 -
 src/chart/treemap.ts                           |   2 -
 src/component/helper/RoamController.ts         |   4 +-
 src/coord/View.ts                              |   2 +-
 src/coord/geo/GeoModel.ts                      |   9 +-
 src/data/Graph.ts                              |  12 +-
 src/data/List.ts                               |  26 +-
 src/data/Tree.ts                               |   2 +-
 src/echarts.ts                                 |   4 +-
 src/model/Series.ts                            |  29 +-
 src/model/mixin/dataFormat.ts                  |   7 -
 src/util/format.ts                             |  17 +-
 src/util/types.ts                              |  54 +-
 src/visual/symbol.ts                           |  15 +-
 47 files changed, 1331 insertions(+), 899 deletions(-)

diff --git a/src/action/roamHelper.ts b/src/action/roamHelper.ts
index 52892e4..f5d8305 100644
--- a/src/action/roamHelper.ts
+++ b/src/action/roamHelper.ts
@@ -17,15 +17,24 @@
 * under the License.
 */
 
-// @ts-nocheck
+import View from '../coord/View';
+import { Payload } from '../util/types';
+
+export interface RoamPaylod extends Payload {
+    dx: number
+    dy: number
+    zoom: number
+    originX: number
+    originY: number
+}
 
-/**
- * @param {module:echarts/coord/View} view
- * @param {Object} payload
- * @param {Object} [zoomLimit]
- */
 export function updateCenterAndZoom(
-    view, payload, zoomLimit
+    view: View,
+    payload: RoamPaylod,
+    zoomLimit?: {
+        min?: number,
+        max?: number
+    }
 ) {
     var previousZoom = view.getZoom();
     var center = view.getCenter();
diff --git a/src/chart/effectScatter/EffectScatterSeries.ts b/src/chart/effectScatter/EffectScatterSeries.ts
index e8258e6..bbbb8d3 100644
--- a/src/chart/effectScatter/EffectScatterSeries.ts
+++ b/src/chart/effectScatter/EffectScatterSeries.ts
@@ -26,7 +26,7 @@ import {
     SeriesOnCalendarOptionMixin,
     SeriesOnGeoOptionMixin,
     SeriesOnSingleOptionMixin,
-    SeriesSymbolOptionMixin,
+    SymbolOptionMixin,
     OptionDataValue,
     ItemStyleOption,
     LabelOption,
@@ -37,7 +37,7 @@ import List from '../../data/List';
 
 type ScatterDataValue = OptionDataValue | OptionDataValue[]
 
-export interface EffectScatterDataItemOption {
+export interface EffectScatterDataItemOption extends SymbolOptionMixin {
     name?: string
 
     value?: ScatterDataValue
@@ -53,7 +53,7 @@ export interface EffectScatterDataItemOption {
 
 export interface EffectScatterSeriesOption extends SeriesOption,
     SeriesOnCartesianOptionMixin, SeriesOnPolarOptionMixin, SeriesOnCalendarOptionMixin,
-    SeriesOnGeoOptionMixin, SeriesOnSingleOptionMixin, SeriesSymbolOptionMixin {
+    SeriesOnGeoOptionMixin, SeriesOnSingleOptionMixin, SymbolOptionMixin {
 
     coordinateSystem?: string
 
@@ -93,7 +93,7 @@ class EffectScatterSeriesModel extends SeriesModel<EffectScatterSeriesOption> {
         return createListFromArray(this.getSource(), this, {useEncodeDefaulter: true});
     }
 
-    defaultOption: EffectScatterSeriesOption = {
+    static defaultOption: EffectScatterSeriesOption = {
         coordinateSystem: 'cartesian2d',
         zlevel: 0,
         z: 2,
diff --git a/src/chart/funnel/FunnelSeries.ts b/src/chart/funnel/FunnelSeries.ts
index 04792b3..9967d90 100644
--- a/src/chart/funnel/FunnelSeries.ts
+++ b/src/chart/funnel/FunnelSeries.ts
@@ -28,7 +28,7 @@ import {
     BoxLayoutOptionMixin,
     HorizontalAlign,
     LabelOption,
-    LabelLineOption,
+    LabelGuideLineOption,
     ItemStyleOption,
     OptionDataValueNumeric
 } from '../../util/types';
@@ -48,12 +48,12 @@ interface FunnelDataItem {
 
     itemStyle?: ItemStyleOption
     label?: FunnelLabelOption
-    labelLine?: LabelLineOption
+    labelLine?: LabelGuideLineOption
 
     emphasis?: {
         itemStyle?: ItemStyleOption
         label?: FunnelLabelOption
-        labelLine?: LabelLineOption
+        labelLine?: LabelGuideLineOption
     }
 }
 
@@ -76,12 +76,12 @@ export interface FunnelSeriesOption
     funnelAlign?: HorizontalAlign
 
     label?: FunnelLabelOption
-    labelLine?: LabelLineOption
+    labelLine?: LabelGuideLineOption
     itemStyle?: ItemStyleOption
 
     emphasis?: {
         label?: FunnelLabelOption
-        labelLine?: LabelLineOption
+        labelLine?: LabelGuideLineOption
         itemStyle?: ItemStyleOption
     }
 
diff --git a/src/chart/funnel/funnelLayout.ts b/src/chart/funnel/funnelLayout.ts
index 6b4e0c6..78d838a 100644
--- a/src/chart/funnel/funnelLayout.ts
+++ b/src/chart/funnel/funnelLayout.ts
@@ -38,10 +38,10 @@ function getViewRect(seriesModel: FunnelSeriesModel, api: ExtensionAPI) {
 
 function getSortedIndices(data: List, sort: FunnelSeriesOption['sort']) {
     var valueDim = data.mapDimension('value');
-    var valueArr = data.mapArray(valueDim, function (val) {
+    var valueArr = data.mapArray(valueDim, function (val: number) {
         return val;
     });
-    var indices = [];
+    var indices: number[] = [];
     var isAscending = sort === 'ascending';
     for (var i = 0, len = data.count(); i < len; i++) {
         indices[i] = i;
@@ -53,7 +53,9 @@ function getSortedIndices(data: List, sort: FunnelSeriesOption['sort']) {
     }
     else if (sort !== 'none') {
         indices.sort(function (a, b) {
-            return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a];
+            return isAscending
+                ? valueArr[a] - valueArr[b]
+                : valueArr[b] - valueArr[a];
         });
     }
     return indices;
diff --git a/src/chart/graph.ts b/src/chart/graph.ts
index 9a390e9..6dcb736 100644
--- a/src/chart/graph.ts
+++ b/src/chart/graph.ts
@@ -17,8 +17,6 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as echarts from '../echarts';
 
 import './graph/GraphSeries';
@@ -33,6 +31,7 @@ import simpleLayout from './graph/simpleLayout';
 import circularLayout from './graph/circularLayout';
 import forceLayout from './graph/forceLayout';
 import createView from './graph/createView';
+import View from '../coord/View';
 
 echarts.registerProcessor(categoryFilter);
 
@@ -46,5 +45,6 @@ echarts.registerLayout(forceLayout);
 
 // Graph view coordinate system
 echarts.registerCoordinateSystem('graphView', {
+    dimensions: View.dimensions,
     create: createView
 });
\ No newline at end of file
diff --git a/src/chart/graph/GraphSeries.ts b/src/chart/graph/GraphSeries.ts
index 4cd1339..a2be48d 100644
--- a/src/chart/graph/GraphSeries.ts
+++ b/src/chart/graph/GraphSeries.ts
@@ -17,9 +17,6 @@
 * under the License.
 */
 
-// @ts-nocheck
-
-import * as echarts from '../../echarts';
 import List from '../../data/List';
 import * as zrUtil from 'zrender/src/core/util';
 import {defaultEmphasis} from '../../util/model';
@@ -27,13 +24,198 @@ import Model from '../../model/Model';
 import {encodeHTML} from '../../util/format';
 import createGraphFromNodeEdge from '../helper/createGraphFromNodeEdge';
 import LegendVisualProvider from '../../visual/LegendVisualProvider';
+import {
+    SeriesOption,
+    SeriesOnCartesianOptionMixin,
+    SeriesOnPolarOptionMixin,
+    SeriesOnCalendarOptionMixin,
+    SeriesOnGeoOptionMixin,
+    SeriesOnSingleOptionMixin,
+    OptionDataValue,
+    RoamOptionMixin,
+    LabelOption,
+    ItemStyleOption,
+    LineStyleOption,
+    SymbolOptionMixin,
+    BoxLayoutOptionMixin,
+    LabelFormatterCallback,
+    Dictionary
+} from '../../util/types';
+import SeriesModel from '../../model/Series';
+import Graph from '../../data/Graph';
+import GlobalModel from '../../model/Global';
+import { VectorArray } from 'zrender/src/core/vector';
+import { ForceLayoutInstance } from './forceLayout';
+
+type GraphDataValue = OptionDataValue | OptionDataValue[]
+
+interface GraphEdgeLineStyleOption extends LineStyleOption {
+    curveness: number
+}
+export interface GraphNodeItemOption extends SymbolOptionMixin {
+    id?: string
+    name?: string
+    value?: GraphDataValue
+
+    itemStyle?: ItemStyleOption
+    label?: LabelOption
+
+    emphasis?: {
+        itemStyle?: ItemStyleOption
+        label?: LabelOption
+    }
+
+    /**
+     * Fixed x position
+     */
+    x?: number
+    /**
+     * Fixed y position
+     */
+    y?: number
+
+    /**
+     * If this node is fixed during force layout.
+     */
+    fixed?: boolean
+
+    category?: number
+
+    draggable?: boolean
+
+    focusNodeAdjacency?: boolean
+}
+
+export interface GraphEdgeItemOption {
+    /**
+     * Name or index of source node.
+     */
+    source?: string | number
+    /**
+     * Name or index of target node.
+     */
+    target?: string | number
+
+    value?: number
+
+    lineStyle?: GraphEdgeLineStyleOption
+    label?: LabelOption
+
+    emphasis?: {
+        lineStyle?: GraphEdgeLineStyleOption
+        label?: LabelOption
+    }
+
+    /**
+     * Symbol of both line ends
+     */
+    symbol?: string | string[]
+
+    symbolSize?: number | number[]
+}
+
+export interface GraphCategoryItemOption extends SymbolOptionMixin {
+    name?: string
+
+    value?: OptionDataValue
+
+    itemStyle?: ItemStyleOption
+    label?: LabelOption
+
+    emphasis?: {
+        itemStyle?: ItemStyleOption
+        label?: LabelOption
+    }
+}
+
+interface GraphSeriesOption extends SeriesOption,
+    SeriesOnCartesianOptionMixin, SeriesOnPolarOptionMixin, SeriesOnCalendarOptionMixin,
+    SeriesOnGeoOptionMixin, SeriesOnSingleOptionMixin,
+    SymbolOptionMixin,
+    RoamOptionMixin,
+    BoxLayoutOptionMixin {
+
+    coordinateSystem?: string
+
+    hoverAnimation?: boolean
+    legendHoverLink?: boolean
+
+    layout?: 'none' | 'force' | 'circular'
+
+    data?: GraphNodeItemOption[]
+    nodes?: GraphNodeItemOption[]
+
+    edges?: GraphEdgeItemOption[]
+    links?: GraphEdgeItemOption[]
+
+    categories?: GraphCategoryItemOption[]
+
+    focusNodeAdjacency?: boolean
 
-var GraphSeries = echarts.extendSeriesModel({
+    /**
+     * Symbol size scale ratio in roam
+     */
+    nodeScaleRatio: 0.6,
+
+    draggable?: boolean
+
+    edgeSymbol: string | string[]
+    edgeSymbolSize: number | number[]
+
+    edgeLabel?: LabelOption & {
+        formatter?: LabelFormatterCallback | string
+    }
+    label?: LabelOption & {
+        formatter?: LabelFormatterCallback | string
+    }
+
+    itemStyle?: ItemStyleOption
+    lineStyle?: GraphEdgeLineStyleOption
+
+    emphasis?: {
+        label?: LabelOption
+        edgeLabel?: LabelOption
+        itemStyle?: ItemStyleOption
+        lineStyle?: LineStyleOption
+    }
 
-    type: 'series.graph',
+    // Configuration of circular layout
+    circular?: {
+        rotateLabel?: boolean
+    }
+
+    // Configuration of force directed layout
+    force: {
+        initLayout: 'circular' | 'none'
+        // Node repulsion. Can be an array to represent range.
+        repulsion: number | number[]
+        gravity: number
+        // Initial friction
+        friction: number
+
+        // Edge length. Can be an array to represent range.
+        edgeLength: number | number[]
+
+        layoutAnimation: boolean
+    }
+}
+
+class GraphSeriesModel extends SeriesModel<GraphSeriesOption> {
+    static readonly type = 'series.graph'
+    readonly type = GraphSeriesModel.type
+
+    private _categoriesData: List
+    private _categoriesModels: Model<GraphCategoryItemOption>[]
+
+    /**
+     * Preserved points during layouting
+     */
+    preservedPoints?: Dictionary<VectorArray>
 
-    init: function (option) {
-        GraphSeries.superApply(this, 'init', arguments);
+    forceLayout?: ForceLayoutInstance
+
+    init(option: GraphSeriesOption) {
+        super.init.apply(this, arguments as any);
 
         var self = this;
         function getCategoriesData() {
@@ -47,22 +229,22 @@ var GraphSeries = echarts.extendSeriesModel({
         this.fillDataTextStyle(option.edges || option.links);
 
         this._updateCategoriesData();
-    },
+    }
 
-    mergeOption: function (option) {
-        GraphSeries.superApply(this, 'mergeOption', arguments);
+    mergeOption(option: GraphSeriesOption) {
+        super.mergeOption.apply(this, arguments as any);
 
         this.fillDataTextStyle(option.edges || option.links);
 
         this._updateCategoriesData();
-    },
+    }
 
-    mergeDefaultAndTheme: function (option) {
-        GraphSeries.superApply(this, 'mergeDefaultAndTheme', arguments);
+    mergeDefaultAndTheme(option: GraphSeriesOption) {
+        super.mergeDefaultAndTheme.apply(this, arguments as any);
         defaultEmphasis(option, 'edgeLabel', ['show']);
-    },
+    }
 
-    getInitialData: function (option, ecModel) {
+    getInitialData(option: GraphSeriesOption, ecModel: GlobalModel) {
         var edges = option.edges || option.links || [];
         var nodes = option.data || option.nodes || [];
         var self = this;
@@ -71,7 +253,7 @@ var GraphSeries = echarts.extendSeriesModel({
             return createGraphFromNodeEdge(nodes, edges, this, true, beforeLink).data;
         }
 
-        function beforeLink(nodeData, edgeData) {
+        function beforeLink(nodeData: List, edgeData: List) {
             // Overwrite nodeData.getItemModel to
             nodeData.wrapMethod('getItemModel', function (model) {
                 var categoriesModels = self._categoriesModels;
@@ -91,7 +273,7 @@ var GraphSeries = echarts.extendSeriesModel({
                 edgeLabelModel.parentModel,
                 ecModel
             );
-            var emphasisEdgeLabelModel = self.getModel('emphasis.edgeLabel');
+            var emphasisEdgeLabelModel = self.getModel(['emphasis', 'edgeLabel']);
             var emphasisFakeSeriesModel = new Model(
                 {emphasis: {label: emphasisEdgeLabelModel.option}},
                 emphasisEdgeLabelModel.parentModel,
@@ -103,42 +285,33 @@ var GraphSeries = echarts.extendSeriesModel({
                 return model;
             });
 
-            function edgeGetParent(path) {
-                path = this.parsePath(path);
-                return (path && path[0] === 'label')
+            function edgeGetParent(this: Model, path: string | string[]) {
+                const pathArr = this.parsePath(path);
+                return (pathArr && pathArr[0] === 'label')
                     ? fakeSeriesModel
-                    : (path && path[0] === 'emphasis' && path[1] === 'label')
+                    : (pathArr && pathArr[0] === 'emphasis' && pathArr[1] === 'label')
                     ? emphasisFakeSeriesModel
                     : this.parentModel;
             }
         }
-    },
+    }
 
-    /**
-     * @return {module:echarts/data/Graph}
-     */
-    getGraph: function () {
+    getGraph(): Graph {
         return this.getData().graph;
-    },
+    }
 
-    /**
-     * @return {module:echarts/data/List}
-     */
-    getEdgeData: function () {
+    getEdgeData(): List {
         return this.getGraph().edgeData;
-    },
+    }
 
-    /**
-     * @return {module:echarts/data/List}
-     */
-    getCategoriesData: function () {
+    getCategoriesData(): List {
         return this._categoriesData;
-    },
+    }
 
     /**
      * @override
      */
-    formatTooltip: function (dataIndex, multipleSeries, dataType) {
+    formatTooltip(dataIndex: number, multipleSeries: boolean, dataType: string) {
         if (dataType === 'edge') {
             var nodeData = this.getData();
             var params = this.getDataParams(dataIndex, dataType);
@@ -149,19 +322,19 @@ var GraphSeries = echarts.extendSeriesModel({
             var html = [];
             sourceName != null && html.push(sourceName);
             targetName != null && html.push(targetName);
-            html = encodeHTML(html.join(' > '));
+            var htmlStr = encodeHTML(html.join(' > '));
 
             if (params.value) {
-                html += ' : ' + encodeHTML(params.value);
+                htmlStr += ' : ' + encodeHTML(params.value);
             }
-            return html;
+            return htmlStr;
         }
         else { // dataType === 'node' or empty
-            return GraphSeries.superApply(this, 'formatTooltip', arguments);
+            return super.formatTooltip.apply(this, arguments as any);
         }
-    },
+    }
 
-    _updateCategoriesData: function () {
+    _updateCategoriesData() {
         var categories = zrUtil.map(this.option.categories || [], function (category) {
             // Data must has value
             return category.value != null ? category : zrUtil.extend({
@@ -174,25 +347,25 @@ var GraphSeries = echarts.extendSeriesModel({
         this._categoriesData = categoriesData;
 
         this._categoriesModels = categoriesData.mapArray(function (idx) {
-            return categoriesData.getItemModel(idx, true);
+            return categoriesData.getItemModel(idx);
         });
-    },
+    }
 
-    setZoom: function (zoom) {
+    setZoom(zoom: number) {
         this.option.zoom = zoom;
-    },
+    }
 
-    setCenter: function (center) {
+    setCenter(center: number[]) {
         this.option.center = center;
-    },
+    }
 
-    isAnimationEnabled: function () {
-        return GraphSeries.superCall(this, 'isAnimationEnabled')
+    isAnimationEnabled() {
+        return super.isAnimationEnabled()
             // Not enable animation when do force layout
-            && !(this.get('layout') === 'force' && this.get('force.layoutAnimation'));
-    },
+            && !(this.get('layout') === 'force' && this.get(['force', 'layoutAnimation']));
+    }
 
-    defaultOption: {
+    static defaultOption: GraphSeriesOption = {
         zlevel: 0,
         z: 2,
 
@@ -288,6 +461,6 @@ var GraphSeries = echarts.extendSeriesModel({
             }
         }
     }
-});
+}
 
-export default GraphSeries;
\ No newline at end of file
+export default GraphSeriesModel;
\ No newline at end of file
diff --git a/src/chart/graph/GraphView.ts b/src/chart/graph/GraphView.ts
index da2e325..12f3f6a 100644
--- a/src/chart/graph/GraphView.ts
+++ b/src/chart/graph/GraphView.ts
@@ -17,9 +17,6 @@
 * under the License.
 */
 
-// @ts-nocheck
-
-import * as echarts from '../../echarts';
 import * as zrUtil from 'zrender/src/core/util';
 import SymbolDraw from '../helper/SymbolDraw';
 import LineDraw from '../helper/LineDraw';
@@ -29,20 +26,48 @@ import {onIrrelevantElement} from '../../component/helper/cursorHelper';
 import * as graphic from '../../util/graphic';
 import adjustEdge from './adjustEdge';
 import {getNodeGlobalScale} from './graphHelper';
+import ChartView from '../../view/Chart';
+import GlobalModel from '../../model/Global';
+import ExtensionAPI from '../../ExtensionAPI';
+import GraphSeriesModel, { GraphNodeItemOption } from './GraphSeries';
+import { CoordinateSystem } from '../../coord/CoordinateSystem';
+import View from '../../coord/View';
+import { GraphNode, GraphEdge } from '../../data/Graph';
+import Displayable from 'zrender/src/graphic/Displayable';
+import Symbol from '../helper/Symbol';
+import Model from '../../model/Model';
+import { Payload } from '../../util/types';
+import { LineLabel } from '../helper/Line';
+
+const FOCUS_ADJACENCY = '__focusNodeAdjacency';
+const UNFOCUS_ADJACENCY = '__unfocusNodeAdjacency';
+
+const nodeOpacityPath = ['itemStyle', 'opacity'] as const;
+const lineOpacityPath = ['lineStyle', 'opacity'] as const;
+
+interface FocusNodePayload extends Payload {
+    dataIndex: number
+    edgeDataIndex: number
+}
 
-var FOCUS_ADJACENCY = '__focusNodeAdjacency';
-var UNFOCUS_ADJACENCY = '__unfocusNodeAdjacency';
-
-var nodeOpacityPath = ['itemStyle', 'opacity'];
-var lineOpacityPath = ['lineStyle', 'opacity'];
+function isViewCoordSys(coordSys: CoordinateSystem): coordSys is View {
+    return coordSys.type === 'view';
+}
 
-function getItemOpacity(item, opacityPath) {
+function getItemOpacity(
+    item: GraphNode | GraphEdge,
+    opacityPath: typeof nodeOpacityPath | typeof lineOpacityPath
+): number {
     var opacity = item.getVisual('opacity');
     return opacity != null ? opacity : item.getModel().get(opacityPath);
 }
 
-function fadeOutItem(item, opacityPath, opacityRatio) {
-    var el = item.getGraphicEl();
+function fadeOutItem(
+    item: GraphNode | GraphEdge,
+    opacityPath: typeof nodeOpacityPath | typeof lineOpacityPath,
+    opacityRatio?: number
+) {
+    var el = item.getGraphicEl() as Symbol;   // TODO Symbol?
     var opacity = getItemOpacity(item, opacityPath);
 
     if (opacityRatio != null) {
@@ -51,7 +76,7 @@ function fadeOutItem(item, opacityPath, opacityRatio) {
     }
 
     el.downplay && el.downplay();
-    el.traverse(function (child) {
+    el.traverse(function (child: LineLabel) {
         if (!child.isGroup) {
             var opct = child.lineLabelOriginalOpacity;
             if (opct == null || opacityRatio != null) {
@@ -62,29 +87,54 @@ function fadeOutItem(item, opacityPath, opacityRatio) {
     });
 }
 
-function fadeInItem(item, opacityPath) {
+function fadeInItem(
+    item: GraphNode | GraphEdge,
+    opacityPath: typeof nodeOpacityPath | typeof lineOpacityPath
+) {
     var opacity = getItemOpacity(item, opacityPath);
-    var el = item.getGraphicEl();
+    var el = item.getGraphicEl() as Symbol;
     // Should go back to normal opacity first, consider hoverLayer,
     // where current state is copied to elMirror, and support
     // emphasis opacity here.
-    el.traverse(function (child) {
+    el.traverse(function (child: Displayable) {
         !child.isGroup && child.setStyle('opacity', opacity);
     });
     el.highlight && el.highlight();
 }
 
-export default echarts.extendChartView({
+class GraphView extends ChartView {
+
+    static readonly type = 'graph'
+    readonly type = GraphView.type
+
+    private _symbolDraw: SymbolDraw
+    private _lineDraw: LineDraw
+
+    private _controller: RoamController
+    private _controllerHost: {
+        target: graphic.Group
+        zoom?: number
+        zoomLimit?: {min?: number, max?: number}
+    }
+
+    private _firstRender: boolean
+
+    private _model: GraphSeriesModel
 
-    type: 'graph',
+    private _layoutTimeout: number
+    private _unfocusDelayTimer: number
 
-    init: function (ecModel, api) {
+    private _layouting: boolean
+
+    init(ecModel: GlobalModel, api: ExtensionAPI) {
         var symbolDraw = new SymbolDraw();
         var lineDraw = new LineDraw();
         var group = this.group;
 
         this._controller = new RoamController(api.getZr());
-        this._controllerHost = {target: group};
+        this._controllerHost = {
+            target: group
+        };
 
         group.add(symbolDraw.group);
         group.add(lineDraw.group);
@@ -93,9 +143,9 @@ export default echarts.extendChartView({
         this._lineDraw = lineDraw;
 
         this._firstRender = true;
-    },
+    }
 
-    render: function (seriesModel, ecModel, api) {
+    render(seriesModel: GraphSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
         var graphView = this;
         var coordSys = seriesModel.coordinateSystem;
 
@@ -106,7 +156,7 @@ export default echarts.extendChartView({
 
         var group = this.group;
 
-        if (coordSys.type === 'view') {
+        if (isViewCoordSys(coordSys)) {
             var groupNewProp = {
                 position: coordSys.position,
                 scale: coordSys.scale
@@ -133,18 +183,18 @@ export default echarts.extendChartView({
 
         clearTimeout(this._layoutTimeout);
         var forceLayout = seriesModel.forceLayout;
-        var layoutAnimation = seriesModel.get('force.layoutAnimation');
+        var layoutAnimation = seriesModel.get(['force', 'layoutAnimation']);
         if (forceLayout) {
             this._startForceLayoutIteration(forceLayout, layoutAnimation);
         }
 
-        data.eachItemGraphicEl(function (el, idx) {
-            var itemModel = data.getItemModel(idx);
+        data.eachItemGraphicEl((el: Symbol, idx) => {
+            var itemModel = data.getItemModel(idx) as Model<GraphNodeItemOption>;
             // Update draggable
             el.off('drag').off('dragend');
             var draggable = itemModel.get('draggable');
             if (draggable) {
-                el.on('drag', function () {
+                el.on('drag', () => {
                     if (forceLayout) {
                         forceLayout.warmUp();
                         !this._layouting
@@ -153,19 +203,19 @@ export default echarts.extendChartView({
                         // Write position back to layout
                         data.setItemLayout(idx, el.position);
                     }
-                }, this).on('dragend', function () {
+                }).on('dragend', () => {
                     if (forceLayout) {
                         forceLayout.setUnfixed(idx);
                     }
-                }, this);
+                });
             }
-            el.setDraggable(draggable && forceLayout);
+            el.setDraggable(draggable && !!forceLayout);
 
-            el[FOCUS_ADJACENCY] && el.off('mouseover', el[FOCUS_ADJACENCY]);
-            el[UNFOCUS_ADJACENCY] && el.off('mouseout', el[UNFOCUS_ADJACENCY]);
+            (el as any)[FOCUS_ADJACENCY] && el.off('mouseover', (el as any)[FOCUS_ADJACENCY]);
+            (el as any)[UNFOCUS_ADJACENCY] && el.off('mouseout', (el as any)[UNFOCUS_ADJACENCY]);
 
             if (itemModel.get('focusNodeAdjacency')) {
-                el.on('mouseover', el[FOCUS_ADJACENCY] = function () {
+                el.on('mouseover', (el as any)[FOCUS_ADJACENCY] = function () {
                     graphView._clearTimer();
                     api.dispatchAction({
                         type: 'focusNodeAdjacency',
@@ -173,21 +223,21 @@ export default echarts.extendChartView({
                         dataIndex: el.dataIndex
                     });
                 });
-                el.on('mouseout', el[UNFOCUS_ADJACENCY] = function () {
+                el.on('mouseout', (el as any)[UNFOCUS_ADJACENCY] = function () {
                     graphView._dispatchUnfocus(api);
                 });
             }
 
-        }, this);
+        });
 
         data.graph.eachEdge(function (edge) {
             var el = edge.getGraphicEl();
 
-            el[FOCUS_ADJACENCY] && el.off('mouseover', el[FOCUS_ADJACENCY]);
-            el[UNFOCUS_ADJACENCY] && el.off('mouseout', el[UNFOCUS_ADJACENCY]);
+            (el as any)[FOCUS_ADJACENCY] && el.off('mouseover', (el as any)[FOCUS_ADJACENCY]);
+            (el as any)[UNFOCUS_ADJACENCY] && el.off('mouseout', (el as any)[UNFOCUS_ADJACENCY]);
 
             if (edge.getModel().get('focusNodeAdjacency')) {
-                el.on('mouseover', el[FOCUS_ADJACENCY] = function () {
+                el.on('mouseover', (el as any)[FOCUS_ADJACENCY] = function () {
                     graphView._clearTimer();
                     api.dispatchAction({
                         type: 'focusNodeAdjacency',
@@ -195,17 +245,17 @@ export default echarts.extendChartView({
                         edgeDataIndex: edge.dataIndex
                     });
                 });
-                el.on('mouseout', el[UNFOCUS_ADJACENCY] = function () {
+                el.on('mouseout', (el as any)[UNFOCUS_ADJACENCY] = function () {
                     graphView._dispatchUnfocus(api);
                 });
             }
         });
 
         var circularRotateLabel = seriesModel.get('layout') === 'circular'
-            && seriesModel.get('circular.rotateLabel');
+            && seriesModel.get(['circular', 'rotateLabel']);
         var cx = data.getLayout('cx');
         var cy = data.getLayout('cy');
-        data.eachItemGraphicEl(function (el, idx) {
+        data.eachItemGraphicEl(function (el: Symbol, idx) {
             var itemModel = data.getItemModel(idx);
             var labelRotate = itemModel.get('label.rotate') || 0;
             var symbolPath = el.getSymbolPath();
@@ -243,15 +293,15 @@ export default echarts.extendChartView({
         });
 
         this._firstRender = false;
-    },
+    }
 
-    dispose: function () {
+    dispose() {
         this._controller && this._controller.dispose();
-        this._controllerHost = {};
+        this._controllerHost = null;
         this._clearTimer();
-    },
+    }
 
-    _dispatchUnfocus: function (api, opt) {
+    _dispatchUnfocus(api: ExtensionAPI) {
         var self = this;
         this._clearTimer();
         this._unfocusDelayTimer = setTimeout(function () {
@@ -260,18 +310,23 @@ export default echarts.extendChartView({
                 type: 'unfocusNodeAdjacency',
                 seriesId: self._model.id
             });
-        }, 500);
+        }, 500) as any;
 
-    },
+    }
 
-    _clearTimer: function () {
+    _clearTimer() {
         if (this._unfocusDelayTimer) {
             clearTimeout(this._unfocusDelayTimer);
             this._unfocusDelayTimer = null;
         }
-    },
+    }
 
-    focusNodeAdjacency: function (seriesModel, ecModel, api, payload) {
+    focusNodeAdjacency(
+        seriesModel: GraphSeriesModel,
+        ecModel: GlobalModel,
+        api: ExtensionAPI,
+        payload: FocusNodePayload
+    ) {
         var data = seriesModel.getData();
         var graph = data.graph;
         var dataIndex = payload.dataIndex;
@@ -307,9 +362,11 @@ export default echarts.extendChartView({
             fadeInItem(edge.node1, nodeOpacityPath);
             fadeInItem(edge.node2, nodeOpacityPath);
         }
-    },
+    }
 
-    unfocusNodeAdjacency: function (seriesModel, ecModel, api, payload) {
+    unfocusNodeAdjacency(
+        seriesModel: GraphSeriesModel
+    ) {
         var graph = seriesModel.getData().graph;
 
         graph.eachNode(function (node) {
@@ -318,23 +375,30 @@ export default echarts.extendChartView({
         graph.eachEdge(function (edge) {
             fadeOutItem(edge, lineOpacityPath);
         });
-    },
+    }
 
-    _startForceLayoutIteration: function (forceLayout, layoutAnimation) {
+    _startForceLayoutIteration(
+        forceLayout: GraphSeriesModel['forceLayout'],
+        layoutAnimation?: boolean
+    ) {
         var self = this;
         (function step() {
             forceLayout.step(function (stopped) {
                 self.updateLayout(self._model);
                 (self._layouting = !stopped) && (
                     layoutAnimation
-                        ? (self._layoutTimeout = setTimeout(step, 16))
+                        ? (self._layoutTimeout = setTimeout(step, 16) as any)
                         : step()
                 );
             });
         })();
-    },
+    }
 
-    _updateController: function (seriesModel, ecModel, api) {
+    _updateController(
+        seriesModel: GraphSeriesModel,
+        ecModel: GlobalModel,
+        api: ExtensionAPI
+    ) {
         var controller = this._controller;
         var controllerHost = this._controllerHost;
         var group = this.group;
@@ -346,7 +410,7 @@ export default echarts.extendChartView({
                 && !onIrrelevantElement(e, api, seriesModel);
         });
 
-        if (seriesModel.coordinateSystem.type !== 'view') {
+        if (!isViewCoordSys(seriesModel.coordinateSystem)) {
             controller.disable();
             return;
         }
@@ -357,7 +421,7 @@ export default echarts.extendChartView({
         controller
             .off('pan')
             .off('zoom')
-            .on('pan', function (e) {
+            .on('pan', (e) => {
                 roamHelper.updateViewOnPan(controllerHost, e.dx, e.dy);
                 api.dispatchAction({
                     seriesId: seriesModel.id,
@@ -366,7 +430,7 @@ export default echarts.extendChartView({
                     dy: e.dy
                 });
             })
-            .on('zoom', function (e) {
+            .on('zoom', (e) => {
                 roamHelper.updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);
                 api.dispatchAction({
                     seriesId: seriesModel.id,
@@ -378,10 +442,10 @@ export default echarts.extendChartView({
                 this._updateNodeAndLinkScale();
                 adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel));
                 this._lineDraw.updateLayout();
-            }, this);
-    },
+            });
+    }
 
-    _updateNodeAndLinkScale: function () {
+    _updateNodeAndLinkScale() {
         var seriesModel = this._model;
         var data = seriesModel.getData();
 
@@ -391,17 +455,21 @@ export default echarts.extendChartView({
         data.eachItemGraphicEl(function (el, idx) {
             el.attr('scale', invScale);
         });
-    },
+    }
 
-    updateLayout: function (seriesModel) {
+    updateLayout(seriesModel: GraphSeriesModel) {
         adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel));
 
         this._symbolDraw.updateLayout();
         this._lineDraw.updateLayout();
-    },
+    }
 
-    remove: function (ecModel, api) {
+    remove(ecModel: GlobalModel, api: ExtensionAPI) {
         this._symbolDraw && this._symbolDraw.remove();
         this._lineDraw && this._lineDraw.remove();
     }
-});
+}
+
+ChartView.registerClass(GraphView);
+
+export default GraphView;
\ No newline at end of file
diff --git a/src/chart/graph/adjustEdge.ts b/src/chart/graph/adjustEdge.ts
index 8c0588d..b8fa14e 100644
--- a/src/chart/graph/adjustEdge.ts
+++ b/src/chart/graph/adjustEdge.ts
@@ -17,19 +17,22 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as curveTool from 'zrender/src/core/curve';
 import * as vec2 from 'zrender/src/core/vector';
 import {getSymbolSize} from './graphHelper';
+import Graph from '../../data/Graph';
 
-var v1 = [];
-var v2 = [];
-var v3 = [];
+var v1: number[] = [];
+var v2: number[] = [];
+var v3: number[] = [];
 var quadraticAt = curveTool.quadraticAt;
 var v2DistSquare = vec2.distSquare;
 var mathAbs = Math.abs;
-function intersectCurveCircle(curvePoints, center, radius) {
+function intersectCurveCircle(
+    curvePoints: number[][],
+    center: number[],
+    radius: number
+) {
     var p0 = curvePoints[0];
     var p1 = curvePoints[1];
     var p2 = curvePoints[2];
@@ -92,12 +95,12 @@ function intersectCurveCircle(curvePoints, center, radius) {
 }
 
 // Adjust edge to avoid
-export default function (graph, scale) {
-    var tmp0 = [];
+export default function (graph: Graph, scale: number) {
+    var tmp0: number[] = [];
     var quadraticSubdivide = curveTool.quadraticSubdivide;
-    var pts = [[], [], []];
-    var pts2 = [[], []];
-    var v = [];
+    var pts: number[][] = [[], [], []];
+    var pts2: number[][] = [[], []];
+    var v: number[] = [];
     scale /= 2;
 
     graph.eachEdge(function (edge, idx) {
diff --git a/src/chart/graph/backwardCompat.ts b/src/chart/graph/backwardCompat.ts
deleted file mode 100644
index 56c6a92..0000000
--- a/src/chart/graph/backwardCompat.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one
-* or more contributor license agreements.  See the NOTICE file
-* distributed with this work for additional information
-* regarding copyright ownership.  The ASF licenses this file
-* to you under the Apache License, Version 2.0 (the
-* "License"); you may not use this file except in compliance
-* with the License.  You may obtain a copy of the License at
-*
-*   http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing,
-* software distributed under the License is distributed on an
-* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-* KIND, either express or implied.  See the License for the
-* specific language governing permissions and limitations
-* under the License.
-*/
-
diff --git a/src/chart/graph/categoryFilter.ts b/src/chart/graph/categoryFilter.ts
index 836a34d..66b6b4e 100644
--- a/src/chart/graph/categoryFilter.ts
+++ b/src/chart/graph/categoryFilter.ts
@@ -17,16 +17,18 @@
 * under the License.
 */
 
-// @ts-nocheck
+import GlobalModel from '../../model/Global';
+import GraphSeriesModel from './GraphSeries';
+import type LegendModel from '../../component/legend/LegendModel';
 
-export default function (ecModel) {
+export default function (ecModel: GlobalModel) {
     var legendModels = ecModel.findComponents({
         mainType: 'legend'
-    });
+    }) as LegendModel[];
     if (!legendModels || !legendModels.length) {
         return;
     }
-    ecModel.eachSeriesByType('graph', function (graphSeries) {
+    ecModel.eachSeriesByType('graph', function (graphSeries: GraphSeriesModel) {
         var categoriesData = graphSeries.getCategoriesData();
         var graph = graphSeries.getGraph();
         var data = graph.data;
@@ -49,5 +51,5 @@ export default function (ecModel) {
             }
             return true;
         });
-    }, this);
+    });
 }
diff --git a/src/chart/graph/categoryVisual.ts b/src/chart/graph/categoryVisual.ts
index 31bec52..2ee40b1 100644
--- a/src/chart/graph/categoryVisual.ts
+++ b/src/chart/graph/categoryVisual.ts
@@ -17,16 +17,18 @@
 * under the License.
 */
 
-// @ts-nocheck
+import GlobalModel from '../../model/Global';
+import GraphSeriesModel from './GraphSeries';
+import { Dictionary, ColorString } from '../../util/types';
 
-export default function (ecModel) {
+export default function (ecModel: GlobalModel) {
 
-    var paletteScope = {};
-    ecModel.eachSeriesByType('graph', function (seriesModel) {
+    var paletteScope: Dictionary<ColorString> = {};
+    ecModel.eachSeriesByType('graph', function (seriesModel: GraphSeriesModel) {
         var categoriesData = seriesModel.getCategoriesData();
         var data = seriesModel.getData();
 
-        var categoryNameIdxMap = {};
+        var categoryNameIdxMap: Dictionary<number> = {};
 
         categoriesData.each(function (idx) {
             var name = categoriesData.getName(idx);
diff --git a/src/chart/graph/circularLayout.ts b/src/chart/graph/circularLayout.ts
index 5434728..8f1712a 100644
--- a/src/chart/graph/circularLayout.ts
+++ b/src/chart/graph/circularLayout.ts
@@ -17,12 +17,12 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import {circularLayout} from './circularLayoutHelper';
+import GlobalModel from '../../model/Global';
+import GraphSeriesModel from './GraphSeries';
 
-export default function (ecModel) {
-    ecModel.eachSeriesByType('graph', function (seriesModel) {
+export default function (ecModel: GlobalModel) {
+    ecModel.eachSeriesByType('graph', function (seriesModel: GraphSeriesModel) {
         if (seriesModel.get('layout') === 'circular') {
             circularLayout(seriesModel, 'symbolSize');
         }
diff --git a/src/chart/graph/circularLayoutHelper.ts b/src/chart/graph/circularLayoutHelper.ts
index 8d72c96..4919057 100644
--- a/src/chart/graph/circularLayoutHelper.ts
+++ b/src/chart/graph/circularLayoutHelper.ts
@@ -17,14 +17,16 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import * as vec2 from 'zrender/src/core/vector';
 import {getSymbolSize, getNodeGlobalScale} from './graphHelper';
+import GraphSeriesModel from './GraphSeries';
+import Graph from '../../data/Graph';
+import List from '../../data/List';
 
 var PI = Math.PI;
 
-var _symbolRadiansHalf = [];
+var _symbolRadiansHalf: number[] = [];
 
 /**
  * `basedOn` can be:
@@ -44,11 +46,11 @@ var _symbolRadiansHalf = [];
  *     FIXME
  *     If progressive rendering is applied to graph some day,
  *     probably we have to use `basedOn: 'value'`.
- *
- * @param {module:echarts/src/model/Series} seriesModel
- * @param {string} basedOn 'value' or 'symbolSize'
  */
-export function circularLayout(seriesModel, basedOn) {
+export function circularLayout(
+    seriesModel: GraphSeriesModel,
+    basedOn: 'value' | 'symbolSize'
+) {
     var coordSys = seriesModel.coordinateSystem;
     if (coordSys && coordSys.type !== 'view') {
         return;
@@ -73,7 +75,7 @@ export function circularLayout(seriesModel, basedOn) {
         return;
     }
 
-    _layoutNodesBasedOn[basedOn](seriesModel, coordSys, graph, nodeData, r, cx, cy, count);
+    _layoutNodesBasedOn[basedOn](seriesModel, graph, nodeData, r, cx, cy, count);
 
     graph.eachEdge(function (edge) {
         var curveness = edge.getModel().get('lineStyle.curveness') || 0;
@@ -93,15 +95,27 @@ export function circularLayout(seriesModel, basedOn) {
     });
 }
 
-var _layoutNodesBasedOn = {
+interface LayoutNode {
+    (
+        seriesModel: GraphSeriesModel,
+        graph: Graph,
+        nodeData: List,
+        r: number,
+        cx: number,
+        cy: number,
+        count: number
+    ): void
+}
+
+var _layoutNodesBasedOn: Record<'value' | 'symbolSize', LayoutNode> = {
 
-    value: function (seriesModel, coordSys, graph, nodeData, r, cx, cy, count) {
+    value(seriesModel, graph, nodeData, r, cx, cy, count) {
         var angle = 0;
         var sum = nodeData.getSum('value');
         var unitAngle = Math.PI * 2 / (sum || count);
 
         graph.eachNode(function (node) {
-            var value = node.getValue('value');
+            var value = node.getValue('value') as number;
             var radianHalf = unitAngle * (sum ? value : 1) / 2;
 
             angle += radianHalf;
@@ -113,7 +127,7 @@ var _layoutNodesBasedOn = {
         });
     },
 
-    symbolSize: function (seriesModel, coordSys, graph, nodeData, r, cx, cy, count) {
+    symbolSize(seriesModel, graph, nodeData, r, cx, cy, count) {
         var sumRadian = 0;
         _symbolRadiansHalf.length = count;
 
diff --git a/src/chart/graph/createView.ts b/src/chart/graph/createView.ts
index a251a95..ceaa7ab 100644
--- a/src/chart/graph/createView.ts
+++ b/src/chart/graph/createView.ts
@@ -17,25 +17,28 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 // FIXME Where to create the simple view coordinate system
 import View from '../../coord/View';
 import {getLayoutRect} from '../../util/layout';
 import * as bbox from 'zrender/src/core/bbox';
+import GraphSeriesModel from './GraphSeries';
+import ExtensionAPI from '../../ExtensionAPI';
+import GlobalModel from '../../model/Global';
+import { extend } from 'zrender/src/core/util';
 
-function getViewRect(seriesModel, api, aspect) {
-    var option = seriesModel.getBoxLayoutParams();
-    option.aspect = aspect;
+function getViewRect(seriesModel: GraphSeriesModel, api: ExtensionAPI, aspect: number) {
+    var option = extend(seriesModel.getBoxLayoutParams(), {
+        aspect: aspect
+    });
     return getLayoutRect(option, {
         width: api.getWidth(),
         height: api.getHeight()
     });
 }
 
-export default function (ecModel, api) {
-    var viewList = [];
-    ecModel.eachSeriesByType('graph', function (seriesModel) {
+export default function (ecModel: GlobalModel, api: ExtensionAPI) {
+    var viewList: View[] = [];
+    ecModel.eachSeriesByType('graph', function (seriesModel: GraphSeriesModel) {
         var coordSysType = seriesModel.get('coordinateSystem');
         if (!coordSysType || coordSysType === 'view') {
 
@@ -45,8 +48,8 @@ export default function (ecModel, api) {
                 return [+itemModel.get('x'), +itemModel.get('y')];
             });
 
-            var min = [];
-            var max = [];
+            var min: number[] = [];
+            var max: number[] = [];
 
             bbox.fromPoints(positions, min, max);
 
diff --git a/src/chart/graph/edgeVisual.ts b/src/chart/graph/edgeVisual.ts
index d2d430c..e5fb1d7 100644
--- a/src/chart/graph/edgeVisual.ts
+++ b/src/chart/graph/edgeVisual.ts
@@ -17,24 +17,25 @@
 * under the License.
 */
 
-// @ts-nocheck
+import GlobalModel from '../../model/Global';
+import GraphSeriesModel from './GraphSeries';
 
-function normalize(a) {
+function normalize(a: string | number | (string | number)[]): (string | number)[] {
     if (!(a instanceof Array)) {
         a = [a, a];
     }
     return a;
 }
 
-export default function (ecModel) {
-    ecModel.eachSeriesByType('graph', function (seriesModel) {
+export default function (ecModel: GlobalModel) {
+    ecModel.eachSeriesByType('graph', function (seriesModel: GraphSeriesModel) {
         var graph = seriesModel.getGraph();
         var edgeData = seriesModel.getEdgeData();
         var symbolType = normalize(seriesModel.get('edgeSymbol'));
         var symbolSize = normalize(seriesModel.get('edgeSymbolSize'));
 
-        var colorQuery = 'lineStyle.color'.split('.');
-        var opacityQuery = 'lineStyle.opacity'.split('.');
+        var colorQuery = ['lineStyle', 'color'] as const;
+        var opacityQuery = ['lineStyle', 'opacity'] as const;
 
         edgeData.setVisual('fromSymbol', symbolType && symbolType[0]);
         edgeData.setVisual('toSymbol', symbolType && symbolType[1]);
diff --git a/src/chart/graph/forceHelper.ts b/src/chart/graph/forceHelper.ts
index 96d3b00..f8703c5 100644
--- a/src/chart/graph/forceHelper.ts
+++ b/src/chart/graph/forceHelper.ts
@@ -17,8 +17,6 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 /*
 * A third-party license is embeded for some of the code in this file:
 * Some formulas were originally copied from "d3.js" with some
@@ -30,14 +28,56 @@
 */
 
 import * as vec2 from 'zrender/src/core/vector';
+import { RectLike } from 'zrender/src/core/BoundingRect';
 
 var scaleAndAdd = vec2.scaleAndAdd;
 
+interface InputNode {
+    p?: vec2.VectorArray
+    fixed?: boolean
+    /**
+     * Weight
+     */
+    w: number
+    /**
+     * Repulsion
+     */
+    rep: number
+}
+interface LayoutNode extends InputNode {
+    pp?: vec2.VectorArray
+    edges?: LayoutEdge[]
+}
+interface InputEdge {
+    ignoreForceLayout?: boolean
+    n1: InputNode
+    n2: InputNode
+
+    /**
+     * Distance
+     */
+    d: number
+}
+interface LayoutEdge extends InputEdge {
+    n1: LayoutNode
+    n2: LayoutNode
+}
+interface LayoutCfg {
+    gravity?: number
+    friction?: number
+    rect?: RectLike
+}
 // function adjacentNode(n, e) {
 //     return e.n1 === n ? e.n2 : e.n1;
 // }
 
-export function forceLayout(nodes, edges, opts) {
+export function forceLayout<N extends InputNode, E extends InputEdge>(
+    inNodes: N[],
+    inEdges: E[],
+    opts: LayoutCfg
+) {
+    var nodes = inNodes as LayoutNode[];
+    var edges = inEdges as LayoutEdge[];
     var rect = opts.rect;
     var width = rect.width;
     var height = rect.height;
@@ -56,7 +96,7 @@ export function forceLayout(nodes, edges, opts) {
     // }
     // Init position
     for (var i = 0; i < nodes.length; i++) {
-        var n = nodes[i];
+        var n = nodes[i] as LayoutNode;
         if (!n.p) {
             n.p = vec2.create(
                 width * (Math.random() - 0.5) + center[0],
@@ -74,27 +114,45 @@ export function forceLayout(nodes, edges, opts) {
     var initialFriction = opts.friction == null ? 0.6 : opts.friction;
     var friction = initialFriction;
 
+    var beforeStepCallback: (nodes: N[], edges: E[]) => void;
+    var afterStepCallback: (nodes: N[], edges: E[], finished: boolean) => void;
+
     return {
         warmUp: function () {
             friction = initialFriction * 0.8;
         },
 
-        setFixed: function (idx) {
+        setFixed: function (idx: number) {
             nodes[idx].fixed = true;
         },
 
-        setUnfixed: function (idx) {
+        setUnfixed: function (idx: number) {
             nodes[idx].fixed = false;
         },
 
         /**
+         * Before step hook
+         */
+        beforeStep: function (cb: typeof beforeStepCallback) {
+            beforeStepCallback = cb;
+        },
+        /**
+         * After step hook
+         */
+        afterStep: function (cb: typeof afterStepCallback) {
+            afterStepCallback = cb;
+        },
+
+        /**
          * Some formulas were originally copied from "d3.js"
          * https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/layout/force.js
          * with some modifications made for this project.
          * See the license statement at the head of this file.
          */
-        step: function (cb) {
-            var v12 = [];
+        step: function (cb?: (finished: boolean) => void) {
+            beforeStepCallback && beforeStepCallback(nodes as N[], edges as E[]);
+
+            var v12: number[] = [];
             var nLen = nodes.length;
             for (var i = 0; i < edges.length; i++) {
                 var e = edges[i];
@@ -147,7 +205,7 @@ export function forceLayout(nodes, edges, opts) {
                     !n2.fixed && scaleAndAdd(n2.pp, n2.pp, v12, -repFact);
                 }
             }
-            var v = [];
+            var v: number[] = [];
             for (var i = 0; i < nLen; i++) {
                 var n = nodes[i];
                 if (!n.fixed) {
@@ -159,7 +217,11 @@ export function forceLayout(nodes, edges, opts) {
 
             friction = friction * 0.992;
 
-            cb && cb(nodes, edges, friction < 0.01);
+            const finished = friction < 0.01;
+
+            afterStepCallback && afterStepCallback(nodes as N[], edges as E[], finished);
+
+            cb && cb(finished);
         }
     };
 }
diff --git a/src/chart/graph/forceLayout.ts b/src/chart/graph/forceLayout.ts
index 30edaa6..e7501ee 100644
--- a/src/chart/graph/forceLayout.ts
+++ b/src/chart/graph/forceLayout.ts
@@ -17,17 +17,25 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import {forceLayout} from './forceHelper';
 import {simpleLayout} from './simpleLayoutHelper';
 import {circularLayout} from './circularLayoutHelper';
 import {linearMap} from '../../util/number';
 import * as vec2 from 'zrender/src/core/vector';
 import * as zrUtil from 'zrender/src/core/util';
+import GlobalModel from '../../model/Global';
+import GraphSeriesModel from './GraphSeries';
+
+export interface ForceLayoutInstance {
+    step(cb: (stopped: boolean) => void): void
+    warmUp(): void
+    setFixed(idx: number): void
+    setUnfixed(idx: number): void
+}
+
 
-export default function (ecModel) {
-    ecModel.eachSeriesByType('graph', function (graphSeries) {
+export default function (ecModel: GlobalModel) {
+    ecModel.eachSeriesByType('graph', function (graphSeries: GraphSeriesModel) {
         var coordSys = graphSeries.coordinateSystem;
         if (coordSys && coordSys.type !== 'view') {
             return;
@@ -57,20 +65,19 @@ export default function (ecModel) {
             // var edgeDataExtent = edgeData.getDataExtent('value');
             var repulsion = forceModel.get('repulsion');
             var edgeLength = forceModel.get('edgeLength');
-            if (!zrUtil.isArray(repulsion)) {
-                repulsion = [repulsion, repulsion];
-            }
-            if (!zrUtil.isArray(edgeLength)) {
-                edgeLength = [edgeLength, edgeLength];
-            }
+            var repulsionArr = zrUtil.isArray(repulsion)
+                ? repulsion : [repulsion, repulsion];
+            var edgeLengthArr = zrUtil.isArray(edgeLength)
+                ? edgeLength : [edgeLength, edgeLength];
+
             // Larger value has smaller length
-            edgeLength = [edgeLength[1], edgeLength[0]];
+            edgeLengthArr = [edgeLengthArr[1], edgeLengthArr[0]];
 
-            var nodes = nodeData.mapArray('value', function (value, idx) {
-                var point = nodeData.getItemLayout(idx);
-                var rep = linearMap(value, nodeDataExtent, repulsion);
+            var nodes = nodeData.mapArray('value', function (value: number, idx) {
+                var point = nodeData.getItemLayout(idx) as number[];
+                var rep = linearMap(value, nodeDataExtent, repulsionArr);
                 if (isNaN(rep)) {
-                    rep = (repulsion[0] + repulsion[1]) / 2;
+                    rep = (repulsionArr[0] + repulsionArr[1]) / 2;
                 }
                 return {
                     w: rep,
@@ -79,11 +86,11 @@ export default function (ecModel) {
                     p: (!point || isNaN(point[0]) || isNaN(point[1])) ? null : point
                 };
             });
-            var edges = edgeData.mapArray('value', function (value, idx) {
+            var edges = edgeData.mapArray('value', function (value: number, idx) {
                 var edge = graph.getEdgeByIndex(idx);
-                var d = linearMap(value, edgeDataExtent, edgeLength);
+                var d = linearMap(value, edgeDataExtent, edgeLengthArr);
                 if (isNaN(d)) {
-                    d = (edgeLength[0] + edgeLength[1]) / 2;
+                    d = (edgeLengthArr[0] + edgeLengthArr[1]) / 2;
                 }
                 var edgeModel = edge.getModel();
                 return {
@@ -102,45 +109,44 @@ export default function (ecModel) {
                 gravity: forceModel.get('gravity'),
                 friction: forceModel.get('friction')
             });
-            var oldStep = forceInstance.step;
-            forceInstance.step = function (cb) {
+            forceInstance.beforeStep(function (nodes, edges) {
                 for (var i = 0, l = nodes.length; i < l; i++) {
                     if (nodes[i].fixed) {
                         // Write back to layout instance
-                        vec2.copy(nodes[i].p, graph.getNodeByIndex(i).getLayout());
+                        vec2.copy(
+                            nodes[i].p,
+                            graph.getNodeByIndex(i).getLayout() as number[]
+                        );
                     }
                 }
-                oldStep(function (nodes, edges, stopped) {
-                    for (var i = 0, l = nodes.length; i < l; i++) {
-                        if (!nodes[i].fixed) {
-                            graph.getNodeByIndex(i).setLayout(nodes[i].p);
-                        }
-                        preservedPoints[nodeData.getId(i)] = nodes[i].p;
+            });
+            forceInstance.afterStep(function (nodes, edges, stopped) {
+                for (var i = 0, l = nodes.length; i < l; i++) {
+                    if (!nodes[i].fixed) {
+                        graph.getNodeByIndex(i).setLayout(nodes[i].p);
                     }
-                    for (var i = 0, l = edges.length; i < l; i++) {
-                        var e = edges[i];
-                        var edge = graph.getEdgeByIndex(i);
-                        var p1 = e.n1.p;
-                        var p2 = e.n2.p;
-                        var points = edge.getLayout();
-                        points = points ? points.slice() : [];
-                        points[0] = points[0] || [];
-                        points[1] = points[1] || [];
-                        vec2.copy(points[0], p1);
-                        vec2.copy(points[1], p2);
-                        if (+e.curveness) {
-                            points[2] = [
-                                (p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * e.curveness,
-                                (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * e.curveness
-                            ];
-                        }
-                        edge.setLayout(points);
+                    preservedPoints[nodeData.getId(i)] = nodes[i].p;
+                }
+                for (var i = 0, l = edges.length; i < l; i++) {
+                    var e = edges[i];
+                    var edge = graph.getEdgeByIndex(i);
+                    var p1 = e.n1.p;
+                    var p2 = e.n2.p;
+                    var points = edge.getLayout() as number[][];
+                    points = points ? points.slice() : [];
+                    points[0] = points[0] || [];
+                    points[1] = points[1] || [];
+                    vec2.copy(points[0], p1);
+                    vec2.copy(points[1], p2);
+                    if (+e.curveness) {
+                        points[2] = [
+                            (p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * e.curveness,
+                            (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * e.curveness
+                        ];
                     }
-                    // Update layout
-
-                    cb && cb(stopped);
-                });
-            };
+                    edge.setLayout(points);
+                }
+            });
             graphSeries.forceLayout = forceInstance;
             graphSeries.preservedPoints = preservedPoints;
 
diff --git a/src/chart/graph/graphAction.ts b/src/chart/graph/graphAction.ts
index 5d2a129..5ef560d 100644
--- a/src/chart/graph/graphAction.ts
+++ b/src/chart/graph/graphAction.ts
@@ -17,11 +17,12 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as echarts from '../../echarts';
-import {updateCenterAndZoom} from '../../action/roamHelper';
+import {updateCenterAndZoom, RoamPaylod} from '../../action/roamHelper';
 import '../helper/focusNodeAdjacencyAction';
+import GlobalModel from '../../model/Global';
+import GraphSeriesModel from './GraphSeries';
+import View from '../../coord/View';
 
 var actionInfo = {
     type: 'graphRoam',
@@ -38,9 +39,11 @@ var actionInfo = {
  * @property {number} [originX]
  * @property {number} [originY]
  */
-echarts.registerAction(actionInfo, function (payload, ecModel) {
-    ecModel.eachComponent({mainType: 'series', query: payload}, function (seriesModel) {
-        var coordSys = seriesModel.coordinateSystem;
+echarts.registerAction(actionInfo, function (payload: RoamPaylod, ecModel: GlobalModel) {
+    ecModel.eachComponent({
+        mainType: 'series', query: payload
+    }, function (seriesModel: GraphSeriesModel) {
+        var coordSys = seriesModel.coordinateSystem as View;
 
         var res = updateCenterAndZoom(coordSys, payload);
 
diff --git a/src/chart/graph/graphHelper.ts b/src/chart/graph/graphHelper.ts
index 47ed84a..6075987 100644
--- a/src/chart/graph/graphHelper.ts
+++ b/src/chart/graph/graphHelper.ts
@@ -17,10 +17,12 @@
 * under the License.
 */
 
-// @ts-nocheck
+import GraphSeriesModel from './GraphSeries';
+import View from '../../coord/View';
+import { GraphNode } from '../../data/Graph';
 
-export function getNodeGlobalScale(seriesModel) {
-    var coordSys = seriesModel.coordinateSystem;
+export function getNodeGlobalScale(seriesModel: GraphSeriesModel) {
+    var coordSys = seriesModel.coordinateSystem as View;
     if (coordSys.type !== 'view') {
         return 1;
     }
@@ -36,7 +38,7 @@ export function getNodeGlobalScale(seriesModel) {
     return nodeScale / groupZoom;
 }
 
-export function getSymbolSize(node) {
+export function getSymbolSize(node: GraphNode) {
     var symbolSize = node.getVisual('symbolSize');
     if (symbolSize instanceof Array) {
         symbolSize = (symbolSize[0] + symbolSize[1]) / 2;
diff --git a/src/chart/graph/simpleLayout.ts b/src/chart/graph/simpleLayout.ts
index b596a38..353a734 100644
--- a/src/chart/graph/simpleLayout.ts
+++ b/src/chart/graph/simpleLayout.ts
@@ -17,19 +17,20 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import {each} from 'zrender/src/core/util';
 import {simpleLayout, simpleLayoutEdge} from './simpleLayoutHelper';
+import GlobalModel from '../../model/Global';
+import ExtensionAPI from '../../ExtensionAPI';
+import GraphSeriesModel from './GraphSeries';
 
-export default function (ecModel, api) {
-    ecModel.eachSeriesByType('graph', function (seriesModel) {
+export default function (ecModel: GlobalModel, api: ExtensionAPI) {
+    ecModel.eachSeriesByType('graph', function (seriesModel: GraphSeriesModel) {
         var layout = seriesModel.get('layout');
         var coordSys = seriesModel.coordinateSystem;
         if (coordSys && coordSys.type !== 'view') {
             var data = seriesModel.getData();
 
-            var dimensions = [];
+            var dimensions: string[] = [];
             each(coordSys.dimensions, function (coordDim) {
                 dimensions = dimensions.concat(data.mapDimension(coordDim, true));
             });
@@ -38,7 +39,7 @@ export default function (ecModel, api) {
                 var value = [];
                 var hasValue = false;
                 for (var i = 0; i < dimensions.length; i++) {
-                    var val = data.get(dimensions[i], dataIndex);
+                    var val = data.get(dimensions[i], dataIndex) as number;
                     if (!isNaN(val)) {
                         hasValue = true;
                     }
diff --git a/src/chart/graph/simpleLayoutHelper.ts b/src/chart/graph/simpleLayoutHelper.ts
index d56c665..9a5abcc 100644
--- a/src/chart/graph/simpleLayoutHelper.ts
+++ b/src/chart/graph/simpleLayoutHelper.ts
@@ -17,11 +17,11 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as vec2 from 'zrender/src/core/vector';
+import GraphSeriesModel from './GraphSeries';
+import Graph from '../../data/Graph';
 
-export function simpleLayout(seriesModel) {
+export function simpleLayout(seriesModel: GraphSeriesModel) {
     var coordSys = seriesModel.coordinateSystem;
     if (coordSys && coordSys.type !== 'view') {
         return;
@@ -36,7 +36,7 @@ export function simpleLayout(seriesModel) {
     simpleLayoutEdge(graph);
 }
 
-export function simpleLayoutEdge(graph) {
+export function simpleLayoutEdge(graph: Graph) {
     graph.eachEdge(function (edge) {
         var curveness = edge.getModel().get('lineStyle.curveness') || 0;
         var p1 = vec2.clone(edge.node1.getLayout());
diff --git a/src/chart/heatmap/HeatmapSeries.ts b/src/chart/heatmap/HeatmapSeries.ts
index c776cc3..2f44a13 100644
--- a/src/chart/heatmap/HeatmapSeries.ts
+++ b/src/chart/heatmap/HeatmapSeries.ts
@@ -92,7 +92,7 @@ class HeatmapSeriesModel extends SeriesModel<HeatmapSeriesOption> {
         }
     }
 
-    defaultOption: HeatmapSeriesOption = {
+    static defaultOption: HeatmapSeriesOption = {
 
         coordinateSystem: 'cartesian2d',
 
diff --git a/src/chart/helper/Line.ts b/src/chart/helper/Line.ts
index 07a4bb3..969789e 100644
--- a/src/chart/helper/Line.ts
+++ b/src/chart/helper/Line.ts
@@ -17,28 +17,48 @@
 * under the License.
 */
 
-// @ts-nocheck
-
-/**
- * @module echarts/chart/helper/Line
- */
-
 import * as zrUtil from 'zrender/src/core/util';
 import * as vector from 'zrender/src/core/vector';
 import * as symbolUtil from '../../util/symbol';
-import LinePath from './LinePath';
+import ECLinePath from './LinePath';
 import * as graphic from '../../util/graphic';
 import {round} from '../../util/number';
+import List from '../../data/List';
+import { StyleProps } from 'zrender/src/graphic/Style';
+import { LineLabelOption, ZRTextAlign, ZRTextVerticalAlign } from '../../util/types';
+import Model from '../../model/Model';
+import SeriesModel from '../../model/Series';
+
+var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol'] as const;
+
+type ECSymbol = ReturnType<typeof createSymbol>
+
+export interface LineLabel extends graphic.Text {
+    lineLabelOriginalOpacity: number
+}
+
+interface InnerLineLabel extends LineLabel {
+    __textAlign: StyleProps['textAlign']
+    __verticalAlign: StyleProps['textVerticalAlign']
+    __position: StyleProps['textPosition']
+    __labelDistance: number[]
+}
+
+function makeSymbolTypeKey(symbolCategory: string) {
+    return '_' + symbolCategory + 'Type' as '_fromSymbolType' | '_toSymbolType';
+}
 
-var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol'];
+export interface SeriesScope {
+    lineStyle?: StyleProps
+    hoverLineStyle?: StyleProps
 
-function makeSymbolTypeKey(symbolCategory) {
-    return '_' + symbolCategory + 'Type';
+    labelModel?: Model<LineLabelOption>
+    hoverLabelModel?: Model<LineLabelOption>
 }
 /**
  * @inner
  */
-function createSymbol(name, lineData, idx) {
+function createSymbol(name: string, lineData: List, idx: number) {
     var color = lineData.getItemVisual(idx, 'color');
     var symbolType = lineData.getItemVisual(idx, name);
     var symbolSize = lineData.getItemVisual(idx, name + 'Size');
@@ -60,8 +80,8 @@ function createSymbol(name, lineData, idx) {
     return symbolPath;
 }
 
-function createLine(points) {
-    var line = new LinePath({
+function createLine(points: number[][]) {
+    var line = new ECLinePath({
         name: 'line',
         subPixelOptimize: true
     });
@@ -69,7 +89,12 @@ function createLine(points) {
     return line;
 }
 
-function setLinePoints(targetShape, points) {
+function setLinePoints(targetShape: ECLinePath['shape'], points: number[][]) {
+    type CurveShape = ECLinePath['shape'] & {
+        cpx1: number
+        cpy1: number
+    }
+
     targetShape.x1 = points[0][0];
     targetShape.y1 = points[0][1];
     targetShape.x2 = points[1][0];
@@ -78,389 +103,382 @@ function setLinePoints(targetShape, points) {
 
     var cp1 = points[2];
     if (cp1) {
-        targetShape.cpx1 = cp1[0];
-        targetShape.cpy1 = cp1[1];
+        (targetShape as CurveShape).cpx1 = cp1[0];
+        (targetShape as CurveShape).cpy1 = cp1[1];
     }
     else {
-        targetShape.cpx1 = NaN;
-        targetShape.cpy1 = NaN;
+        (targetShape as CurveShape).cpx1 = NaN;
+        (targetShape as CurveShape).cpy1 = NaN;
     }
 }
 
-function updateSymbolAndLabelBeforeLineUpdate() {
-    var lineGroup = this;
-    var symbolFrom = lineGroup.childOfName('fromSymbol');
-    var symbolTo = lineGroup.childOfName('toSymbol');
-    var label = lineGroup.childOfName('label');
-    // Quick reject
-    if (!symbolFrom && !symbolTo && label.ignore) {
-        return;
-    }
+class Line extends graphic.Group {
 
-    var invScale = 1;
-    var parentNode = this.parent;
-    while (parentNode) {
-        if (parentNode.scale) {
-            invScale /= parentNode.scale[0];
-        }
-        parentNode = parentNode.parent;
-    }
+    private _fromSymbolType: string
+    private _toSymbolType: string
 
-    var line = lineGroup.childOfName('line');
-    // If line not changed
-    // FIXME Parent scale changed
-    if (!this.__dirty && !line.__dirty) {
-        return;
+    constructor(lineData: List, idx: number, seriesScope?: SeriesScope) {
+        super();
+        this._createLine(lineData, idx, seriesScope);
     }
 
-    var percent = line.shape.percent;
-    var fromPos = line.pointAt(0);
-    var toPos = line.pointAt(percent);
+    _createLine(lineData: List, idx: number, seriesScope?: SeriesScope) {
+        var seriesModel = lineData.hostModel;
+        var linePoints = lineData.getItemLayout(idx);
+        var line = createLine(linePoints);
+        line.shape.percent = 0;
+        graphic.initProps(line, {
+            shape: {
+                percent: 1
+            }
+        }, seriesModel, idx);
 
-    var d = vector.sub([], toPos, fromPos);
-    vector.normalize(d, d);
+        this.add(line);
 
-    if (symbolFrom) {
-        symbolFrom.attr('position', fromPos);
-        var tangent = line.tangentAt(0);
-        symbolFrom.attr('rotation', Math.PI / 2 - Math.atan2(
-            tangent[1], tangent[0]
-        ));
-        symbolFrom.attr('scale', [invScale * percent, invScale * percent]);
-    }
-    if (symbolTo) {
-        symbolTo.attr('position', toPos);
-        var tangent = line.tangentAt(1);
-        symbolTo.attr('rotation', -Math.PI / 2 - Math.atan2(
-            tangent[1], tangent[0]
-        ));
-        symbolTo.attr('scale', [invScale * percent, invScale * percent]);
+        var label = new graphic.Text({
+            name: 'label'
+        }) as InnerLineLabel;
+        // FIXME
+        // Temporary solution for `focusNodeAdjacency`.
+        // line label do not use the opacity of lineStyle.
+        label.lineLabelOriginalOpacity = 1;
+        this.add(label);
+
+        zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {
+            var symbol = createSymbol(symbolCategory, lineData, idx);
+            // symbols must added after line to make sure
+            // it will be updated after line#update.
+            // Or symbol position and rotation update in line#beforeUpdate will be one frame slow
+            this.add(symbol);
+            this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory);
+        }, this);
+
+        this._updateCommonStl(lineData, idx, seriesScope);
     }
 
-    if (!label.ignore) {
-        label.attr('position', toPos);
-
-        var textPosition;
-        var textAlign;
-        var textVerticalAlign;
-        var textOrigin;
-
-        var distance = label.__labelDistance;
-        var distanceX = distance[0] * invScale;
-        var distanceY = distance[1] * invScale;
-        var halfPercent = percent / 2;
-        var tangent = line.tangentAt(halfPercent);
-        var n = [tangent[1], -tangent[0]];
-        var cp = line.pointAt(halfPercent);
-        if (n[1] > 0) {
-            n[0] = -n[0];
-            n[1] = -n[1];
-        }
-        var dir = tangent[0] < 0 ? -1 : 1;
+    updateData(lineData: List, idx: number, seriesScope: SeriesScope) {
+        var seriesModel = lineData.hostModel;
 
-        if (label.__position !== 'start' && label.__position !== 'end') {
-            var rotation = -Math.atan2(tangent[1], tangent[0]);
-            if (toPos[0] < fromPos[0]) {
-                rotation = Math.PI + rotation;
-            }
-            label.attr('rotation', rotation);
-        }
+        var line = this.childOfName('line') as ECLinePath;
+        var linePoints = lineData.getItemLayout(idx);
+        var target = {
+            shape: {} as ECLinePath['shape']
+        };
 
-        var dy;
-        switch (label.__position) {
-            case 'insideStartTop':
-            case 'insideMiddleTop':
-            case 'insideEndTop':
-            case 'middle':
-                dy = -distanceY;
-                textVerticalAlign = 'bottom';
-                break;
-
-            case 'insideStartBottom':
-            case 'insideMiddleBottom':
-            case 'insideEndBottom':
-                dy = distanceY;
-                textVerticalAlign = 'top';
-                break;
-
-            default:
-                dy = 0;
-                textVerticalAlign = 'middle';
-        }
+        setLinePoints(target.shape, linePoints);
+        graphic.updateProps(line, target, seriesModel, idx);
+
+        zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {
+            var symbolType = lineData.getItemVisual(idx, symbolCategory);
+            var key = makeSymbolTypeKey(symbolCategory);
+            // Symbol changed
+            if (this[key] !== symbolType) {
+                this.remove(this.childOfName(symbolCategory));
+                var symbol = createSymbol(symbolCategory, lineData, idx);
+                this.add(symbol);
+            }
+            this[key] = symbolType;
+        }, this);
 
-        switch (label.__position) {
-            case 'end':
-                textPosition = [d[0] * distanceX + toPos[0], d[1] * distanceY + toPos[1]];
-                textAlign = d[0] > 0.8 ? 'left' : (d[0] < -0.8 ? 'right' : 'center');
-                textVerticalAlign = d[1] > 0.8 ? 'top' : (d[1] < -0.8 ? 'bottom' : 'middle');
-                break;
-
-            case 'start':
-                textPosition = [-d[0] * distanceX + fromPos[0], -d[1] * distanceY + fromPos[1]];
-                textAlign = d[0] > 0.8 ? 'right' : (d[0] < -0.8 ? 'left' : 'center');
-                textVerticalAlign = d[1] > 0.8 ? 'bottom' : (d[1] < -0.8 ? 'top' : 'middle');
-                break;
-
-            case 'insideStartTop':
-            case 'insideStart':
-            case 'insideStartBottom':
-                textPosition = [distanceX * dir + fromPos[0], fromPos[1] + dy];
-                textAlign = tangent[0] < 0 ? 'right' : 'left';
-                textOrigin = [-distanceX * dir, -dy];
-                break;
-
-            case 'insideMiddleTop':
-            case 'insideMiddle':
-            case 'insideMiddleBottom':
-            case 'middle':
-                textPosition = [cp[0], cp[1] + dy];
-                textAlign = 'center';
-                textOrigin = [0, -dy];
-                break;
-
-            case 'insideEndTop':
-            case 'insideEnd':
-            case 'insideEndBottom':
-                textPosition = [-distanceX * dir + toPos[0], toPos[1] + dy];
-                textAlign = tangent[0] >= 0 ? 'right' : 'left';
-                textOrigin = [distanceX * dir, -dy];
-                break;
-        }
+        this._updateCommonStl(lineData, idx, seriesScope);
+    };
 
-        label.attr({
-            style: {
-                // Use the user specified text align and baseline first
-                textVerticalAlign: label.__verticalAlign || textVerticalAlign,
-                textAlign: label.__textAlign || textAlign
-            },
-            position: textPosition,
-            scale: [invScale, invScale],
-            origin: textOrigin
-        });
-    }
-}
+    _updateCommonStl(lineData: List, idx: number, seriesScope?: SeriesScope) {
+        var seriesModel = lineData.hostModel as SeriesModel;
 
-/**
- * @constructor
- * @extends {module:zrender/graphic/Group}
- * @alias {module:echarts/chart/helper/Line}
- */
-function Line(lineData, idx, seriesScope) {
-    graphic.Group.call(this);
+        var line = this.childOfName('line') as ECLinePath;
 
-    this._createLine(lineData, idx, seriesScope);
-}
+        var lineStyle = seriesScope && seriesScope.lineStyle;
+        var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle;
+        var labelModel = seriesScope && seriesScope.labelModel;
+        var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel;
 
-var lineProto = Line.prototype;
+        // Optimization for large dataset
+        if (!seriesScope || lineData.hasItemOption) {
+            var itemModel = lineData.getItemModel(idx);
 
-// Update symbol position and rotation
-lineProto.beforeUpdate = updateSymbolAndLabelBeforeLineUpdate;
+            lineStyle = itemModel.getModel('lineStyle').getLineStyle();
+            hoverLineStyle = itemModel.getModel(['emphasis', 'lineStyle']).getLineStyle();
 
-lineProto._createLine = function (lineData, idx, seriesScope) {
-    var seriesModel = lineData.hostModel;
-    var linePoints = lineData.getItemLayout(idx);
-    var line = createLine(linePoints);
-    line.shape.percent = 0;
-    graphic.initProps(line, {
-        shape: {
-            percent: 1
+            labelModel = itemModel.getModel('label');
+            hoverLabelModel = itemModel.getModel(['emphasis', 'label']);
         }
-    }, seriesModel, idx);
 
-    this.add(line);
-
-    var label = new graphic.Text({
-        name: 'label',
-        // FIXME
-        // Temporary solution for `focusNodeAdjacency`.
-        // line label do not use the opacity of lineStyle.
-        lineLabelOriginalOpacity: 1
-    });
-    this.add(label);
-
-    zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {
-        var symbol = createSymbol(symbolCategory, lineData, idx);
-        // symbols must added after line to make sure
-        // it will be updated after line#update.
-        // Or symbol position and rotation update in line#beforeUpdate will be one frame slow
-        this.add(symbol);
-        this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory);
-    }, this);
-
-    this._updateCommonStl(lineData, idx, seriesScope);
-};
-
-lineProto.updateData = function (lineData, idx, seriesScope) {
-    var seriesModel = lineData.hostModel;
-
-    var line = this.childOfName('line');
-    var linePoints = lineData.getItemLayout(idx);
-    var target = {
-        shape: {}
-    };
+        var visualColor = lineData.getItemVisual(idx, 'color');
+        var visualOpacity = zrUtil.retrieve3(
+            lineData.getItemVisual(idx, 'opacity'),
+            lineStyle.opacity,
+            1
+        );
+
+        line.useStyle(zrUtil.defaults(
+            {
+                strokeNoScale: true,
+                fill: 'none',
+                stroke: visualColor,
+                opacity: visualOpacity
+            },
+            lineStyle
+        ));
+        line.hoverStyle = hoverLineStyle;
+
+        // Update symbol
+        zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {
+            var symbol = this.childOfName(symbolCategory) as ECSymbol;
+            if (symbol) {
+                symbol.setColor(visualColor);
+                symbol.setStyle({
+                    opacity: visualOpacity
+                });
+            }
+        }, this);
+
+        var showLabel = labelModel.getShallow('show');
+        var hoverShowLabel = hoverLabelModel.getShallow('show');
+
+        var label = this.childOfName('label') as InnerLineLabel;
+        var defaultLabelColor;
+        var baseText;
+
+        // FIXME: the logic below probably should be merged to `graphic.setLabelStyle`.
+        if (showLabel || hoverShowLabel) {
+            defaultLabelColor = visualColor || '#000';
+
+            baseText = seriesModel.getFormattedLabel(idx, 'normal', lineData.dataType);
+            if (baseText == null) {
+                var rawVal = seriesModel.getRawValue(idx);
+                baseText = rawVal == null
+                    ? lineData.getName(idx)
+                    : isFinite(rawVal)
+                    ? round(rawVal)
+                    : rawVal;
+            }
+        }
+        var normalText = showLabel ? baseText : null;
+        var emphasisText = hoverShowLabel
+            ? zrUtil.retrieve2(
+                seriesModel.getFormattedLabel(idx, 'emphasis', lineData.dataType),
+                baseText
+            )
+            : null;
+
+        var labelStyle = label.style;
+
+        // Always set `textStyle` even if `normalStyle.text` is null, because default
+        // values have to be set on `normalStyle`.
+        if (normalText != null || emphasisText != null) {
+            graphic.setTextStyle(label.style, labelModel, {
+                text: normalText
+            }, {
+                autoColor: defaultLabelColor
+            });
 
-    setLinePoints(target.shape, linePoints);
-    graphic.updateProps(line, target, seriesModel, idx);
+            label.__textAlign = labelStyle.textAlign;
+            label.__verticalAlign = labelStyle.textVerticalAlign;
+            // 'start', 'middle', 'end'
+            label.__position = labelModel.get('position') || 'middle';
 
-    zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {
-        var symbolType = lineData.getItemVisual(idx, symbolCategory);
-        var key = makeSymbolTypeKey(symbolCategory);
-        // Symbol changed
-        if (this[key] !== symbolType) {
-            this.remove(this.childOfName(symbolCategory));
-            var symbol = createSymbol(symbolCategory, lineData, idx);
-            this.add(symbol);
+            var distance = labelModel.get('distance');
+            if (!zrUtil.isArray(distance)) {
+                distance = [distance, distance];
+            }
+            label.__labelDistance = distance;
         }
-        this[key] = symbolType;
-    }, this);
-
-    this._updateCommonStl(lineData, idx, seriesScope);
-};
 
-lineProto._updateCommonStl = function (lineData, idx, seriesScope) {
-    var seriesModel = lineData.hostModel;
+        if (emphasisText != null) {
+            // Only these properties supported in this emphasis style here.
+            label.hoverStyle = {
+                text: emphasisText,
+                textFill: hoverLabelModel.getTextColor(true),
+                // For merging hover style to normal style, do not use
+                // `hoverLabelModel.getFont()` here.
+                fontStyle: hoverLabelModel.getShallow('fontStyle'),
+                fontWeight: hoverLabelModel.getShallow('fontWeight'),
+                fontSize: hoverLabelModel.getShallow('fontSize'),
+                fontFamily: hoverLabelModel.getShallow('fontFamily')
+            };
+        }
+        else {
+            label.hoverStyle = {
+                text: null
+            };
+        }
 
-    var line = this.childOfName('line');
+        label.ignore = !showLabel && !hoverShowLabel;
 
-    var lineStyle = seriesScope && seriesScope.lineStyle;
-    var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle;
-    var labelModel = seriesScope && seriesScope.labelModel;
-    var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel;
+        graphic.setHoverStyle(this);
+    }
 
-    // Optimization for large dataset
-    if (!seriesScope || lineData.hasItemOption) {
-        var itemModel = lineData.getItemModel(idx);
+    highlight() {
+        this.trigger('emphasis');
+    }
 
-        lineStyle = itemModel.getModel('lineStyle').getLineStyle();
-        hoverLineStyle = itemModel.getModel('emphasis.lineStyle').getLineStyle();
+    downplay() {
+        this.trigger('normal');
+    }
 
-        labelModel = itemModel.getModel('label');
-        hoverLabelModel = itemModel.getModel('emphasis.label');
+    updateLayout(lineData: List, idx: number) {
+        this.setLinePoints(lineData.getItemLayout(idx));
     }
 
-    var visualColor = lineData.getItemVisual(idx, 'color');
-    var visualOpacity = zrUtil.retrieve3(
-        lineData.getItemVisual(idx, 'opacity'),
-        lineStyle.opacity,
-        1
-    );
+    setLinePoints(points: number[][]) {
+        var linePath = this.childOfName('line') as ECLinePath;
+        setLinePoints(linePath.shape, points);
+        linePath.dirty();
+    }
 
-    line.useStyle(zrUtil.defaults(
-        {
-            strokeNoScale: true,
-            fill: 'none',
-            stroke: visualColor,
-            opacity: visualOpacity
-        },
-        lineStyle
-    ));
-    line.hoverStyle = hoverLineStyle;
-
-    // Update symbol
-    zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {
-        var symbol = this.childOfName(symbolCategory);
-        if (symbol) {
-            symbol.setColor(visualColor);
-            symbol.setStyle({
-                opacity: visualOpacity
-            });
-        }
-    }, this);
-
-    var showLabel = labelModel.getShallow('show');
-    var hoverShowLabel = hoverLabelModel.getShallow('show');
-
-    var label = this.childOfName('label');
-    var defaultLabelColor;
-    var baseText;
-
-    // FIXME: the logic below probably should be merged to `graphic.setLabelStyle`.
-    if (showLabel || hoverShowLabel) {
-        defaultLabelColor = visualColor || '#000';
-
-        baseText = seriesModel.getFormattedLabel(idx, 'normal', lineData.dataType);
-        if (baseText == null) {
-            var rawVal = seriesModel.getRawValue(idx);
-            baseText = rawVal == null
-                ? lineData.getName(idx)
-                : isFinite(rawVal)
-                ? round(rawVal)
-                : rawVal;
+    beforeUpdate() {
+        var lineGroup = this;
+        var symbolFrom = lineGroup.childOfName('fromSymbol') as ECSymbol;
+        var symbolTo = lineGroup.childOfName('toSymbol') as ECSymbol;
+        var label = lineGroup.childOfName('label') as InnerLineLabel;
+        // Quick reject
+        if (!symbolFrom && !symbolTo && label.ignore) {
+            return;
         }
-    }
-    var normalText = showLabel ? baseText : null;
-    var emphasisText = hoverShowLabel
-        ? zrUtil.retrieve2(
-            seriesModel.getFormattedLabel(idx, 'emphasis', lineData.dataType),
-            baseText
-        )
-        : null;
-
-    var labelStyle = label.style;
-
-    // Always set `textStyle` even if `normalStyle.text` is null, because default
-    // values have to be set on `normalStyle`.
-    if (normalText != null || emphasisText != null) {
-        graphic.setTextStyle(label.style, labelModel, {
-            text: normalText
-        }, {
-            autoColor: defaultLabelColor
-        });
-
-        label.__textAlign = labelStyle.textAlign;
-        label.__verticalAlign = labelStyle.textVerticalAlign;
-        // 'start', 'middle', 'end'
-        label.__position = labelModel.get('position') || 'middle';
-
-        var distance = labelModel.get('distance');
-        if (!zrUtil.isArray(distance)) {
-            distance = [distance, distance];
+
+        var invScale = 1;
+        var parentNode = this.parent;
+        while (parentNode) {
+            if (parentNode.scale) {
+                invScale /= parentNode.scale[0];
+            }
+            parentNode = parentNode.parent;
         }
-        label.__labelDistance = distance;
-    }
 
-    if (emphasisText != null) {
-        // Only these properties supported in this emphasis style here.
-        label.hoverStyle = {
-            text: emphasisText,
-            textFill: hoverLabelModel.getTextColor(true),
-            // For merging hover style to normal style, do not use
-            // `hoverLabelModel.getFont()` here.
-            fontStyle: hoverLabelModel.getShallow('fontStyle'),
-            fontWeight: hoverLabelModel.getShallow('fontWeight'),
-            fontSize: hoverLabelModel.getShallow('fontSize'),
-            fontFamily: hoverLabelModel.getShallow('fontFamily')
-        };
-    }
-    else {
-        label.hoverStyle = {
-            text: null
-        };
-    }
+        var line = lineGroup.childOfName('line') as ECLinePath;
+        // If line not changed
+        // FIXME Parent scale changed
+        if (!this.__dirty && !line.__dirty) {
+            return;
+        }
 
-    label.ignore = !showLabel && !hoverShowLabel;
+        var percent = line.shape.percent;
+        var fromPos = line.pointAt(0);
+        var toPos = line.pointAt(percent);
 
-    graphic.setHoverStyle(this);
-};
+        var d = vector.sub([], toPos, fromPos);
+        vector.normalize(d, d);
 
-lineProto.highlight = function () {
-    this.trigger('emphasis');
-};
+        if (symbolFrom) {
+            symbolFrom.attr('position', fromPos);
+            var tangent = line.tangentAt(0);
+            symbolFrom.attr('rotation', Math.PI / 2 - Math.atan2(
+                tangent[1], tangent[0]
+            ));
+            symbolFrom.attr('scale', [invScale * percent, invScale * percent]);
+        }
+        if (symbolTo) {
+            symbolTo.attr('position', toPos);
+            var tangent = line.tangentAt(1);
+            symbolTo.attr('rotation', -Math.PI / 2 - Math.atan2(
+                tangent[1], tangent[0]
+            ));
+            symbolTo.attr('scale', [invScale * percent, invScale * percent]);
+        }
 
-lineProto.downplay = function () {
-    this.trigger('normal');
-};
+        if (!label.ignore) {
+            label.attr('position', toPos);
+
+            var textPosition;
+            var textAlign: ZRTextAlign;
+            var textVerticalAlign: ZRTextVerticalAlign;
+            var textOrigin;
+
+            var distance = label.__labelDistance;
+            var distanceX = distance[0] * invScale;
+            var distanceY = distance[1] * invScale;
+            var halfPercent = percent / 2;
+            var tangent = line.tangentAt(halfPercent);
+            var n = [tangent[1], -tangent[0]];
+            var cp = line.pointAt(halfPercent);
+            if (n[1] > 0) {
+                n[0] = -n[0];
+                n[1] = -n[1];
+            }
+            var dir = tangent[0] < 0 ? -1 : 1;
+
+            if (label.__position !== 'start' && label.__position !== 'end') {
+                var rotation = -Math.atan2(tangent[1], tangent[0]);
+                if (toPos[0] < fromPos[0]) {
+                    rotation = Math.PI + rotation;
+                }
+                label.attr('rotation', rotation);
+            }
 
-lineProto.updateLayout = function (lineData, idx) {
-    this.setLinePoints(lineData.getItemLayout(idx));
-};
+            var dy;
+            switch (label.__position) {
+                case 'insideStartTop':
+                case 'insideMiddleTop':
+                case 'insideEndTop':
+                case 'middle':
+                    dy = -distanceY;
+                    textVerticalAlign = 'bottom';
+                    break;
+
+                case 'insideStartBottom':
+                case 'insideMiddleBottom':
+                case 'insideEndBottom':
+                    dy = distanceY;
+                    textVerticalAlign = 'top';
+                    break;
+
+                default:
+                    dy = 0;
+                    textVerticalAlign = 'middle';
+            }
 
-lineProto.setLinePoints = function (points) {
-    var linePath = this.childOfName('line');
-    setLinePoints(linePath.shape, points);
-    linePath.dirty();
-};
+            switch (label.__position) {
+                case 'end':
+                    textPosition = [d[0] * distanceX + toPos[0], d[1] * distanceY + toPos[1]];
+                    textAlign = d[0] > 0.8 ? 'left' : (d[0] < -0.8 ? 'right' : 'center');
+                    textVerticalAlign = d[1] > 0.8 ? 'top' : (d[1] < -0.8 ? 'bottom' : 'middle');
+                    break;
+
+                case 'start':
+                    textPosition = [-d[0] * distanceX + fromPos[0], -d[1] * distanceY + fromPos[1]];
+                    textAlign = d[0] > 0.8 ? 'right' : (d[0] < -0.8 ? 'left' : 'center');
+                    textVerticalAlign = d[1] > 0.8 ? 'bottom' : (d[1] < -0.8 ? 'top' : 'middle');
+                    break;
+
+                case 'insideStartTop':
+                case 'insideStart':
+                case 'insideStartBottom':
+                    textPosition = [distanceX * dir + fromPos[0], fromPos[1] + dy];
+                    textAlign = tangent[0] < 0 ? 'right' : 'left';
+                    textOrigin = [-distanceX * dir, -dy];
+                    break;
+
+                case 'insideMiddleTop':
+                case 'insideMiddle':
+                case 'insideMiddleBottom':
+                case 'middle':
+                    textPosition = [cp[0], cp[1] + dy];
+                    textAlign = 'center';
+                    textOrigin = [0, -dy];
+                    break;
+
+                case 'insideEndTop':
+                case 'insideEnd':
+                case 'insideEndBottom':
+                    textPosition = [-distanceX * dir + toPos[0], toPos[1] + dy];
+                    textAlign = tangent[0] >= 0 ? 'right' : 'left';
+                    textOrigin = [distanceX * dir, -dy];
+                    break;
+            }
 
-zrUtil.inherits(Line, graphic.Group);
+            label.attr({
+                style: {
+                    // Use the user specified text align and baseline first
+                    textVerticalAlign: label.__verticalAlign || textVerticalAlign,
+                    textAlign: label.__textAlign || textAlign
+                },
+                position: textPosition,
+                scale: [invScale, invScale],
+                origin: textOrigin
+            });
+        }
+    }
+}
 
 export default Line;
\ No newline at end of file
diff --git a/src/chart/helper/LineDraw.ts b/src/chart/helper/LineDraw.ts
index 378badf..eb241fb 100644
--- a/src/chart/helper/LineDraw.ts
+++ b/src/chart/helper/LineDraw.ts
@@ -17,163 +17,179 @@
 * under the License.
 */
 
-// @ts-nocheck
-
-/**
- * @module echarts/chart/helper/LineDraw
- */
-
 import * as graphic from '../../util/graphic';
-import LineGroup from './Line';
-// import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable';
+import LineGroup, {SeriesScope} from './Line';
+import List from '../../data/List';
+import { StageHandlerProgressParams, LineStyleOption, LineLabelOption } from '../../util/types';
+import Displayable from 'zrender/src/graphic/Displayable';
+import Model from '../../model/Model';
+
+interface LineLike extends graphic.Group {
+    updateData(data: List, idx: number, scope?: SeriesScope): void
+    updateLayout(data: List, idx: number): void
+    fadeOut?(cb: () => void): void
+}
 
-/**
- * @alias module:echarts/component/marker/LineDraw
- * @constructor
- */
-function LineDraw(ctor) {
-    this._ctor = ctor || LineGroup;
+interface LineLikeCtor {
+    new(data: List, idx: number, scope?: SeriesScope): LineLike
+}
 
-    this.group = new graphic.Group();
+interface LineOption {
+    lineStyle?: LineStyleOption
+    label?: LineLabelOption
+    emphasis: {
+        lineStyle?: LineStyleOption
+        label?: LineLabelOption
+    }
 }
 
-var lineDrawProto = LineDraw.prototype;
+class LineDraw {
+    group = new graphic.Group()
 
-lineDrawProto.isPersistent = function () {
-    return true;
-};
+    private _LineCtor: LineLikeCtor
 
-/**
- * @param {module:echarts/data/List} lineData
- */
-lineDrawProto.updateData = function (lineData) {
-    var lineDraw = this;
-    var group = lineDraw.group;
+    private _lineData: List
 
-    var oldLineData = lineDraw._lineData;
-    lineDraw._lineData = lineData;
+    private _seriesScope: SeriesScope
 
-    // There is no oldLineData only when first rendering or switching from
-    // stream mode to normal mode, where previous elements should be removed.
-    if (!oldLineData) {
-        group.removeAll();
+    constructor(LineCtor?: LineLikeCtor) {
+        this._LineCtor = LineCtor || LineGroup;
     }
 
-    var seriesScope = makeSeriesScope(lineData);
-
-    lineData.diff(oldLineData)
-        .add(function (idx) {
-            doAdd(lineDraw, lineData, idx, seriesScope);
-        })
-        .update(function (newIdx, oldIdx) {
-            doUpdate(lineDraw, oldLineData, lineData, oldIdx, newIdx, seriesScope);
-        })
-        .remove(function (idx) {
-            group.remove(oldLineData.getItemGraphicEl(idx));
-        })
-        .execute();
-};
-
-function doAdd(lineDraw, lineData, idx, seriesScope) {
-    var itemLayout = lineData.getItemLayout(idx);
-
-    if (!lineNeedsDraw(itemLayout)) {
-        return;
-    }
+    isPersistent() {
+        return true;
+    };
 
-    var el = new lineDraw._ctor(lineData, idx, seriesScope);
-    lineData.setItemGraphicEl(idx, el);
-    lineDraw.group.add(el);
-}
+    updateData(lineData: List<Model<LineOption>>) {
+        var lineDraw = this;
+        var group = lineDraw.group;
 
-function doUpdate(lineDraw, oldLineData, newLineData, oldIdx, newIdx, seriesScope) {
-    var itemEl = oldLineData.getItemGraphicEl(oldIdx);
+        var oldLineData = lineDraw._lineData;
+        lineDraw._lineData = lineData;
 
-    if (!lineNeedsDraw(newLineData.getItemLayout(newIdx))) {
-        lineDraw.group.remove(itemEl);
-        return;
-    }
+        // There is no oldLineData only when first rendering or switching from
+        // stream mode to normal mode, where previous elements should be removed.
+        if (!oldLineData) {
+            group.removeAll();
+        }
 
-    if (!itemEl) {
-        itemEl = new lineDraw._ctor(newLineData, newIdx, seriesScope);
-    }
-    else {
-        itemEl.updateData(newLineData, newIdx, seriesScope);
-    }
+        var seriesScope = makeSeriesScope(lineData);
+
+        lineData.diff(oldLineData)
+            .add((idx) => {
+                this._doAdd(lineData, idx, seriesScope);
+            })
+            .update((newIdx, oldIdx) => {
+                this._doUpdate(oldLineData, lineData, oldIdx, newIdx, seriesScope);
+            })
+            .remove((idx) => {
+                group.remove(oldLineData.getItemGraphicEl(idx));
+            })
+            .execute();
+    };
 
-    newLineData.setItemGraphicEl(newIdx, itemEl);
+    updateLayout() {
+        var lineData = this._lineData;
 
-    lineDraw.group.add(itemEl);
-}
+        // Do not support update layout in incremental mode.
+        if (!lineData) {
+            return;
+        }
 
-lineDrawProto.updateLayout = function () {
-    var lineData = this._lineData;
+        lineData.eachItemGraphicEl(function (el: LineLike, idx) {
+            el.updateLayout(lineData, idx);
+        }, this);
+    };
 
-    // Do not support update layout in incremental mode.
-    if (!lineData) {
-        return;
-    }
+    incrementalPrepareUpdate(lineData: List) {
+        this._seriesScope = makeSeriesScope(lineData);
+        this._lineData = null;
+        this.group.removeAll();
+    };
 
-    lineData.eachItemGraphicEl(function (el, idx) {
-        el.updateLayout(lineData, idx);
-    }, this);
-};
-
-lineDrawProto.incrementalPrepareUpdate = function (lineData) {
-    this._seriesScope = makeSeriesScope(lineData);
-    this._lineData = null;
-    this.group.removeAll();
-};
-
-lineDrawProto.incrementalUpdate = function (taskParams, lineData) {
-    function updateIncrementalAndHover(el) {
-        if (!el.isGroup) {
-            el.incremental = el.useHoverLayer = true;
+    incrementalUpdate(taskParams: StageHandlerProgressParams, lineData: List) {
+        function updateIncrementalAndHover(el: Displayable) {
+            if (!el.isGroup) {
+                el.incremental = el.useHoverLayer = true;
+            }
         }
-    }
 
-    for (var idx = taskParams.start; idx < taskParams.end; idx++) {
+        for (var idx = taskParams.start; idx < taskParams.end; idx++) {
+            var itemLayout = lineData.getItemLayout(idx);
+
+            if (lineNeedsDraw(itemLayout)) {
+                var el = new this._LineCtor(lineData, idx, this._seriesScope);
+                el.traverse(updateIncrementalAndHover);
+
+                this.group.add(el);
+                lineData.setItemGraphicEl(idx, el);
+            }
+        }
+    };
+
+    remove() {
+        this.group.removeAll();
+    };
+
+    private _doAdd(
+        lineData: List,
+        idx: number,
+        seriesScope: SeriesScope
+    ) {
         var itemLayout = lineData.getItemLayout(idx);
 
-        if (lineNeedsDraw(itemLayout)) {
-            var el = new this._ctor(lineData, idx, this._seriesScope);
-            el.traverse(updateIncrementalAndHover);
+        if (!lineNeedsDraw(itemLayout)) {
+            return;
+        }
 
-            this.group.add(el);
-            lineData.setItemGraphicEl(idx, el);
+        var el = new this._LineCtor(lineData, idx, seriesScope);
+        lineData.setItemGraphicEl(idx, el);
+        this.group.add(el);
+    }
+    private _doUpdate(
+        oldLineData: List,
+        newLineData: List,
+        oldIdx: number,
+        newIdx: number,
+        seriesScope: SeriesScope
+    ) {
+        var itemEl = oldLineData.getItemGraphicEl(oldIdx) as LineLike;
+
+        if (!lineNeedsDraw(newLineData.getItemLayout(newIdx))) {
+            this.group.remove(itemEl);
+            return;
         }
+
+        if (!itemEl) {
+            itemEl = new this._LineCtor(newLineData, newIdx, seriesScope);
+        }
+        else {
+            itemEl.updateData(newLineData, newIdx, seriesScope);
+        }
+
+        newLineData.setItemGraphicEl(newIdx, itemEl);
+
+        this.group.add(itemEl);
     }
-};
+}
 
-function makeSeriesScope(lineData) {
+function makeSeriesScope(lineData: List<Model<LineOption>>) {
     var hostModel = lineData.hostModel;
     return {
         lineStyle: hostModel.getModel('lineStyle').getLineStyle(),
-        hoverLineStyle: hostModel.getModel('emphasis.lineStyle').getLineStyle(),
+        hoverLineStyle: hostModel.getModel(['emphasis', 'lineStyle']).getLineStyle(),
         labelModel: hostModel.getModel('label'),
-        hoverLabelModel: hostModel.getModel('emphasis.label')
+        hoverLabelModel: hostModel.getModel(['emphasis', 'label'])
     };
 }
 
-lineDrawProto.remove = function () {
-    this._clearIncremental();
-    this._incremental = null;
-    this.group.removeAll();
-};
-
-lineDrawProto._clearIncremental = function () {
-    var incremental = this._incremental;
-    if (incremental) {
-        incremental.clearDisplaybles();
-    }
-};
-
-function isPointNaN(pt) {
+function isPointNaN(pt: number[]) {
     return isNaN(pt[0]) || isNaN(pt[1]);
 }
 
-function lineNeedsDraw(pts) {
+function lineNeedsDraw(pts: number[][]) {
     return !isPointNaN(pts[0]) && !isPointNaN(pts[1]);
 }
 
+
 export default LineDraw;
diff --git a/src/chart/helper/LinePath.ts b/src/chart/helper/LinePath.ts
index b1acce6..a6a414c 100644
--- a/src/chart/helper/LinePath.ts
+++ b/src/chart/helper/LinePath.ts
@@ -17,60 +17,79 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 /**
  * Line path for bezier and straight line draw
  */
 
 import * as graphic from '../../util/graphic';
 import * as vec2 from 'zrender/src/core/vector';
+import { PathProps } from 'zrender/src/graphic/Path';
 
 var straightLineProto = graphic.Line.prototype;
 var bezierCurveProto = graphic.BezierCurve.prototype;
 
-function isLine(shape) {
-    return isNaN(+shape.cpx1) || isNaN(+shape.cpy1);
+class StraightLineShape {
+    // Start point
+    x1 = 0
+    y1 = 0
+    // End point
+    x2 = 0
+    y2 = 0
+
+    percent = 1
+}
+
+class CurveShape extends StraightLineShape {
+    cpx1: number
+    cpy1: number
 }
 
-export default graphic.extendShape({
-
-    type: 'ec-line',
-
-    style: {
-        stroke: '#000',
-        fill: null
-    },
-
-    shape: {
-        x1: 0,
-        y1: 0,
-        x2: 0,
-        y2: 0,
-        percent: 1,
-        cpx1: null,
-        cpy1: null
-    },
-
-    buildPath: function (ctx, shape) {
-        this[isLine(shape) ? '_buildPathLine' : '_buildPathCurve'](ctx, shape);
-    },
-    _buildPathLine: straightLineProto.buildPath,
-    _buildPathCurve: bezierCurveProto.buildPath,
-
-    pointAt: function (t) {
-        return this[isLine(this.shape) ? '_pointAtLine' : '_pointAtCurve'](t);
-    },
-    _pointAtLine: straightLineProto.pointAt,
-    _pointAtCurve: bezierCurveProto.pointAt,
-
-    tangentAt: function (t) {
+interface ECLineProps extends PathProps {
+    shape?: Partial<StraightLineShape | CurveShape>
+}
+function isStraightLine(shape: StraightLineShape | CurveShape): shape is StraightLineShape {
+    return isNaN(+(shape as CurveShape).cpx1) || isNaN(+(shape as CurveShape).cpy1);
+}
+
+class ECLinePath extends graphic.Path<ECLineProps> {
+
+    type = 'ec-line'
+
+    shape: StraightLineShape | CurveShape
+
+    constructor(opts?: ECLineProps) {
+        super(opts, {
+            stroke: '#000',
+            fill: null
+        }, new StraightLineShape());// Default to be line
+    }
+
+    buildPath(ctx: CanvasRenderingContext2D, shape: StraightLineShape | CurveShape) {
+        if (isStraightLine(shape)) {
+            straightLineProto.buildPath(ctx, shape);
+        }
+        else {
+            bezierCurveProto.buildPath(ctx, shape);
+        }
+    }
+
+    pointAt(t: number) {
+        if (isStraightLine(this.shape)) {
+            return straightLineProto.pointAt(t);
+        }
+        else {
+            return bezierCurveProto.pointAt(t);
+        }
+    }
+
+    tangentAt(t: number) {
         var shape = this.shape;
-        var p = isLine(shape)
+        var p = isStraightLine(shape)
             ? [shape.x2 - shape.x1, shape.y2 - shape.y1]
-            : this._tangentAtCurve(t);
+            : bezierCurveProto.tangentAt(t);
         return vec2.normalize(p, p);
-    },
-    _tangentAtCurve: bezierCurveProto.tangentAt
+    }
+
+}
 
-});
\ No newline at end of file
+export default ECLinePath;
\ No newline at end of file
diff --git a/src/chart/helper/Symbol.ts b/src/chart/helper/Symbol.ts
index 6c7242d..b9bc623 100644
--- a/src/chart/helper/Symbol.ts
+++ b/src/chart/helper/Symbol.ts
@@ -121,7 +121,7 @@ class Symbol extends graphic.Group {
      * Get symbol path element.
      */
     getSymbolPath() {
-        return this.childAt(0);
+        return this.childAt(0) as ECSymbol;
     }
 
     /**
diff --git a/src/chart/line/LineSeries.ts b/src/chart/line/LineSeries.ts
index ac64978..cd788e1 100644
--- a/src/chart/line/LineSeries.ts
+++ b/src/chart/line/LineSeries.ts
@@ -30,7 +30,7 @@ import {
     ItemStyleOption,
     AreaStyleOption,
     OptionDataValue,
-    SeriesSymbolOptionMixin
+    SymbolOptionMixin
 } from '../../util/types';
 import List from '../../data/List';
 import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
@@ -40,7 +40,7 @@ type SamplingFunc = (frame: number[]) => number
 
 type LineDataValue = OptionDataValue | OptionDataValue[]
 
-export interface LineDataItemOption {
+export interface LineDataItemOption extends SymbolOptionMixin {
     name?: string
 
     value?: LineDataValue
@@ -58,7 +58,7 @@ export interface LineSeriesOption extends SeriesOption,
     SeriesOnCartesianOptionMixin,
     SeriesOnPolarOptionMixin,
     SeriesStackOptionMixin,
-    SeriesSymbolOptionMixin {
+    SymbolOptionMixin {
     coordinateSystem?: 'cartesian2d' | 'polar'
 
     hoverAnimation?: boolean
diff --git a/src/chart/map/MapSeries.ts b/src/chart/map/MapSeries.ts
index 6b1c8b1..7c4625f 100644
--- a/src/chart/map/MapSeries.ts
+++ b/src/chart/map/MapSeries.ts
@@ -26,7 +26,17 @@ import {DataSelectableMixin, DataSelectableOptionMixin, SelectableTarget} from '
 import {retrieveRawAttr} from '../../data/helper/dataProvider';
 import geoSourceManager from '../../coord/geo/geoSourceManager';
 import {makeSeriesEncodeForNameBased} from '../../data/helper/sourceHelper';
-import { SeriesOption, BoxLayoutOptionMixin, SeriesEncodeOptionMixin, LabelOption, SeriesTooltipOption, OptionDataItemObject, OptionDataValueNumeric, ParsedValue, SeriesOnGeoOptionMixin } from '../../util/types';
+import {
+    SeriesOption,
+    BoxLayoutOptionMixin,
+    SeriesEncodeOptionMixin,
+    LabelOption,
+    SeriesTooltipOption,
+    OptionDataItemObject,
+    OptionDataValueNumeric,
+    ParsedValue,
+    SeriesOnGeoOptionMixin
+} from '../../util/types';
 import { Dictionary } from 'zrender/src/core/types';
 import GeoModel, { GeoCommonOptionMixin, GeoItemStyleOption } from '../../coord/geo/GeoModel';
 import List from '../../data/List';
diff --git a/src/chart/pictorialBar.ts b/src/chart/pictorialBar.ts
index 86dadf7..975e4d9 100644
--- a/src/chart/pictorialBar.ts
+++ b/src/chart/pictorialBar.ts
@@ -17,8 +17,6 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as echarts from '../echarts';
 import * as zrUtil from 'zrender/src/core/util';
 
diff --git a/src/chart/pie/PieSeries.ts b/src/chart/pie/PieSeries.ts
index 9a51351..a161e41 100644
--- a/src/chart/pie/PieSeries.ts
+++ b/src/chart/pie/PieSeries.ts
@@ -30,7 +30,7 @@ import {
     SeriesOption,
     CallbackDataParams,
     CircleLayoutOptionMixin,
-    LabelLineOption,
+    LabelGuideLineOption,
     ItemStyleOption,
     LabelOption,
     BoxLayoutOptionMixin,
@@ -54,12 +54,12 @@ interface PieDataItem extends
 
     itemStyle?: ItemStyleOption
     label?: PieLabelOption
-    labelLine?: LabelLineOption
+    labelLine?: LabelGuideLineOption
 
     emphasis?: {
         itemStyle?: ItemStyleOption
         label?: PieLabelOption
-        labelLine?: LabelLineOption
+        labelLine?: LabelGuideLineOption
     }
 }
 export interface PieSeriesOption extends
@@ -78,7 +78,7 @@ export interface PieSeriesOption extends
     // TODO: TYPE Color Callback
     itemStyle?: ItemStyleOption
     label?: PieLabelOption
-    labelLine?: LabelLineOption
+    labelLine?: LabelGuideLineOption
 
     clockwise?: boolean
     startAngle?: number
@@ -96,7 +96,7 @@ export interface PieSeriesOption extends
     emphasis?: {
         itemStyle?: ItemStyleOption
         label?: PieLabelOption
-        labelLine?: LabelLineOption
+        labelLine?: LabelGuideLineOption
     }
 
     animationType?: 'expansion' | 'scale'
diff --git a/src/chart/radar.ts b/src/chart/radar.ts
index 39ce9be..0ed87f7 100644
--- a/src/chart/radar.ts
+++ b/src/chart/radar.ts
@@ -17,8 +17,6 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as echarts from '../echarts';
 
 // Must use radar component
diff --git a/src/chart/scatter/ScatterSeries.ts b/src/chart/scatter/ScatterSeries.ts
index db4829c..f5cd3df 100644
--- a/src/chart/scatter/ScatterSeries.ts
+++ b/src/chart/scatter/ScatterSeries.ts
@@ -31,14 +31,14 @@ import {
     LabelOption,
     SeriesLargeOptionMixin,
     SeriesStackOptionMixin,
-    SeriesSymbolOptionMixin
+    SymbolOptionMixin
 } from '../../util/types';
 import GlobalModel from '../../model/Global';
 import List from '../../data/List';
 
 type ScatterDataValue = OptionDataValue | OptionDataValue[]
 
-export interface ScatterDataItemOption {
+export interface ScatterDataItemOption extends SymbolOptionMixin {
     name?: string
 
     value?: ScatterDataValue
@@ -56,7 +56,7 @@ export interface ScatterSeriesOption extends SeriesOption,
     SeriesOnCartesianOptionMixin, SeriesOnPolarOptionMixin, SeriesOnCalendarOptionMixin,
     SeriesOnGeoOptionMixin, SeriesOnSingleOptionMixin,
     SeriesLargeOptionMixin, SeriesStackOptionMixin,
-    SeriesSymbolOptionMixin {
+    SymbolOptionMixin {
 
     coordinateSystem?: string
 
diff --git a/src/chart/themeRiver.ts b/src/chart/themeRiver.ts
index 87cec62..26f136c 100644
--- a/src/chart/themeRiver.ts
+++ b/src/chart/themeRiver.ts
@@ -17,8 +17,6 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as echarts from '../echarts';
 
 import '../component/singleAxis';
diff --git a/src/chart/tree.ts b/src/chart/tree.ts
index 20ab129..e498d25 100644
--- a/src/chart/tree.ts
+++ b/src/chart/tree.ts
@@ -17,8 +17,6 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as echarts from '../echarts';
 
 import './tree/TreeSeries';
diff --git a/src/chart/treemap.ts b/src/chart/treemap.ts
index 60bf7f0..3414052 100644
--- a/src/chart/treemap.ts
+++ b/src/chart/treemap.ts
@@ -17,8 +17,6 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as echarts from '../echarts';
 
 import './treemap/TreemapSeries';
diff --git a/src/component/helper/RoamController.ts b/src/component/helper/RoamController.ts
index 5f6454c..4a1d12e 100644
--- a/src/component/helper/RoamController.ts
+++ b/src/component/helper/RoamController.ts
@@ -21,12 +21,12 @@ import Eventful from 'zrender/src/core/Eventful';
 import * as eventTool from 'zrender/src/core/event';
 import * as interactionMutex from './interactionMutex';
 import { ZRenderType } from 'zrender/src/zrender';
-import { ZRElementEvent } from '../../util/types';
+import { ZRElementEvent, RoamOptionMixin } from '../../util/types';
 import { Bind3, isString, bind, defaults, clone } from 'zrender/src/core/util';
 
 // Can be null/undefined or true/false
 // or 'pan/move' or 'zoom'/'scale'
-export type RoamType = boolean | 'pan' | 'move' | 'zoom' | 'scale';
+export type RoamType = RoamOptionMixin['roam']
 
 interface RoamOption {
     zoomOnMouseWheel?: boolean | 'ctrl' | 'shift' | 'alt'
diff --git a/src/coord/View.ts b/src/coord/View.ts
index ebf0e42..1e0762f 100644
--- a/src/coord/View.ts
+++ b/src/coord/View.ts
@@ -57,7 +57,7 @@ class View extends Transformable implements CoordinateSystemMaster, CoordinateSy
     private _rawTransform: matrix.MatrixArray;
 
 
-    constructor(name: string) {
+    constructor(name?: string) {
         super();
         this.name = name;
     }
diff --git a/src/coord/geo/GeoModel.ts b/src/coord/geo/GeoModel.ts
index 3b590a5..00d795f 100644
--- a/src/coord/geo/GeoModel.ts
+++ b/src/coord/geo/GeoModel.ts
@@ -31,7 +31,8 @@ import {
     ItemStyleOption,
     ZRColor,
     LabelOption,
-    DisplayState
+    DisplayState,
+    RoamOptionMixin
 } from '../../util/types';
 import { RoamType } from '../../component/helper/RoamController';
 import { NameMap } from './geoTypes';
@@ -58,7 +59,7 @@ export interface RegoinOption extends SelectableTarget {
     }
 };
 
-export interface GeoCommonOptionMixin {
+export interface GeoCommonOptionMixin extends RoamOptionMixin {
     // Map name
     map: string;
 
@@ -76,10 +77,6 @@ export interface GeoCommonOptionMixin {
     // For example, [ [180, 90], [-180, -90] ]
     // higher priority than center and zoom
     boundingCoords?: number[][];
-    // Default on center of map
-    center?: number[];
-    roam?: RoamType;
-    zoom?: number;
 
     scaleLimit?: {
         min?: number;
diff --git a/src/data/Graph.ts b/src/data/Graph.ts
index b5eef6c..6322319 100644
--- a/src/data/Graph.ts
+++ b/src/data/Graph.ts
@@ -317,8 +317,6 @@ class Graph {
         return graph;
     };
 
-    static Node: typeof GraphNode
-    static Edge: typeof GraphEdge
 
 }
 
@@ -367,7 +365,7 @@ class GraphNode {
     }
 
     // TODO: TYPE Same type with Model#getModel
-    getModel(path: string | string[]): Model {
+    getModel(path?: string | string[]): Model {
         if (this.dataIndex < 0) {
             return;
         }
@@ -399,7 +397,7 @@ class GraphEdge {
         this.dataIndex = dataIndex == null ? -1 : dataIndex;
     }
 
-    getModel(path: string | string[]): Model {
+    getModel(path?: string | string[]): Model {
         if (this.dataIndex < 0) {
             return;
         }
@@ -460,8 +458,6 @@ interface GraphNode extends ReturnType<typeof createGraphDataProxyMixin> {};
 zrUtil.mixin(GraphEdge, createGraphDataProxyMixin('hostGraph', 'data'));
 zrUtil.mixin(GraphNode, createGraphDataProxyMixin('hostGraph', 'edgeData'));
 
-Graph.Node = GraphNode;
-Graph.Edge = GraphEdge;
-
-
 export default Graph;
+
+export {GraphNode, GraphEdge};
\ No newline at end of file
diff --git a/src/data/List.ts b/src/data/List.ts
index 6a1be69..37be86c 100644
--- a/src/data/List.ts
+++ b/src/data/List.ts
@@ -39,6 +39,8 @@ import {
 } from '../util/types';
 import {parseDate} from '../util/number';
 import {isDataItemOption} from '../util/model';
+import type Graph from './Graph';
+import type Tree from './Tree';
 
 
 var isObject = zrUtil.isObject;
@@ -120,8 +122,7 @@ var CLONE_PROPERTIES = [
 ];
 
 
-
-class List <HostModel extends Model = Model> {
+class List<HostModel extends Model = Model> {
 
     readonly type = 'list';
 
@@ -134,6 +135,15 @@ class List <HostModel extends Model = Model> {
 
     readonly dataType: string;
 
+    /**
+     * Host graph if List is used to store graph nodes / edges.
+     */
+    readonly graph: Graph
+    /**
+     * Host tree if List is used to store tree ndoes.
+     */
+    readonly tree: Tree
+
     // Indices stores the indices of data subset after filtered.
     // This data subset will be used in chart.
     private _indices: ArrayLike<any>;
@@ -1375,11 +1385,11 @@ class List <HostModel extends Model = Model> {
     /**
      * Data mapping to a plain array
      */
-    mapArray<Ctx>(cb: MapArrayCb0<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): any[];
-    mapArray<Ctx>(dims: DimensionLoose, cb: MapArrayCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): any[];
-    mapArray<Ctx>(dims: [DimensionLoose], cb: MapArrayCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): any[];
-    mapArray<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: MapArrayCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): any[];
-    mapArray<Ctx>(dims: ItrParamDims, cb: MapArrayCb<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): any[];
+    mapArray<Ctx, Cb extends MapArrayCb0<Ctx>>(cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
+    mapArray<Ctx, Cb extends MapArrayCb1<Ctx>>(dims: DimensionLoose, cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
+    mapArray<Ctx, Cb extends MapArrayCb1<Ctx>>(dims: [DimensionLoose], cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
+    mapArray<Ctx, Cb extends MapArrayCb2<Ctx>>(dims: [DimensionLoose, DimensionLoose], cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
+    mapArray<Ctx, Cb extends MapArrayCb<Ctx>>(dims: ItrParamDims, cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
     mapArray<Ctx>(
         dims: ItrParamDims | MapArrayCb<Ctx>,
         cb: MapArrayCb<Ctx> | Ctx,
@@ -1637,7 +1647,7 @@ class List <HostModel extends Model = Model> {
     /**
      * Get layout of single data item
      */
-    getItemLayout(idx: number): Dictionary<any> {
+    getItemLayout(idx: number): any {
         return this._itemLayouts[idx];
     }
 
diff --git a/src/data/Tree.ts b/src/data/Tree.ts
index a8aa124..c9c0e85 100644
--- a/src/data/Tree.ts
+++ b/src/data/Tree.ts
@@ -287,7 +287,7 @@ class TreeNode {
     }
 };
 
-class Tree<HostModel extends Model, LevelOption, LeavesOption> {
+class Tree<HostModel extends Model = Model, LevelOption = any, LeavesOption = any> {
 
     type: 'tree' = 'tree'
 
diff --git a/src/echarts.ts b/src/echarts.ts
index c82c1d6..0c97527 100644
--- a/src/echarts.ts
+++ b/src/echarts.ts
@@ -722,7 +722,9 @@ class ECharts {
     getVisual(finder: ModelFinder, visualType: string) {
         var ecModel = this._model;
 
-        finder = modelUtil.parseFinder(ecModel, finder, {defaultMainType: 'series'});
+        finder = modelUtil.parseFinder(ecModel, finder, {
+            defaultMainType: 'series'
+        });
 
         var seriesModel = finder.seriesModel;
 
diff --git a/src/model/Series.ts b/src/model/Series.ts
index e0ea224..acb70db 100644
--- a/src/model/Series.ts
+++ b/src/model/Series.ts
@@ -29,7 +29,7 @@ import {
 import * as modelUtil from '../util/model';
 import {
     DataHost, DimensionName, StageHandlerProgressParams,
-    SeriesOption, TooltipRenderMode, ZRColor, BoxLayoutOptionMixin, ScaleDataValue
+    SeriesOption, TooltipRenderMode, ZRColor, BoxLayoutOptionMixin, ScaleDataValue, Dictionary, ColorString
 } from '../util/types';
 import ComponentModel, { ComponentModelConstructor } from './Component';
 import {ColorPaletteMixin} from './mixin/colorPalette';
@@ -54,6 +54,7 @@ import LegendVisualProvider from '../visual/LegendVisualProvider';
 import List from '../data/List';
 import Source from '../data/Source';
 import Axis from '../coord/Axis';
+import { GradientObject } from 'zrender/src/graphic/Gradient';
 
 var inner = modelUtil.makeInner<{
     data: List
@@ -340,14 +341,14 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
         renderMode?: TooltipRenderMode
     ): {
         html: string,
-        markers: {[markName: string]: string}
+        markers: Dictionary<ColorString>
     } | string { // The override method can also return string
 
         var series = this;
         renderMode = renderMode || 'html';
         var newLine = renderMode === 'html' ? '<br/>' : '\n';
         var isRichText = renderMode === 'richText';
-        var markers: {[markName: string]: string} = {};
+        var markers: Dictionary<ColorString> = {};
         var markerId = 0;
 
         function formatArrayValue(value: any[]) {
@@ -376,7 +377,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
                 var dimType = dimInfo.type;
                 var markName = 'sub' + series.seriesIndex + 'at' + markerId;
                 var dimHead = getTooltipMarker({
-                    color: color,
+                    color: colorStr,
                     type: 'subItem',
                     renderMode: renderMode,
                     markerId: markName
@@ -397,7 +398,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
                 valStr && result.push(valStr);
 
                 if (isRichText) {
-                    markers[markName] = color;
+                    markers[markName] = colorStr;
                     ++markerId;
                 }
             }
@@ -426,11 +427,15 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
         var value = this.getRawValue(dataIndex);
         var isValueArr = zrUtil.isArray(value);
 
-        var color = data.getItemVisual(dataIndex, 'color');
-        if (zrUtil.isObject(color) && color.colorStops) {
-            color = (color.colorStops[0] || {}).color;
+        var color = data.getItemVisual(dataIndex, 'color') as ZRColor;
+        var colorStr: ColorString;
+        if (zrUtil.isString(color)) {
+            colorStr = color;
         }
-        color = color || 'transparent';
+        else if (color && (color as GradientObject).colorStops) {
+            colorStr = ((color as GradientObject).colorStops[0] || {}).color;
+        }
+        colorStr = colorStr || 'transparent';
 
         // Complicated rule for pretty tooltip.
         var formattedValue = (tooltipDimLen > 1 || (isValueArr && !tooltipDimLen))
@@ -442,12 +447,12 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
 
         var markName = series.seriesIndex + 'at' + markerId;
         var colorEl = getTooltipMarker({
-            color: color,
+            color: colorStr,
             type: 'item',
             renderMode: renderMode,
             markerId: markName
         });
-        markers[markName] = color;
+        markers[markName] = colorStr;
         ++markerId;
 
         var name = data.getName(dataIndex);
@@ -495,7 +500,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
         this.dataTask.dirty();
     }
 
-    getColorFromPalette(name: string, scope: any, requestColorNum: number): ZRColor {
+    getColorFromPalette(name: string, scope: any, requestColorNum?: number): ZRColor {
         var ecModel = this.ecModel;
         // PENDING
         var color = ColorPaletteMixin.prototype.getColorFromPalette.call(this, name, scope, requestColorNum);
diff --git a/src/model/mixin/dataFormat.ts b/src/model/mixin/dataFormat.ts
index 696f350..3ccaa2f 100644
--- a/src/model/mixin/dataFormat.ts
+++ b/src/model/mixin/dataFormat.ts
@@ -42,7 +42,6 @@ class DataFormatMixin {
      * Get params for formatter
      */
     getDataParams(
-        this: DataFormatMixin,
         dataIndex: number,
         dataType?: string,
         el?: Element // May be used in override.
@@ -101,7 +100,6 @@ class DataFormatMixin {
      * @return If not formatter, return null/undefined
      */
     getFormattedLabel(
-        this: DataFormatMixin,
         dataIndex: number,
         status?: DisplayState,
         dataType?: string,
@@ -145,12 +143,8 @@ class DataFormatMixin {
 
     /**
      * Get raw value in option
-     * @param {number} idx
-     * @param {string} [dataType]
-     * @return {Array|number|string}
      */
     getRawValue(
-        this: DataFormatMixin,
         idx: number,
         dataType?: string
     ) {
@@ -169,7 +163,6 @@ class DataFormatMixin {
      *                                     DOM operation is not supported.
      */
     formatTooltip(
-        this: DataFormatMixin,
         dataIndex: number,
         multipleSeries?: boolean,
         dataType?: string,
diff --git a/src/util/format.ts b/src/util/format.ts
index 217aef6..ca945a4 100644
--- a/src/util/format.ts
+++ b/src/util/format.ts
@@ -20,7 +20,7 @@
 import * as zrUtil from 'zrender/src/core/util';
 import * as textContain from 'zrender/src/contain/text';
 import * as numberUtil from './number';
-import {TooltipRenderMode} from './types';
+import {TooltipRenderMode, ColorString} from './types';
 import { Dictionary } from 'zrender/src/core/types';
 import { StyleProps } from 'zrender/src/graphic/Style';
 // import Text from 'zrender/src/graphic/Text';
@@ -131,13 +131,13 @@ interface RichTextTooltipMarker {
     renderMode: TooltipRenderMode;
     content: string;
     style: {
-        color: string
+        color: ColorString
         [key: string]: any
     };
 }
 export type TooltipMarker = string | RichTextTooltipMarker;
 interface GetTooltipMarkerOpt {
-    color?: string;
+    color?: ColorString;
     extraCssText?: string;
     // By default: 'item'
     type?: 'item' | 'subItem';
@@ -146,10 +146,14 @@ interface GetTooltipMarkerOpt {
     // By default: 'X'
     markerId?: string;
 }
-export function getTooltipMarker(color: string, extraCssText?: string): TooltipMarker;
+// Only support color string
+export function getTooltipMarker(color: ColorString, extraCssText?: string): TooltipMarker;
 export function getTooltipMarker(opt: GetTooltipMarkerOpt): TooltipMarker;
-export function getTooltipMarker(opt: string | GetTooltipMarkerOpt, extraCssText?: string): TooltipMarker {
-    opt = zrUtil.isString(opt) ? {color: opt, extraCssText: extraCssText} : (opt || {});
+export function getTooltipMarker(inOpt: ColorString | GetTooltipMarkerOpt, extraCssText?: string): TooltipMarker {
+    var opt = zrUtil.isString(inOpt) ? {
+        color: inOpt,
+        extraCssText: extraCssText
+    } : (inOpt || {}) as GetTooltipMarkerOpt;
     var color = opt.color;
     var type = opt.type;
     var extraCssText = opt.extraCssText;
@@ -164,6 +168,7 @@ export function getTooltipMarker(opt: string | GetTooltipMarkerOpt, extraCssText
         return type === 'subItem'
         ? '<span style="display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;'
             + 'border-radius:4px;width:4px;height:4px;background-color:'
+            // Only support string
             + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>'
         : '<span style="display:inline-block;margin-right:5px;'
             + 'border-radius:10px;width:10px;height:10px;background-color:'
diff --git a/src/util/types.ts b/src/util/types.ts
index d2b34c5..6667c53 100644
--- a/src/util/types.ts
+++ b/src/util/types.ts
@@ -590,22 +590,44 @@ export interface AnimationOptionMixin {
     animationDelayUpdate?: number | AnimationDelayCallback
 }
 
+export interface RoamOptionMixin {
+    /**
+     * If enable roam. can be specified 'scale' or 'move'
+     */
+    roam?: boolean | 'pan' | 'move' | 'zoom' | 'scale'
+    /**
+     * Current center position.
+     */
+    center?: number[]
+    /**
+     * Current zoom level. Default is 1
+     */
+    zoom?: number
+
+    scaleLimit?: {
+        min?: number
+        max?: number
+    }
+}
+
 // TODO: TYPE value type?
-export type SymbolSizeCallback = (rawValue: any, params: CallbackDataParams) => number | number[]
-export type SymbolCallback = (rawValue: any, params: CallbackDataParams) => string
+export type SymbolSizeCallback<T> = (rawValue: any, params: T) => number | number[]
+export type SymbolCallback<T> = (rawValue: any, params: T) => string
 /**
  * Mixin of option set to control the element symbol.
  * Include type of symbol, and size of symbol.
  */
-export interface SymbolOptionMixin {
+export interface SymbolOptionMixin<T = unknown> {
     /**
      * type of symbol, like `cirlce`, `rect`, or custom path and image.
      */
-    symbol?: string | SymbolCallback
+    symbol?: string | (unknown extends T ? never : SymbolCallback<T>)
     /**
      * Size of symbol.
      */
-    symbolSize?: number | number[] | SymbolSizeCallback
+    symbolSize?: number | number[] | (unknown extends T ? never : SymbolSizeCallback<T>)
+
+    symbolRotate?: number
     symbolKeepAspect?: boolean
 }
 
@@ -712,6 +734,10 @@ export interface TextCommonOption extends ShadowOptionMixin {
 
     tag?: string
 }
+
+export interface LabelFormatterCallback<T = CallbackDataParams> {
+    (params: T): string
+}
 /**
  * LabelOption is an option set to control the style of labels.
  * Include color, background, shadow, truncate, rotation, distance, etc..
@@ -734,7 +760,18 @@ export interface LabelOption extends TextCommonOption {
     rich?: Dictionary<TextCommonOption>
 }
 
-export interface LabelLineOption {
+/**
+ * Option for labels on line, like markLine, lines
+ */
+export interface LineLabelOption extends Omit<LabelOption, 'distance'> {
+    /**
+     * Distance can be an array.
+     * Which will specify horizontal and vertical distance respectively
+     */
+    distance?: number | number[]
+}
+
+export interface LabelGuideLineOption {
     show?: boolean
     length?: number
     length2?: number
@@ -1052,11 +1089,6 @@ export interface SeriesLargeOptionMixin {
 export interface SeriesStackOptionMixin {
     stack?: string
 }
-export interface SeriesSymbolOptionMixin {
-    symbol?: string
-    symbolSize?: number | number[]
-    symbolRotate?: number
-}
 
 export interface SeriesEncodeOptionMixin {
     datasetIndex?: number;
diff --git a/src/visual/symbol.ts b/src/visual/symbol.ts
index facc227..c9d2012 100644
--- a/src/visual/symbol.ts
+++ b/src/visual/symbol.ts
@@ -18,7 +18,14 @@
 */
 
 import {isFunction} from 'zrender/src/core/util';
-import { StageHandler, SeriesOption, SymbolOptionMixin, SymbolSizeCallback, SymbolCallback } from '../util/types';
+import {
+    StageHandler,
+    SeriesOption,
+    SymbolOptionMixin,
+    SymbolSizeCallback,
+    SymbolCallback,
+    CallbackDataParams
+} from '../util/types';
 import List from '../data/List';
 import SeriesModel from '../model/Series';
 import GlobalModel from '../model/Global';
@@ -32,7 +39,7 @@ export default function (seriesType: string, defaultSymbolType: string, legendSy
         performRawSeries: true,
 
         reset: function (
-            seriesModel: SeriesModel<SeriesOption & SymbolOptionMixin>,
+            seriesModel: SeriesModel<SeriesOption & SymbolOptionMixin<CallbackDataParams>>,
             ecModel: GlobalModel
         ) {
             var data = seriesModel.getData();
@@ -68,10 +75,10 @@ export default function (seriesType: string, defaultSymbolType: string, legendSy
                     var rawValue = seriesModel.getRawValue(idx);
                     var params = seriesModel.getDataParams(idx);
                     hasSymbolTypeCallback && data.setItemVisual(
-                        idx, 'symbol', (symbolType as SymbolCallback)(rawValue, params)
+                        idx, 'symbol', (symbolType as SymbolCallback<CallbackDataParams>)(rawValue, params)
                     );
                     hasSymbolSizeCallback && data.setItemVisual(
-                        idx, 'symbolSize', (symbolSize as SymbolSizeCallback)(rawValue, params)
+                        idx, 'symbolSize', (symbolSize as SymbolSizeCallback<CallbackDataParams>)(rawValue, params)
                     );
                 }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org