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/02/24 13:17:25 UTC

[incubator-echarts] branch typescript updated: ts: types for tooltip. enhance makeInner. Rename CoordinateSystemExecutive to CoordinateSystem

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 a24c333  ts: types for tooltip. enhance makeInner. Rename CoordinateSystemExecutive to CoordinateSystem
a24c333 is described below

commit a24c333c7edccb0dbe24ef6768f72e1a7165b4db
Author: pissang <bm...@gmail.com>
AuthorDate: Mon Feb 24 21:16:47 2020 +0800

    ts: types for tooltip. enhance makeInner. Rename CoordinateSystemExecutive to CoordinateSystem
---
 src/ExtensionAPI.ts                                |   3 +-
 src/chart/pie/labelLayout.ts                       |   5 +-
 src/component/axisPointer/AxisPointerView.ts       |  39 +-
 src/component/axisPointer/globalListener.ts        |  81 +++-
 src/component/legend/LegendView.ts                 |  23 +-
 src/component/legend/ScrollableLegendView.ts       |   4 +-
 .../{TooltipContent.ts => TooltipHTMLContent.ts}   | 238 ++++++------
 src/component/tooltip/TooltipModel.ts              | 124 +-----
 src/component/tooltip/TooltipRichContent.ts        | 108 +++---
 src/component/tooltip/TooltipView.ts               | 430 ++++++++++++++-------
 src/coord/CoordinateSystem.ts                      |   4 +-
 src/coord/cartesian/Cartesian2D.ts                 |   4 +-
 src/data/List.ts                                   |   6 +-
 src/echarts.ts                                     |  22 +-
 src/model/Component.ts                             |   4 +-
 src/model/Model.ts                                 |   9 +-
 src/model/Series.ts                                |  11 +-
 src/model/mixin/colorPalette.ts                    |   6 +-
 src/util/format.ts                                 |   2 +-
 src/util/graphic.ts                                |  27 +-
 src/util/layout.ts                                 |   6 +-
 src/util/model.ts                                  |   4 +-
 src/util/types.ts                                  | 142 ++++++-
 src/view/Chart.ts                                  |   8 +-
 24 files changed, 805 insertions(+), 505 deletions(-)

diff --git a/src/ExtensionAPI.ts b/src/ExtensionAPI.ts
index d9ba448..37dc6c3 100644
--- a/src/ExtensionAPI.ts
+++ b/src/ExtensionAPI.ts
@@ -38,7 +38,8 @@ var availableMethods = {
     getModel: 1,
     getOption: 1,
     getViewOfComponentModel: 1,
-    getViewOfSeriesModel: 1
+    getViewOfSeriesModel: 1,
+    getId: 1
 };
 
 interface ExtensionAPI extends Pick<EChartsType, keyof typeof availableMethods> {}
diff --git a/src/chart/pie/labelLayout.ts b/src/chart/pie/labelLayout.ts
index 8c8c073..836e710 100644
--- a/src/chart/pie/labelLayout.ts
+++ b/src/chart/pie/labelLayout.ts
@@ -23,8 +23,7 @@ import * as textContain from 'zrender/src/contain/text';
 import {parsePercent} from '../../util/number';
 import PieSeries, { PieSeriesOption } from './PieSeries';
 import { VectorArray } from 'zrender/src/core/vector';
-import { ZRAlign, ZRVerticalAlign } from '../../util/types';
-import { RectLike } from 'zrender/src/core/BoundingRect';
+import { ZRAlign, ZRVerticalAlign, ZRRectLike } from '../../util/types';
 
 var RADIAN = Math.PI / 180;
 
@@ -44,7 +43,7 @@ interface LabelLayout {
     labelAlignTo: PieSeriesOption['label']['alignTo'],
     labelMargin: number,
     bleedMargin: PieSeriesOption['label']['bleedMargin'],
-    textRect: RectLike,
+    textRect: ZRRectLike,
     text: string,
     font: string
 }
diff --git a/src/component/axisPointer/AxisPointerView.ts b/src/component/axisPointer/AxisPointerView.ts
index c93f8d9..f3a6665 100644
--- a/src/component/axisPointer/AxisPointerView.ts
+++ b/src/component/axisPointer/AxisPointerView.ts
@@ -17,17 +17,19 @@
 * under the License.
 */
 
-// @ts-nocheck
-
-import * as echarts from '../../echarts';
 import * as globalListener from './globalListener';
+import ComponentView from '../../view/Component';
+import AxisPointerModel from './AxisPointerModel';
+import GlobalModel from '../../model/Global';
+import ExtensionAPI from '../../ExtensionAPI';
+import TooltipModel from '../tooltip/TooltipModel';
 
-var AxisPointerView = echarts.extendComponentView({
-
-    type: 'axisPointer',
+class AxisPointerView extends ComponentView {
+    static type = 'axisPointer' as const
+    type = AxisPointerView.type
 
-    render: function (globalAxisPointerModel, ecModel, api) {
-        var globalTooltipModel = ecModel.getComponent('tooltip');
+    render(globalAxisPointerModel: AxisPointerModel, ecModel: GlobalModel, api: ExtensionAPI) {
+        var globalTooltipModel = ecModel.getComponent('tooltip') as TooltipModel;
         var triggerOn = globalAxisPointerModel.get('triggerOn')
             || (globalTooltipModel && globalTooltipModel.get('triggerOn') || 'mousemove|click');
 
@@ -50,24 +52,17 @@ var AxisPointerView = echarts.extendComponentView({
                 }
             }
         );
-    },
+    }
 
-    /**
-     * @override
-     */
-    remove: function (ecModel, api) {
-        globalListener.unregister(api.getZr(), 'axisPointer');
-        AxisPointerView.superApply(this._model, 'remove', arguments);
-    },
+    remove(ecModel: GlobalModel, api: ExtensionAPI) {
+        globalListener.unregister('axisPointer', api);
+    }
 
-    /**
-     * @override
-     */
-    dispose: function (ecModel, api) {
+    dispose(ecModel: GlobalModel, api: ExtensionAPI) {
         globalListener.unregister('axisPointer', api);
-        AxisPointerView.superApply(this._model, 'dispose', arguments);
     }
+}
 
-});
+ComponentView.registerClass(AxisPointerView);
 
 export default AxisPointerView;
\ No newline at end of file
diff --git a/src/component/axisPointer/globalListener.ts b/src/component/axisPointer/globalListener.ts
index 48cbfc6..84b85f7 100644
--- a/src/component/axisPointer/globalListener.ts
+++ b/src/component/axisPointer/globalListener.ts
@@ -17,14 +17,47 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as zrUtil from 'zrender/src/core/util';
 import env from 'zrender/src/core/env';
 import {makeInner} from '../../util/model';
+import ExtensionAPI from '../../ExtensionAPI';
+import { ZRenderType } from 'zrender/src/zrender';
+import { ZRElementEvent } from '../../util/types';
+import { Dictionary } from 'zrender/src/core/types';
+
+type DispatchActionMethod = ExtensionAPI['dispatchAction']
+
+type Handler = (
+    currTrigger: 'click' | 'mousemove' | 'leave',
+    event: ZRElementEvent,
+    dispatchAction: DispatchActionMethod
+) => void
+
+interface Record {
+    handler: Handler
+}
+
+interface InnerStore {
+    initialized: boolean
+    records: Dictionary<Record>
+}
+
+interface ShowTipPayload {
+    type: 'showTip'
+    dispatchAction: DispatchActionMethod
+}
+
+interface HideTipPayload {
+    type: 'hideTip'
+    dispatchAction: DispatchActionMethod
+}
+interface Pendings {
+    showTip: ShowTipPayload[]
+    hideTip: HideTipPayload[]
+}
 
-var inner = makeInner();
-var each = zrUtil.each;
+const inner = makeInner<InnerStore>();
+const each = zrUtil.each;
 
 /**
  * @param {string} key
@@ -33,7 +66,7 @@ var each = zrUtil.each;
  *      param: {string} currTrigger
  *      param: {Array.<number>} point
  */
-export function register(key, api, handler) {
+export function register(key: string, api: ExtensionAPI, handler?: Handler) {
     if (env.node) {
         return;
     }
@@ -43,11 +76,11 @@ export function register(key, api, handler) {
 
     initGlobalListeners(zr, api);
 
-    var record = inner(zr).records[key] || (inner(zr).records[key] = {});
+    var record = inner(zr).records[key] || (inner(zr).records[key] = {} as Record);
     record.handler = handler;
 }
 
-function initGlobalListeners(zr, api) {
+function initGlobalListeners(zr: ZRenderType, api?: ExtensionAPI) {
     if (inner(zr).initialized) {
         return;
     }
@@ -59,7 +92,10 @@ function initGlobalListeners(zr, api) {
     // useHandler('mouseout', onLeave);
     useHandler('globalout', onLeave);
 
-    function useHandler(eventType, cb) {
+    function useHandler(
+        eventType: string,
+        cb: (record: Record, e: ZRElementEvent, dispatchAction: DispatchActionMethod) => void
+    ) {
         zr.on(eventType, function (e) {
             var dis = makeDispatchAction(api);
 
@@ -72,7 +108,7 @@ function initGlobalListeners(zr, api) {
     }
 }
 
-function dispatchTooltipFinally(pendings, api) {
+function dispatchTooltipFinally(pendings: Pendings, api: ExtensionAPI) {
     var showLen = pendings.showTip.length;
     var hideLen = pendings.hideTip.length;
 
@@ -89,16 +125,25 @@ function dispatchTooltipFinally(pendings, api) {
     }
 }
 
-function onLeave(record, e, dispatchAction) {
+function onLeave(
+    record: Record,
+    e: ZRElementEvent,
+    dispatchAction: DispatchActionMethod
+) {
     record.handler('leave', null, dispatchAction);
 }
 
-function doEnter(currTrigger, record, e, dispatchAction) {
+function doEnter(
+    currTrigger: 'click' | 'mousemove' | 'leave',
+    record: Record,
+    e: ZRElementEvent,
+    dispatchAction: DispatchActionMethod
+) {
     record.handler(currTrigger, e, dispatchAction);
 }
 
-function makeDispatchAction(api) {
-    var pendings = {
+function makeDispatchAction(api: ExtensionAPI) {
+    var pendings: Pendings = {
         showTip: [],
         hideTip: []
     };
@@ -107,10 +152,10 @@ function makeDispatchAction(api) {
     // 'showTip' and 'hideTip' can be triggered by axisPointer and tooltip,
     // which may be conflict, (axisPointer call showTip but tooltip call hideTip);
     // So we have to add "final stage" to merge those dispatched actions.
-    var dispatchAction = function (payload) {
+    var dispatchAction = function (payload: ShowTipPayload | HideTipPayload) {
         var pendingList = pendings[payload.type];
         if (pendingList) {
-            pendingList.push(payload);
+            (pendingList as ShowTipPayload[]).push(payload as ShowTipPayload);
         }
         else {
             payload.dispatchAction = dispatchAction;
@@ -124,11 +169,7 @@ function makeDispatchAction(api) {
     };
 }
 
-/**
- * @param {string} key
- * @param {module:echarts/ExtensionAPI} api
- */
-export function unregister(key, api) {
+export function unregister(key: string, api: ExtensionAPI) {
     if (env.node) {
         return;
     }
diff --git a/src/component/legend/LegendView.ts b/src/component/legend/LegendView.ts
index 04402d1..35e0a16 100644
--- a/src/component/legend/LegendView.ts
+++ b/src/component/legend/LegendView.ts
@@ -27,10 +27,8 @@ import ComponentView from '../../view/Component';
 import LegendModel, { LegendOption, LegendSelectorButtonOption } from './LegendModel';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../ExtensionAPI';
-import { RectLike } from 'zrender/src/core/BoundingRect';
-import { ColorString, ZRTextAlign, ZRColor, ItemStyleOption } from '../../util/types';
+import { ColorString, ZRTextAlign, ZRColor, ItemStyleOption, ZRRectLike, ECElement, CommonTooltipOption } from '../../util/types';
 import Model from '../../model/Model';
-import ZImage from 'zrender/src/graphic/Image';
 
 var curry = zrUtil.curry;
 var each = zrUtil.each;
@@ -340,7 +338,7 @@ class LegendView extends ComponentView {
 
         var itemIcon = itemModel.get('icon');
 
-        var tooltipModel = itemModel.getModel('tooltip');
+        var tooltipModel = itemModel.getModel('tooltip') as Model<CommonTooltipOption>;
         var legendGlobalTooltipModel = tooltipModel.parentModel;
 
         // Use user given icon first
@@ -417,9 +415,10 @@ class LegendView extends ComponentView {
         // Add a invisible rect to increase the area of mouse hover
         var hitRect = new graphic.Rect({
             shape: itemGroup.getBoundingRect(),
-            invisible: true,
-            // @ts-ignore
-            tooltip: tooltipModel.get('show') ? zrUtil.extend({
+            invisible: true
+        });
+        if (tooltipModel.get('show')) {
+            (hitRect as ECElement).tooltip = zrUtil.extend({
                 content: name,
                 // Defaul formatter
                 formatter: legendGlobalTooltipModel.get('formatter', true) || function () {
@@ -431,8 +430,8 @@ class LegendView extends ComponentView {
                     name: name,
                     $vars: ['name']
                 }
-            }, tooltipModel.option) : null
-        });
+            }, tooltipModel.option);
+        }
         itemGroup.add(hitRect);
 
         itemGroup.eachChild(function (child) {
@@ -458,7 +457,7 @@ class LegendView extends ComponentView {
         isFirstRender: boolean,
         selector: LegendOption['selector'],
         selectorPosition: LegendOption['selectorPosition']
-    ): RectLike {
+    ): ZRRectLike {
         var contentGroup = this.getContentGroup();
         var selectorGroup = this.getSelectorGroup();
 
@@ -504,7 +503,7 @@ class LegendView extends ComponentView {
             selectorGroup.attr('position', selectorPos);
             contentGroup.attr('position', contentPos);
 
-            var mainRect = {x: 0, y: 0} as RectLike;
+            var mainRect = {x: 0, y: 0} as ZRRectLike;
             mainRect[wh] = contentRect[wh] + selectorButtonGap + selectorRect[wh];
             mainRect[hw] = Math.max(contentRect[hw], selectorRect[hw]);
             mainRect[yx] = Math.min(0, selectorRect[yx] + selectorPos[1 - orientIdx]);
@@ -527,7 +526,7 @@ class LegendView extends ComponentView {
 }
 
 function setSymbolStyle(
-    symbol: graphic.Path | ZImage,
+    symbol: graphic.Path | graphic.Image,
     symbolType: string,
     legendModelItemStyle: Model<ItemStyleOption>,
     borderColor: ZRColor,
diff --git a/src/component/legend/ScrollableLegendView.ts b/src/component/legend/ScrollableLegendView.ts
index 04de2c7..1a8e06d 100644
--- a/src/component/legend/ScrollableLegendView.ts
+++ b/src/component/legend/ScrollableLegendView.ts
@@ -29,10 +29,10 @@ import { LegendSelectorButtonOption } from './LegendModel';
 import ExtensionAPI from '../../ExtensionAPI';
 import GlobalModel from '../../model/Global';
 import ScrollableLegendModel, {ScrollableLegendOption} from './ScrollableLegendModel';
-import { RectLike } from 'zrender/src/core/BoundingRect';
 import Displayable from 'zrender/src/graphic/Displayable';
 import ComponentView from '../../view/Component';
 import Element from 'zrender/src/Element';
+import { ZRRectLike } from '../../util/types';
 
 var Group = graphic.Group;
 
@@ -298,7 +298,7 @@ class ScrollableLegendView extends LegendView {
         // Calculate `mainRect` and set `clipPath`.
         // mainRect should not be calculated by `this.group.getBoundingRect()`
         // for sake of the overflow.
-        var mainRect = {x: 0, y: 0} as RectLike;
+        var mainRect = {x: 0, y: 0} as ZRRectLike;
 
         // Consider content may be overflow (should be clipped).
         mainRect[wh] = showController ? maxSize[wh] : contentRect[wh];
diff --git a/src/component/tooltip/TooltipContent.ts b/src/component/tooltip/TooltipHTMLContent.ts
similarity index 65%
rename from src/component/tooltip/TooltipContent.ts
rename to src/component/tooltip/TooltipHTMLContent.ts
index 2b94d4e..879fdcc 100644
--- a/src/component/tooltip/TooltipContent.ts
+++ b/src/component/tooltip/TooltipHTMLContent.ts
@@ -17,14 +17,17 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as zrUtil from 'zrender/src/core/util';
 import * as zrColor from 'zrender/src/tool/color';
 import * as eventUtil from 'zrender/src/core/event';
 import * as domUtil from 'zrender/src/core/dom';
 import env from 'zrender/src/core/env';
 import * as formatUtil from '../../util/format';
+import ExtensionAPI from '../../ExtensionAPI';
+import { ZRenderType } from 'zrender/src/zrender';
+import { TooltipOption } from './TooltipModel';
+import Model from '../../model/Model';
+import { ZRRawEvent } from 'zrender/src/core/types';
 
 var each = zrUtil.each;
 var toCamelCase = formatUtil.toCamelCase;
@@ -33,12 +36,7 @@ var vendors = ['', '-webkit-', '-moz-', '-o-'];
 
 var gCssText = 'position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;';
 
-/**
- * @param {number} duration
- * @return {string}
- * @inner
- */
-function assembleTransition(duration) {
+function assembleTransition(duration: number): string {
     var transitionCurve = 'cubic-bezier(0.23, 1, 0.32, 1)';
     var transitionText = 'left ' + duration + 's ' + transitionCurve + ','
                         + 'top ' + duration + 's ' + transitionCurve;
@@ -52,7 +50,7 @@ function assembleTransition(duration) {
  * @return {string}
  * @inner
  */
-function assembleFont(textStyleModel) {
+function assembleFont(textStyleModel: Model<TooltipOption['textStyle']>): string {
     var cssText = [];
 
     var fontSize = textStyleModel.get('fontSize');
@@ -65,7 +63,7 @@ function assembleFont(textStyleModel) {
     fontSize
         && cssText.push('line-height:' + Math.round(fontSize * 3 / 2) + 'px');
 
-    each(['decoration', 'align'], function (name) {
+    each(['decoration', 'align'] as const, function (name) {
         var val = textStyleModel.get(name);
         val && cssText.push('text-' + name + ':' + val);
     });
@@ -73,12 +71,7 @@ function assembleFont(textStyleModel) {
     return cssText.join(';');
 }
 
-/**
- * @param {Object} tooltipModel
- * @return {string}
- * @inner
- */
-function assembleCssText(tooltipModel) {
+function assembleCssText(tooltipModel: Model<TooltipOption>) {
 
     var cssText = [];
 
@@ -105,9 +98,9 @@ function assembleCssText(tooltipModel) {
     }
 
     // Border style
-    each(['width', 'color', 'radius'], function (name) {
+    each(['width', 'color', 'radius'] as const, function (name) {
         var borderName = 'border-' + name;
-        var camelCase = toCamelCase(borderName);
+        var camelCase = toCamelCase(borderName) as 'borderWidth' | 'borderColor' | 'borderRadius';
         var val = tooltipModel.get(camelCase);
         val != null
             && cssText.push(borderName + ':' + val + (name === 'color' ? '' : 'px'));
@@ -125,7 +118,7 @@ function assembleCssText(tooltipModel) {
 }
 
 // If not able to make, do not modify the input `out`.
-function makeStyleCoord(out, zr, appendToBody, zrX, zrY) {
+function makeStyleCoord(out: number[], zr: ZRenderType, appendToBody: boolean, zrX: number, zrY: number) {
     var zrPainter = zr && zr.painter;
 
     if (appendToBody) {
@@ -149,104 +142,111 @@ function makeStyleCoord(out, zr, appendToBody, zrX, zrY) {
     }
 }
 
-/**
- * @alias module:echarts/component/tooltip/TooltipContent
- * @param {HTMLElement} container
- * @param {ExtensionAPI} api
- * @param {Object} [opt]
- * @param {boolean} [opt.appendToBody]
- *        `false`: the DOM element will be inside the container. Default value.
- *        `true`: the DOM element will be appended to HTML body, which avoid
- *                some overflow clip but intrude outside of the container.
- * @constructor
- */
-function TooltipContent(container, api, opt) {
-    if (env.wxa) {
-        return null;
-    }
+interface TooltipContentOption {
+    /**
+     * `false`: the DOM element will be inside the container. Default value.
+     * `true`: the DOM element will be appended to HTML body, which avoid
+     *  some overflow clip but intrude outside of the container.
+     */
+    appendToBody: boolean
+}
 
-    var el = document.createElement('div');
-    el.domBelongToZr = true;
-    this.el = el;
-    var zr = this._zr = api.getZr();
-    var appendToBody = this._appendToBody = opt && opt.appendToBody;
+class TooltipHTMLContent {
 
-    this._styleCoord = [0, 0];
+    el: HTMLDivElement
 
-    makeStyleCoord(this._styleCoord, zr, appendToBody, api.getWidth() / 2, api.getHeight() / 2);
+    private _container: HTMLElement
 
-    if (appendToBody) {
-        document.body.appendChild(el);
-    }
-    else {
-        container.appendChild(el);
-    }
+    private _show: boolean = false
 
-    this._container = container;
+    private _styleCoord: [number, number] = [0, 0]
+    private _appendToBody: boolean
 
-    this._show = false;
+    private _enterable = true
+    private _zr: ZRenderType
 
+    private _hideTimeout: number
     /**
-     * @private
+     * Hide delay time
      */
-    this._hideTimeout;
-
-    // FIXME
-    // Is it needed to trigger zr event manually if
-    // the browser do not support `pointer-events: none`.
-
-    var self = this;
-    el.onmouseenter = function () {
-        // clear the timeout in hideLater and keep showing tooltip
-        if (self._enterable) {
-            clearTimeout(self._hideTimeout);
-            self._show = true;
+    private _hideDelay: number
+
+    private _inContent: boolean
+
+
+    constructor(
+        container: HTMLElement,
+        api: ExtensionAPI,
+        opt: TooltipContentOption
+    ) {
+        if (env.wxa) {
+            return null;
         }
-        self._inContent = true;
-    };
-    el.onmousemove = function (e) {
-        e = e || window.event;
-        if (!self._enterable) {
-            // `pointer-events: none` is set to tooltip content div
-            // if `enterable` is set as `false`, and `el.onmousemove`
-            // can not be triggered. But in browser that do not
-            // support `pointer-events`, we need to do this:
-            // Try trigger zrender event to avoid mouse
-            // in and out shape too frequently
-            var handler = zr.handler;
-            var zrViewportRoot = zr.painter.getViewportRoot();
-            eventUtil.normalizeEvent(zrViewportRoot, e, true);
-            handler.dispatch('mousemove', e);
+
+        var el = document.createElement('div');
+        // TODO: TYPE
+        (el as any).domBelongToZr = true;
+        this.el = el;
+        var zr = this._zr = api.getZr();
+        var appendToBody = this._appendToBody = opt && opt.appendToBody;
+
+        makeStyleCoord(this._styleCoord, zr, appendToBody, api.getWidth() / 2, api.getHeight() / 2);
+
+        if (appendToBody) {
+            document.body.appendChild(el);
         }
-    };
-    el.onmouseleave = function () {
-        if (self._enterable) {
-            if (self._show) {
-                self.hideLater(self._hideDelay);
-            }
+        else {
+            container.appendChild(el);
         }
-        self._inContent = false;
-    };
-}
-
-TooltipContent.prototype = {
 
-    constructor: TooltipContent,
+        this._container = container;
 
-    /**
-     * @private
-     * @type {boolean}
-     */
-    _enterable: true,
+        // FIXME
+        // Is it needed to trigger zr event manually if
+        // the browser do not support `pointer-events: none`.
+
+        var self = this;
+        el.onmouseenter = function () {
+            // clear the timeout in hideLater and keep showing tooltip
+            if (self._enterable) {
+                clearTimeout(self._hideTimeout);
+                self._show = true;
+            }
+            self._inContent = true;
+        };
+        el.onmousemove = function (e) {
+            e = e || (window as any).event;
+            if (!self._enterable) {
+                // `pointer-events: none` is set to tooltip content div
+                // if `enterable` is set as `false`, and `el.onmousemove`
+                // can not be triggered. But in browser that do not
+                // support `pointer-events`, we need to do this:
+                // Try trigger zrender event to avoid mouse
+                // in and out shape too frequently
+                var handler = zr.handler;
+                var zrViewportRoot = zr.painter.getViewportRoot();
+                eventUtil.normalizeEvent(zrViewportRoot, e as ZRRawEvent, true);
+                handler.dispatch('mousemove', e);
+            }
+        };
+        el.onmouseleave = function () {
+            if (self._enterable) {
+                if (self._show) {
+                    self.hideLater(self._hideDelay);
+                }
+            }
+            self._inContent = false;
+        };
+    }
 
     /**
      * Update when tooltip is rendered
      */
-    update: function () {
+    update() {
         // FIXME
         // Move this logic to ec main?
         var container = this._container;
-        var stl = container.currentStyle
+        var stl = (container as any).currentStyle
             || document.defaultView.getComputedStyle(container);
         var domStyle = container.style;
         if (domStyle.position !== 'absolute' && stl.position !== 'absolute') {
@@ -255,9 +255,9 @@ TooltipContent.prototype = {
         // Hide the tooltip
         // PENDING
         // this.hide();
-    },
+    }
 
-    show: function (tooltipModel) {
+    show(tooltipModel: Model<TooltipOption>) {
         clearTimeout(this._hideTimeout);
         var el = this.el;
         var styleCoord = this._styleCoord;
@@ -279,58 +279,58 @@ TooltipContent.prototype = {
         el.style.pointerEvents = this._enterable ? 'auto' : 'none';
 
         this._show = true;
-    },
+    }
 
-    setContent: function (content) {
+    setContent(content: string) {
         this.el.innerHTML = content == null ? '' : content;
-    },
+    }
 
-    setEnterable: function (enterable) {
+    setEnterable(enterable: boolean) {
         this._enterable = enterable;
-    },
+    }
 
-    getSize: function () {
+    getSize() {
         var el = this.el;
         return [el.clientWidth, el.clientHeight];
-    },
+    }
 
-    moveTo: function (zrX, zrY) {
+    moveTo(zrX: number, zrY: number) {
         var styleCoord = this._styleCoord;
         makeStyleCoord(styleCoord, this._zr, this._appendToBody, zrX, zrY);
 
         var style = this.el.style;
         style.left = styleCoord[0] + 'px';
         style.top = styleCoord[1] + 'px';
-    },
+    }
 
-    hide: function () {
+    hide() {
         this.el.style.display = 'none';
         this._show = false;
-    },
+    }
 
-    hideLater: function (time) {
+    hideLater(time?: number) {
         if (this._show && !(this._inContent && this._enterable)) {
             if (time) {
                 this._hideDelay = time;
                 // Set show false to avoid invoke hideLater mutiple times
                 this._show = false;
-                this._hideTimeout = setTimeout(zrUtil.bind(this.hide, this), time);
+                this._hideTimeout = setTimeout(zrUtil.bind(this.hide, this), time) as any;
             }
             else {
                 this.hide();
             }
         }
-    },
+    }
 
-    isShow: function () {
+    isShow() {
         return this._show;
-    },
+    }
 
-    dispose: function () {
+    dispose() {
         this.el.parentNode.removeChild(this.el);
-    },
+    }
 
-    getOuterSize: function () {
+    getOuterSize() {
         var width = this.el.clientWidth;
         var height = this.el.clientHeight;
 
@@ -347,6 +347,6 @@ TooltipContent.prototype = {
         return {width: width, height: height};
     }
 
-};
+}
 
-export default TooltipContent;
+export default TooltipHTMLContent;
diff --git a/src/component/tooltip/TooltipModel.ts b/src/component/tooltip/TooltipModel.ts
index b493bd7..dd945f4 100644
--- a/src/component/tooltip/TooltipModel.ts
+++ b/src/component/tooltip/TooltipModel.ts
@@ -20,45 +20,23 @@
 import ComponentModel from '../../model/Component';
 import {
     ComponentOption,
-    CallbackDataParams,
-    ZRAlign,
-    ZRVerticalAlign,
-    ColorString,
     LabelOption,
-    TooltipRenderMode,
-    LineStyleOption
+    LineStyleOption,
+    CommonTooltipOption,
+    TooltipRenderMode
 } from '../../util/types';
-import { RectLike } from 'zrender/src/core/BoundingRect';
 import {AxisPointerOption} from '../axisPointer/AxisPointerModel';
 
-/**
- * Position relative to the hoverred element. Only available when trigger is item.
- */
-type BuitlinPosition = 'inside' | 'top' | 'left' | 'right' | 'bottom'
-interface PositionCallback {
-    (
-        point: [number, number],
-        params: CallbackDataParams,
-        dom: HTMLElement | null,
-        /**
-         * Rect of hover elements. Will be null if not hovered
-         */
-        rect: RectLike | null,
-        size: {
-            /**
-             * Size of popup content
-             */
-            contentSize: [number, number]
-            /**
-             * Size of the chart view
-             */
-            viewSize: [number, number]
-        }
-    ): number[] | string[] | BuitlinPosition
-}
-interface TooltipOption extends ComponentOption {
-    show?: boolean
 
+export interface TooltipOption extends CommonTooltipOption, ComponentOption {
+
+    axisPointer?: AxisPointerOption & {
+        axis?: 'auto' | 'x' | 'y' | 'angle' | 'radius'
+        crossStyle?: LineStyleOption & {
+            // TODO
+            textStyle?: LabelOption
+        }
+    }
     /**
      * If show popup content
      */
@@ -67,14 +45,6 @@ interface TooltipOption extends ComponentOption {
      * Trigger only works on coordinate system.
      */
     trigger?: 'item' | 'axis' | 'none'
-    /**
-     * When to trigger
-     */
-    triggerOn?: 'mousemove' | 'click' | 'none' | 'mousemove|click'
-    /**
-     * Whether to not hide popup content automatically
-     */
-    alwaysShowContent?: boolean
 
     displayMode?: 'single' | 'multipleByCoordSys';
 
@@ -86,70 +56,10 @@ interface TooltipOption extends ComponentOption {
     renderMode?: 'auto' | TooltipRenderMode   // TODO richText renamed canvas?
 
     /**
-     * Absolution pixel [x, y] array. Or relative percent string [x, y] array.
-     * If trigger is 'item'. position can be set to 'inside' / 'top' / 'left' / 'right' / 'bottom',
-     * which is relative to the hovered element.
-     *
-     * Support to be a callback
-     */
-    position?: number[] | string[] | BuitlinPosition | PositionCallback
-
-    confine?: boolean
-
-    /**
-     * Consider triggered from axisPointer handle, verticalAlign should be 'middle'
-     */
-    align?: ZRAlign
-
-    verticalAlign?: ZRVerticalAlign
-    /**
-     * Delay of show. milesecond.
-     */
-    showDelay?: number
-
-    /**
-     * Delay of hide. milesecond.
-     */
-    hideDelay?: number
-
-    transitionDuration?: number
-    /**
-     * Whether mouse is allowed to enter the floating layer of tooltip
-     * If you need to interact in the tooltip like with links or buttons, it can be set as true.
-     * @default false
-     */
-    enterable?: boolean
-
-    backgroundColor?: ColorString
-    borderColor?: ColorString
-    /**
-     * @default 4
-     */
-    borderRadius?: number
-    borderWidth?: number
-
-    /**
-     * Padding between tooltip content and tooltip border.
+     * If append popup dom to document.body
+     * Only available when renderMode is html
      */
-    padding?: number | number[]
-
-    /**
-     * Available when renderMode is 'html'
-     */
-    extraCssText?: string
-
-    textStyle?: Pick<LabelOption,
-        'color' | 'fontStyle' | 'fontWeight' | 'fontFamily' | 'fontSize' |
-        'lineHeight' | 'width' | 'height' | 'textBorderColor' | 'textBorderWidth' |
-        'textShadowColor' | 'textShadowBlur' | 'textShadowOffsetX' | 'textShadowOffsetY'>
-
-    axisPointer?: AxisPointerOption & {
-        axis?: 'auto' | 'x' | 'y' | 'angle' | 'radius'
-        crossStyle?: LineStyleOption & {
-            // TODO
-            textStyle?: LabelOption
-        }
-    }
+    appendToBody?: boolean
 }
 
 class TooltipModel extends ComponentModel<TooltipOption> {
@@ -246,4 +156,6 @@ class TooltipModel extends ComponentModel<TooltipOption> {
     }
 }
 
-ComponentModel.registerClass(TooltipModel);
\ No newline at end of file
+ComponentModel.registerClass(TooltipModel);
+
+export default TooltipModel;
\ No newline at end of file
diff --git a/src/component/tooltip/TooltipRichContent.ts b/src/component/tooltip/TooltipRichContent.ts
index 48b33ef..2890625 100644
--- a/src/component/tooltip/TooltipRichContent.ts
+++ b/src/component/tooltip/TooltipRichContent.ts
@@ -17,67 +17,67 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as zrUtil from 'zrender/src/core/util';
 // import Group from 'zrender/src/container/Group';
 import Text from 'zrender/src/graphic/Text';
+import ExtensionAPI from '../../ExtensionAPI';
+import { ZRenderType } from 'zrender/src/zrender';
+import TooltipModel, { TooltipOption } from './TooltipModel';
+import * as graphic from '../../util/graphic';
+import { Dictionary } from 'zrender/src/core/types';
+import { ColorString } from '../../util/types';
+import { StyleProps } from 'zrender/src/graphic/Style';
+import Model from '../../model/Model';
 
-/**
- * @alias module:echarts/component/tooltip/TooltipRichContent
- * @constructor
- */
-function TooltipRichContent(api) {
+class TooltipRichContent {
 
-    this._zr = api.getZr();
+    private _zr: ZRenderType
 
-    this._show = false;
+    private _show = false
 
-    /**
-     * @private
-     */
-    this._hideTimeout;
-}
+    private _hideTimeout: number
 
-TooltipRichContent.prototype = {
+    private _enterable = true
 
-    constructor: TooltipRichContent,
+    private _inContent: boolean
 
-    /**
-     * @private
-     * @type {boolean}
-     */
-    _enterable: true,
+    private _hideDelay: number
+
+    el: graphic.Text
+
+    constructor(api: ExtensionAPI) {
+        this._zr = api.getZr();
+    }
 
     /**
      * Update when tooltip is rendered
      */
-    update: function () {
+    update() {
         // noop
-    },
+    }
 
-    show: function (tooltipModel) {
+    show() {
         if (this._hideTimeout) {
             clearTimeout(this._hideTimeout);
         }
 
-        this.el.attr('show', true);
+        this.el.show();
         this._show = true;
-    },
+    }
 
     /**
      * Set tooltip content
-     *
-     * @param {string} content rich text string of content
-     * @param {Object} markerRich rich text style
-     * @param {Object} tooltipModel tooltip model
      */
-    setContent: function (content, markerRich, tooltipModel) {
+    setContent(
+        content: string,
+        markerRich: Dictionary<ColorString>,
+        tooltipModel: Model<TooltipOption>
+    ) {
         if (this.el) {
             this._zr.remove(this.el);
         }
 
-        var markers = {};
+        var markers: StyleProps['rich'] = {};
         var text = content;
         var prefix = '{marker';
         var suffix = '|}';
@@ -90,9 +90,10 @@ TooltipRichContent.prototype = {
                     textWidth: 4,
                     textHeight: 4,
                     textBorderRadius: 2,
-                    textBackgroundColor: markerRich[name],
+                    textBackgroundColor: markerRich[name]
+
                     // TODO: textOffset is not implemented for rich text
-                    textOffset: [3, 0]
+                    // textOffset: [3, 0]
                 };
             }
             else {
@@ -115,7 +116,7 @@ TooltipRichContent.prototype = {
                 textLineHeight: 20,
                 textBackgroundColor: tooltipModel.get('backgroundColor'),
                 textBorderRadius: tooltipModel.get('borderRadius'),
-                textFill: tooltipModel.get('textStyle.color'),
+                textFill: tooltipModel.get(['textStyle', 'color']),
                 textPadding: tooltipModel.get('padding')
             },
             z: tooltipModel.get('z')
@@ -139,55 +140,60 @@ TooltipRichContent.prototype = {
             }
             self._inContent = false;
         });
-    },
+    }
 
-    setEnterable: function (enterable) {
+    setEnterable(enterable?: boolean) {
         this._enterable = enterable;
-    },
+    }
 
-    getSize: function () {
+    getSize() {
         var bounding = this.el.getBoundingRect();
         return [bounding.width, bounding.height];
-    },
+    }
 
-    moveTo: function (x, y) {
+    moveTo(x: number, y: number) {
         if (this.el) {
             this.el.attr('position', [x, y]);
         }
-    },
+    }
 
-    hide: function () {
+    hide() {
         if (this.el) {
             this.el.hide();
         }
         this._show = false;
-    },
+    }
 
-    hideLater: function (time) {
+    hideLater(time?: number) {
         if (this._show && !(this._inContent && this._enterable)) {
             if (time) {
                 this._hideDelay = time;
                 // Set show false to avoid invoke hideLater mutiple times
                 this._show = false;
-                this._hideTimeout = setTimeout(zrUtil.bind(this.hide, this), time);
+                this._hideTimeout = setTimeout(zrUtil.bind(this.hide, this), time) as any;
             }
             else {
                 this.hide();
             }
         }
-    },
+    }
 
-    isShow: function () {
+    isShow() {
         return this._show;
-    },
+    }
 
-    getOuterSize: function () {
+    getOuterSize() {
         var size = this.getSize();
         return {
             width: size[0],
             height: size[1]
         };
     }
-};
+
+    dispose() {
+        this._zr.remove(this.el);
+    }
+}
+
 
 export default TooltipRichContent;
diff --git a/src/component/tooltip/TooltipView.ts b/src/component/tooltip/TooltipView.ts
index 366b843..0cc6199 100644
--- a/src/component/tooltip/TooltipView.ts
+++ b/src/component/tooltip/TooltipView.ts
@@ -16,13 +16,9 @@
 * specific language governing permissions and limitations
 * under the License.
 */
-
-// @ts-nocheck
-
-import * as echarts from '../../echarts';
 import * as zrUtil from 'zrender/src/core/util';
 import env from 'zrender/src/core/env';
-import TooltipContent from './TooltipContent';
+import TooltipHTMLContent from './TooltipHTMLContent';
 import TooltipRichContent from './TooltipRichContent';
 import * as formatUtil from '../../util/format';
 import * as numberUtil from '../../util/number';
@@ -34,31 +30,154 @@ import * as globalListener from '../axisPointer/globalListener';
 import * as axisHelper from '../../coord/axisHelper';
 import * as axisPointerViewHelper from '../axisPointer/viewHelper';
 import { getTooltipRenderMode } from '../../util/model';
-
-var bind = zrUtil.bind;
-var each = zrUtil.each;
-var parsePercent = numberUtil.parsePercent;
-
-var proxyRect = new graphic.Rect({
+import ComponentView from '../../view/Component';
+import {
+    ZRAlign,
+    ZRVerticalAlign,
+    ZRRectLike,
+    BoxLayoutOptionMixin,
+    CallbackDataParams,
+    TooltipRenderMode,
+    ECElement,
+    ColorString,
+    CommonTooltipOption
+} from '../../util/types';
+import GlobalModel from '../../model/Global';
+import ExtensionAPI from '../../ExtensionAPI';
+import TooltipModel, {TooltipOption} from './TooltipModel';
+import Element from 'zrender/src/Element';
+import { Dictionary } from 'zrender/src/core/types';
+import Axis from '../../coord/Axis';
+import ComponentModel from '../../model/Component';
+
+const bind = zrUtil.bind;
+const each = zrUtil.each;
+const parsePercent = numberUtil.parsePercent;
+
+const proxyRect = new graphic.Rect({
     shape: {x: -1, y: -1, width: 2, height: 2}
 });
 
-export default echarts.extendComponentView({
+type AxisModel = ComponentModel & {
+    axis: Axis
+}
+
+interface DataIndex {
+    seriesIndex: number
+    dataIndex: number
+
+    dataIndexInside: number
+}
+interface DataByAxis {
+    // TODO: TYPE Value type
+    value: string | number
+    axisIndex: number
+    axisDim: string
+    axisType: string
+    axisId: string
+
+    seriesDataIndices: DataIndex[]
+}
+interface DataByCoordSys {
+    dataByAxis: DataByAxis[]
+}
+
+interface ShowTipPayload {
+    type?: 'showTip'
+    from?: string
+
+    // Type 1
+    tooltip?: ECElement['tooltip']
+
+    // Type 2
+    dataByCoordSys?: DataByCoordSys[]
+    tooltipOption?: CommonTooltipOption
+
+    // Type 3
+    seriesIndex?: number
+    dataIndex?: number
+
+
+    x?: number
+    y?: number
+    position?: TooltipOption['position']
+
+    dispatchAction?: ExtensionAPI['dispatchAction']
+}
+
+interface HideTipPayload {
+    type?: 'hideTip'
+    from?: string
+
+    dispatchAction?: ExtensionAPI['dispatchAction']
+}
+
+interface TryShowParams {
+    target?: ECElement,
 
-    type: 'tooltip',
+    offsetX?: number
+    offsetY?: number
 
-    init: function (ecModel, api) {
+    /**
+     * Used for axis trigger.
+     */
+    dataByCoordSys?: DataByCoordSys[]
+
+    tooltipOption?: CommonTooltipOption
+
+    position?: TooltipOption['position']
+}
+
+type TooltipDataParams = CallbackDataParams & {
+    axisDim?: string
+    axisIndex?: number
+    axisType?: string
+    axisId?: string
+    // TODO: TYPE Value type
+    axisValue?: string | number
+    axisValueLabel?: string
+}
+class TooltipView extends ComponentView {
+    static type = 'tooltip' as const
+    type = TooltipView.type
+
+    private _renderMode: TooltipRenderMode
+
+    private _newLine: '<br/>' | '\n'
+
+    private _tooltipModel: TooltipModel
+
+    private _ecModel: GlobalModel
+
+    private _api: ExtensionAPI
+
+    private _alwaysShowContent: boolean
+
+    private _tooltipContent: TooltipHTMLContent | TooltipRichContent
+
+    private _refreshUpdateTimeout: number
+
+    private _lastX: number
+    private _lastY: number
+
+    private _ticket: string
+
+    private _showTimout: number
+
+    private _lastDataByCoordSys: DataByCoordSys[]
+
+    init(ecModel: GlobalModel, api: ExtensionAPI) {
         if (env.node) {
             return;
         }
 
-        var tooltipModel = ecModel.getComponent('tooltip');
+        var tooltipModel = ecModel.getComponent('tooltip') as TooltipModel;
         var renderMode = tooltipModel.get('renderMode');
         this._renderMode = getTooltipRenderMode(renderMode);
 
         var tooltipContent;
         if (this._renderMode === 'html') {
-            tooltipContent = new TooltipContent(api.getDom(), api, {
+            tooltipContent = new TooltipHTMLContent(api.getDom(), api, {
                 appendToBody: tooltipModel.get('appendToBody', true)
             });
             this._newLine = '<br/>';
@@ -69,9 +188,13 @@ export default echarts.extendComponentView({
         }
 
         this._tooltipContent = tooltipContent;
-    },
+    }
 
-    render: function (tooltipModel, ecModel, api) {
+    render(
+        tooltipModel: TooltipModel,
+        ecModel: GlobalModel,
+        api: ExtensionAPI
+    ) {
         if (env.node) {
             return;
         }
@@ -79,29 +202,13 @@ export default echarts.extendComponentView({
         // Reset
         this.group.removeAll();
 
-        /**
-         * @private
-         * @type {module:echarts/component/tooltip/TooltipModel}
-         */
         this._tooltipModel = tooltipModel;
 
-        /**
-         * @private
-         * @type {module:echarts/model/Global}
-         */
         this._ecModel = ecModel;
 
-        /**
-         * @private
-         * @type {module:echarts/ExtensionAPI}
-         */
         this._api = api;
 
-        /**
-         * Should be cleaned when render.
-         * @private
-         * @type {Array.<Array.<Object>>}
-         */
+        // Should be cleaned when render.
         this._lastDataByCoordSys = null;
 
         /**
@@ -117,9 +224,9 @@ export default echarts.extendComponentView({
         this._initGlobalListener();
 
         this._keepShow();
-    },
+    }
 
-    _initGlobalListener: function () {
+    _initGlobalListener() {
         var tooltipModel = this._tooltipModel;
         var triggerOn = tooltipModel.get('triggerOn');
 
@@ -138,9 +245,9 @@ export default echarts.extendComponentView({
                 }
             }, this)
         );
-    },
+    }
 
-    _keepShow: function () {
+    _keepShow() {
         var tooltipModel = this._tooltipModel;
         var ecModel = this._ecModel;
         var api = this._api;
@@ -165,7 +272,7 @@ export default echarts.extendComponentView({
                 });
             });
         }
-    },
+    }
 
     /**
      * Show tip manually by
@@ -183,7 +290,12 @@ export default echarts.extendComponentView({
      *
      *  TODO Batch
      */
-    manuallyShowTip: function (tooltipModel, ecModel, api, payload) {
+    manuallyShowTip(
+        tooltipModel: TooltipModel,
+        ecModel: GlobalModel,
+        api: ExtensionAPI,
+        payload: ShowTipPayload
+    ) {
         if (payload.from === this.uid || env.node) {
             return;
         }
@@ -197,7 +309,7 @@ export default echarts.extendComponentView({
         var dataByCoordSys = payload.dataByCoordSys;
 
         if (payload.tooltip && payload.x != null && payload.y != null) {
-            var el = proxyRect;
+            var el = proxyRect as ECElement;
             el.position = [payload.x, payload.y];
             el.update();
             el.tooltip = payload.tooltip;
@@ -213,7 +325,7 @@ export default echarts.extendComponentView({
                 offsetX: payload.x,
                 offsetY: payload.y,
                 position: payload.position,
-                dataByCoordSys: payload.dataByCoordSys,
+                dataByCoordSys: dataByCoordSys,
                 tooltipOption: payload.tooltipOption
             }, dispatchAction);
         }
@@ -251,9 +363,14 @@ export default echarts.extendComponentView({
                 target: api.getZr().findHover(payload.x, payload.y).target
             }, dispatchAction);
         }
-    },
+    }
 
-    manuallyHideTip: function (tooltipModel, ecModel, api, payload) {
+    manuallyHideTip(
+        tooltipModel: TooltipModel,
+        ecModel: GlobalModel,
+        api: ExtensionAPI,
+        payload: HideTipPayload
+    ) {
         var tooltipContent = this._tooltipContent;
 
         if (!this._alwaysShowContent && this._tooltipModel) {
@@ -265,14 +382,20 @@ export default echarts.extendComponentView({
         if (payload.from !== this.uid) {
             this._hide(makeDispatchAction(payload, api));
         }
-    },
+    }
 
     // Be compatible with previous design, that is, when tooltip.type is 'axis' and
     // dispatchAction 'showTip' with seriesIndex and dataIndex will trigger axis pointer
     // and tooltip.
-    _manuallyAxisShowTip: function (tooltipModel, ecModel, api, payload) {
+    _manuallyAxisShowTip(
+        tooltipModel: TooltipModel,
+        ecModel: GlobalModel,
+        api: ExtensionAPI,
+        payload: ShowTipPayload
+    ) {
         var seriesIndex = payload.seriesIndex;
         var dataIndex = payload.dataIndex;
+        // @ts-ignore
         var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo;
 
         if (seriesIndex == null || dataIndex == null || coordSysAxesInfo == null) {
@@ -285,14 +408,14 @@ export default echarts.extendComponentView({
         }
 
         var data = seriesModel.getData();
-        var tooltipModel = buildTooltipModel([
+        var tooltipCascadedModel = buildTooltipModel([
             data.getItemModel(dataIndex),
             seriesModel,
             (seriesModel.coordinateSystem || {}).model,
             tooltipModel
         ]);
 
-        if (tooltipModel.get('trigger') !== 'axis') {
+        if (tooltipCascadedModel.get('trigger') !== 'axis') {
             return;
         }
 
@@ -304,9 +427,12 @@ export default echarts.extendComponentView({
         });
 
         return true;
-    },
+    }
 
-    _tryShow: function (e, dispatchAction) {
+    _tryShow(
+        e: TryShowParams,
+        dispatchAction: ExtensionAPI['dispatchAction']
+    ) {
         var el = e.target;
         var tooltipModel = this._tooltipModel;
 
@@ -336,9 +462,12 @@ export default echarts.extendComponentView({
             this._lastDataByCoordSys = null;
             this._hide(dispatchAction);
         }
-    },
+    }
 
-    _showOrMove: function (tooltipModel, cb) {
+    _showOrMove(
+        tooltipModel: Model<TooltipOption>,
+        cb: () => void
+    ) {
         // showDelay is used in this case: tooltip.enterable is set
         // as true. User intent to move mouse into tooltip and click
         // something. `showDelay` makes it easyer to enter the content
@@ -347,18 +476,21 @@ export default echarts.extendComponentView({
         cb = zrUtil.bind(cb, this);
         clearTimeout(this._showTimout);
         delay > 0
-            ? (this._showTimout = setTimeout(cb, delay))
+            ? (this._showTimout = setTimeout(cb, delay) as any)
             : cb();
-    },
+    }
 
-    _showAxisTooltip: function (dataByCoordSys, e) {
+    _showAxisTooltip(
+        dataByCoordSys: DataByCoordSys[],
+        e: TryShowParams
+    ) {
         var ecModel = this._ecModel;
         var globalTooltipModel = this._tooltipModel;
 
         var point = [e.offsetX, e.offsetY];
 
-        var singleDefaultHTML = [];
-        var singleParamsList = [];
+        var singleDefaultHTML: string[] = [];
+        var singleParamsList: TooltipDataParams[] = [];
         var singleTooltipModel = buildTooltipModel([
             e.tooltipOption,
             globalTooltipModel
@@ -384,32 +516,35 @@ export default echarts.extendComponentView({
             each(itemCoordSys.dataByAxis, function (item) {
                 var axisModel = ecModel.getComponent(item.axisDim + 'Axis', item.axisIndex);
                 var axisValue = item.value;
-                var seriesDefaultHTML = [];
+                var seriesDefaultHTML: string[] = [];
 
                 if (!axisModel || axisValue == null) {
                     return;
                 }
 
                 var valueLabel = axisPointerViewHelper.getValueLabel(
-                    axisValue, axisModel.axis, ecModel,
+                    axisValue, (axisModel as AxisModel).axis, ecModel,
                     item.seriesDataIndices,
+                    // @ts-ignore
                     item.valueLabelOpt
                 );
 
                 zrUtil.each(item.seriesDataIndices, function (idxItem) {
                     var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);
                     var dataIndex = idxItem.dataIndexInside;
-                    var dataParams = series && series.getDataParams(dataIndex);
+                    var dataParams = series && series.getDataParams(dataIndex) as TooltipDataParams;
                     dataParams.axisDim = item.axisDim;
                     dataParams.axisIndex = item.axisIndex;
                     dataParams.axisType = item.axisType;
                     dataParams.axisId = item.axisId;
-                    dataParams.axisValue = axisHelper.getAxisRawValue(axisModel.axis, axisValue);
+                    dataParams.axisValue = axisHelper.getAxisRawValue((axisModel as AxisModel).axis, axisValue);
                     dataParams.axisValueLabel = valueLabel;
 
                     if (dataParams) {
                         singleParamsList.push(dataParams);
-                        var seriesTooltip = series.formatTooltip(dataIndex, true, null, renderMode);
+                        var seriesTooltip = series.formatTooltip(
+                            dataIndex, true, null, renderMode as TooltipRenderMode
+                        );
 
                         var html;
                         if (zrUtil.isObject(seriesTooltip)) {
@@ -443,10 +578,10 @@ export default echarts.extendComponentView({
 
         // In most case, the second axis is shown upper than the first one.
         singleDefaultHTML.reverse();
-        singleDefaultHTML = singleDefaultHTML.join(this._newLine + this._newLine);
+        const singleDefaultHTMLStr = singleDefaultHTML.join(this._newLine + this._newLine);
 
         var positionExpr = e.position;
-        this._showOrMove(singleTooltipModel, function () {
+        this._showOrMove(singleTooltipModel, function (this: TooltipView) {
             if (this._updateContentNotChangedOnAxis(dataByCoordSys)) {
                 this._updatePosition(
                     singleTooltipModel,
@@ -458,7 +593,7 @@ export default echarts.extendComponentView({
             }
             else {
                 this._showTooltipContent(
-                    singleTooltipModel, singleDefaultHTML, singleParamsList, Math.random(),
+                    singleTooltipModel, singleDefaultHTMLStr, singleParamsList, Math.random() + '',
                     point[0], point[1], positionExpr, undefined, markers
                 );
             }
@@ -466,9 +601,13 @@ export default echarts.extendComponentView({
 
         // Do not trigger events here, because this branch only be entered
         // from dispatchAction.
-    },
+    }
 
-    _showSeriesItemTooltip: function (e, el, dispatchAction) {
+    _showSeriesItemTooltip(
+        e: TryShowParams,
+        el: ECElement,
+        dispatchAction: ExtensionAPI['dispatchAction']
+    ) {
         var ecModel = this._ecModel;
         // Use dataModel in element if possible
         // Used when mouseover on a element like markPoint or edge
@@ -496,8 +635,8 @@ export default echarts.extendComponentView({
 
         var params = dataModel.getDataParams(dataIndex, dataType);
         var seriesTooltip = dataModel.formatTooltip(dataIndex, false, dataType, this._renderMode);
-        var defaultHtml;
-        var markers;
+        var defaultHtml: string;
+        var markers: Dictionary<ColorString>;
         if (zrUtil.isObject(seriesTooltip)) {
             defaultHtml = seriesTooltip.html;
             markers = seriesTooltip.markers;
@@ -509,7 +648,7 @@ export default echarts.extendComponentView({
 
         var asyncTicket = 'item_' + dataModel.name + '_' + dataIndex;
 
-        this._showOrMove(tooltipModel, function () {
+        this._showOrMove(tooltipModel, function (this: TooltipView) {
             this._showTooltipContent(
                 tooltipModel, defaultHtml, params, asyncTicket,
                 e.offsetX, e.offsetY, e.position, e.target, markers
@@ -525,9 +664,13 @@ export default echarts.extendComponentView({
             seriesIndex: seriesIndex,
             from: this.uid
         });
-    },
+    }
 
-    _showComponentItemTooltip: function (e, el, dispatchAction) {
+    _showComponentItemTooltip(
+        e: TryShowParams,
+        el: ECElement,
+        dispatchAction: ExtensionAPI['dispatchAction']
+    ) {
         var tooltipOpt = el.tooltip;
         if (typeof tooltipOpt === 'string') {
             var content = tooltipOpt;
@@ -539,13 +682,13 @@ export default echarts.extendComponentView({
         }
         var subTooltipModel = new Model(tooltipOpt, this._tooltipModel, this._ecModel);
         var defaultHtml = subTooltipModel.get('content');
-        var asyncTicket = Math.random();
+        var asyncTicket = Math.random() + '';
 
         // Do not check whether `trigger` is 'none' here, because `trigger`
         // only works on cooridinate system. In fact, we have not found case
         // that requires setting `trigger` nothing on component yet.
 
-        this._showOrMove(subTooltipModel, function () {
+        this._showOrMove(subTooltipModel, function (this: TooltipView) {
             this._showTooltipContent(
                 subTooltipModel, defaultHtml, subTooltipModel.get('formatterParams') || {},
                 asyncTicket, e.offsetX, e.offsetY, e.position, el
@@ -557,10 +700,20 @@ export default echarts.extendComponentView({
             type: 'showTip',
             from: this.uid
         });
-    },
+    }
 
-    _showTooltipContent: function (
-        tooltipModel, defaultHtml, params, asyncTicket, x, y, positionExpr, el, markers
+    _showTooltipContent(
+        // Use Model<TooltipOption> insteadof TooltipModel because this model may be from series or other options.
+        // Instead of top level tooltip.
+        tooltipModel: Model<TooltipOption>,
+        defaultHtml: string,
+        params: TooltipDataParams | TooltipDataParams[],
+        asyncTicket: string,
+        x: number,
+        y: number,
+        positionExpr: TooltipOption['position'],
+        el: ECElement,
+        markers?: Dictionary<ColorString>
     ) {
         // Reset ticket
         this._ticket = '';
@@ -579,7 +732,7 @@ export default echarts.extendComponentView({
             html = formatUtil.formatTpl(formatter, params, true);
         }
         else if (typeof formatter === 'function') {
-            var callback = bind(function (cbTicket, html) {
+            var callback = bind(function (cbTicket: string, html: string) {
                 if (cbTicket === this._ticket) {
                     tooltipContent.setContent(html, markers, tooltipModel);
                     this._updatePosition(
@@ -597,19 +750,17 @@ export default echarts.extendComponentView({
         this._updatePosition(
             tooltipModel, positionExpr, x, y, tooltipContent, params, el
         );
-    },
+    }
 
-    /**
-     * @param  {string|Function|Array.<number>|Object} positionExpr
-     * @param  {number} x Mouse x
-     * @param  {number} y Mouse y
-     * @param  {boolean} confine Whether confine tooltip content in view rect.
-     * @param  {Object|<Array.<Object>} params
-     * @param  {module:zrender/Element} el target element
-     * @param  {module:echarts/ExtensionAPI} api
-     * @return {Array.<number>}
-     */
-    _updatePosition: function (tooltipModel, positionExpr, x, y, content, params, el) {
+    _updatePosition(
+        tooltipModel: Model<TooltipOption>,
+        positionExpr: TooltipOption['position'],
+        x: number,  // Mouse x
+        y: number,  // Mouse y
+        content: TooltipHTMLContent | TooltipRichContent,
+        params:  TooltipDataParams | TooltipDataParams[],
+        el?: Element
+    ) {
         var viewWidth = this._api.getWidth();
         var viewHeight = this._api.getHeight();
 
@@ -625,7 +776,7 @@ export default echarts.extendComponentView({
             // Callback of position can be an array or a string specify the position
             positionExpr = positionExpr([x, y], params, content.el, rect, {
                 viewSize: [viewWidth, viewHeight],
-                contentSize: contentSize.slice()
+                contentSize: contentSize.slice() as [number, number]
             });
         }
 
@@ -634,10 +785,11 @@ export default echarts.extendComponentView({
             y = parsePercent(positionExpr[1], viewHeight);
         }
         else if (zrUtil.isObject(positionExpr)) {
-            positionExpr.width = contentSize[0];
-            positionExpr.height = contentSize[1];
+            const boxLayoutPosition = positionExpr as BoxLayoutOptionMixin;
+            boxLayoutPosition.width = contentSize[0];
+            boxLayoutPosition.height = contentSize[1];
             var layoutRect = layoutUtil.getLayoutRect(
-                positionExpr, {width: viewWidth, height: viewHeight}
+                boxLayoutPosition, {width: viewWidth, height: viewHeight}
             );
             x = layoutRect.x;
             y = layoutRect.y;
@@ -648,14 +800,14 @@ export default echarts.extendComponentView({
         }
         // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element
         else if (typeof positionExpr === 'string' && el) {
-            var pos = calcTooltipPosition(
+            const pos = calcTooltipPosition(
                 positionExpr, rect, contentSize
             );
             x = pos[0];
             y = pos[1];
         }
         else {
-            var pos = refixTooltipPosition(
+            const pos = refixTooltipPosition(
                 x, y, content, viewWidth, viewHeight, align ? null : 20, vAlign ? null : 20
             );
             x = pos[0];
@@ -666,7 +818,7 @@ export default echarts.extendComponentView({
         vAlign && (y -= isCenterAlign(vAlign) ? contentSize[1] / 2 : vAlign === 'bottom' ? contentSize[1] : 0);
 
         if (tooltipModel.get('confine')) {
-            var pos = confineTooltipPosition(
+            const pos = confineTooltipPosition(
                 x, y, content, viewWidth, viewHeight
             );
             x = pos[0];
@@ -674,36 +826,36 @@ export default echarts.extendComponentView({
         }
 
         content.moveTo(x, y);
-    },
+    }
 
     // FIXME
     // Should we remove this but leave this to user?
-    _updateContentNotChangedOnAxis: function (dataByCoordSys) {
+    _updateContentNotChangedOnAxis(dataByCoordSys: DataByCoordSys[]) {
         var lastCoordSys = this._lastDataByCoordSys;
         var contentNotChanged = !!lastCoordSys
             && lastCoordSys.length === dataByCoordSys.length;
 
         contentNotChanged && each(lastCoordSys, function (lastItemCoordSys, indexCoordSys) {
-            var lastDataByAxis = lastItemCoordSys.dataByAxis || {};
-            var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {};
-            var thisDataByAxis = thisItemCoordSys.dataByAxis || [];
-            contentNotChanged &= lastDataByAxis.length === thisDataByAxis.length;
+            var lastDataByAxis = lastItemCoordSys.dataByAxis || [] as DataByAxis[];
+            var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {} as DataByCoordSys;
+            var thisDataByAxis = thisItemCoordSys.dataByAxis || [] as DataByAxis[];
+            contentNotChanged = contentNotChanged && lastDataByAxis.length === thisDataByAxis.length;
 
             contentNotChanged && each(lastDataByAxis, function (lastItem, indexAxis) {
-                var thisItem = thisDataByAxis[indexAxis] || {};
-                var lastIndices = lastItem.seriesDataIndices || [];
-                var newIndices = thisItem.seriesDataIndices || [];
+                var thisItem = thisDataByAxis[indexAxis] || {} as DataByAxis;
+                var lastIndices = lastItem.seriesDataIndices || [] as DataIndex[];
+                var newIndices = thisItem.seriesDataIndices || [] as DataIndex[];
 
-                contentNotChanged
-                    &= lastItem.value === thisItem.value
+                contentNotChanged = contentNotChanged
+                    && lastItem.value === thisItem.value
                     && lastItem.axisType === thisItem.axisType
                     && lastItem.axisId === thisItem.axisId
                     && lastIndices.length === newIndices.length;
 
                 contentNotChanged && each(lastIndices, function (lastIdxItem, j) {
                     var newIdxItem = newIndices[j];
-                    contentNotChanged
-                        &= lastIdxItem.seriesIndex === newIdxItem.seriesIndex
+                    contentNotChanged = contentNotChanged
+                        && lastIdxItem.seriesIndex === newIdxItem.seriesIndex
                         && lastIdxItem.dataIndex === newIdxItem.dataIndex;
                 });
             });
@@ -712,9 +864,9 @@ export default echarts.extendComponentView({
         this._lastDataByCoordSys = dataByCoordSys;
 
         return !!contentNotChanged;
-    },
+    }
 
-    _hide: function (dispatchAction) {
+    _hide(dispatchAction: ExtensionAPI['dispatchAction']) {
         // Do not directly hideLater here, because this behavior may be prevented
         // in dispatchAction when showTip is dispatched.
 
@@ -725,29 +877,31 @@ export default echarts.extendComponentView({
             type: 'hideTip',
             from: this.uid
         });
-    },
+    }
 
-    dispose: function (ecModel, api) {
+    dispose(ecModel: GlobalModel, api: ExtensionAPI) {
         if (env.node) {
             return;
         }
         this._tooltipContent.dispose();
         globalListener.unregister('itemTooltip', api);
     }
-});
-
+}
 
+type TooltipableOption = {
+    tooltip?: TooltipOption | string
+}
 /**
- * @param {Array.<Object|module:echarts/model/Model>} modelCascade
  * From top to bottom. (the last one should be globalTooltipModel);
  */
-function buildTooltipModel(modelCascade) {
-    var resultModel = modelCascade.pop();
+function buildTooltipModel(modelCascade: (TooltipModel | Model<TooltipableOption> | TooltipOption | string)[]) {
+    // Last is always tooltip model.
+    var resultModel = modelCascade.pop() as Model<TooltipOption>;
     while (modelCascade.length) {
         var tooltipOpt = modelCascade.pop();
         if (tooltipOpt) {
-            if (Model.isInstance(tooltipOpt)) {
-                tooltipOpt = tooltipOpt.get('tooltip', true);
+            if (tooltipOpt instanceof Model) {
+                tooltipOpt = (tooltipOpt as Model<TooltipableOption>).get('tooltip', true);
             }
             // In each data item tooltip can be simply write:
             // {
@@ -755,19 +909,26 @@ function buildTooltipModel(modelCascade) {
             //  tooltip: 'Something you need to know'
             // }
             if (typeof tooltipOpt === 'string') {
-                tooltipOpt = {formatter: tooltipOpt};
+                tooltipOpt = {
+                    formatter: tooltipOpt
+                };
             }
-            resultModel = new Model(tooltipOpt, resultModel, resultModel.ecModel);
+            resultModel = new Model(tooltipOpt, resultModel, resultModel.ecModel) as Model<TooltipOption>;
         }
     }
     return resultModel;
 }
 
-function makeDispatchAction(payload, api) {
+function makeDispatchAction(payload: ShowTipPayload | HideTipPayload, api: ExtensionAPI) {
     return payload.dispatchAction || zrUtil.bind(api.dispatchAction, api);
 }
 
-function refixTooltipPosition(x, y, content, viewWidth, viewHeight, gapH, gapV) {
+function refixTooltipPosition(
+    x: number, y: number,
+    content: TooltipHTMLContent | TooltipRichContent,
+    viewWidth: number, viewHeight: number,
+    gapH: number, gapV: number
+) {
     var size = content.getOuterSize();
     var width = size.width;
     var height = size.height;
@@ -791,7 +952,12 @@ function refixTooltipPosition(x, y, content, viewWidth, viewHeight, gapH, gapV)
     return [x, y];
 }
 
-function confineTooltipPosition(x, y, content, viewWidth, viewHeight) {
+function confineTooltipPosition(
+    x: number, y: number,
+    content: TooltipHTMLContent | TooltipRichContent,
+    viewWidth: number,
+    viewHeight: number
+): [number, number] {
     var size = content.getOuterSize();
     var width = size.width;
     var height = size.height;
@@ -804,7 +970,11 @@ function confineTooltipPosition(x, y, content, viewWidth, viewHeight) {
     return [x, y];
 }
 
-function calcTooltipPosition(position, rect, contentSize) {
+function calcTooltipPosition(
+    position: TooltipOption['position'],
+    rect: ZRRectLike,
+    contentSize: number[]
+): [number, number] {
     var domWidth = contentSize[0];
     var domHeight = contentSize[1];
     var gap = 5;
@@ -836,6 +1006,8 @@ function calcTooltipPosition(position, rect, contentSize) {
     return [x, y];
 }
 
-function isCenterAlign(align) {
+function isCenterAlign(align: ZRAlign | ZRVerticalAlign) {
     return align === 'center' || align === 'middle';
 }
+
+ComponentView.registerClass(TooltipView);
diff --git a/src/coord/CoordinateSystem.ts b/src/coord/CoordinateSystem.ts
index 6f5aabf..fbb62d8 100644
--- a/src/coord/CoordinateSystem.ts
+++ b/src/coord/CoordinateSystem.ts
@@ -86,11 +86,13 @@ export interface CoordinateSystemMaster {
  * For example: cartesian is CoordinateSystemExecutive.
  * series.coordinateSystem is CoordinateSystemExecutive.
  */
-export interface CoordinateSystemExecutive {
+export interface CoordinateSystem {
 
     // Should be the same as its coordinateSystemCreator.
     dimensions: DimensionName[];
 
+    model?: Model;
+
     // @param data
     // @param reserved Defined by the coordinate system itself
     // @param out
diff --git a/src/coord/cartesian/Cartesian2D.ts b/src/coord/cartesian/Cartesian2D.ts
index 8dd8345..4a69942 100644
--- a/src/coord/cartesian/Cartesian2D.ts
+++ b/src/coord/cartesian/Cartesian2D.ts
@@ -22,11 +22,11 @@ import BoundingRect from 'zrender/src/core/BoundingRect';
 import Cartesian from './Cartesian';
 import { ScaleDataValue } from '../../util/types';
 import Axis2D from './Axis2D';
-import { CoordinateSystemExecutive } from '../CoordinateSystem';
+import { CoordinateSystem } from '../CoordinateSystem';
 import Grid, {cartesian2DDimensions} from './Grid';
 import GridModel from './GridModel';
 
-class Cartesian2D extends Cartesian<Axis2D> implements CoordinateSystemExecutive {
+class Cartesian2D extends Cartesian<Axis2D> implements CoordinateSystem {
 
     readonly type: string = 'cartesian2d';
 
diff --git a/src/data/List.ts b/src/data/List.ts
index b3f1121..aef3324 100644
--- a/src/data/List.ts
+++ b/src/data/List.ts
@@ -1599,7 +1599,7 @@ class List <HostModel extends Model = Model> {
     setVisual(key: string, val: any): void;
     setVisual(kvObj: Dictionary<any>): void;
     setVisual(key: string | Dictionary<any>, val?: any): void {
-        if (isObject<Dictionary<any>>(key)) {
+        if (isObject(key)) {
             for (var name in key) {
                 if (key.hasOwnProperty(name)) {
                     this.setVisual(name, key[name]);
@@ -1617,7 +1617,7 @@ class List <HostModel extends Model = Model> {
     setLayout(key: string, val: any): void;
     setLayout(kvObj: Dictionary<any>): void;
     setLayout(key: string | Dictionary<any>, val?: any): void {
-        if (isObject<Dictionary<any>>(key)) {
+        if (isObject(key)) {
             for (var name in key) {
                 if (key.hasOwnProperty(name)) {
                     this.setLayout(name, key[name]);
@@ -1695,7 +1695,7 @@ class List <HostModel extends Model = Model> {
         var hasItemVisual = this.hasItemVisual;
         this._itemVisuals[idx] = itemVisual;
 
-        if (isObject<Dictionary<any>>(key)) {
+        if (isObject(key)) {
             for (var name in key) {
                 if (key.hasOwnProperty(name)) {
                     itemVisual[name] = key[name];
diff --git a/src/echarts.ts b/src/echarts.ts
index 1f8bfc0..c21c4f7 100644
--- a/src/echarts.ts
+++ b/src/echarts.ts
@@ -364,6 +364,10 @@ class ECharts {
         return this._dom;
     }
 
+    getId(): string {
+        return this.id;
+    }
+
     getZr(): zrender.ZRenderType {
         return this._zr;
     }
@@ -394,7 +398,7 @@ class ECharts {
         }
 
         var silent;
-        if (isObject<SetOptionOpts>(notMerge)) {
+        if (isObject(notMerge)) {
             lazyUpdate = notMerge.lazyUpdate;
             silent = notMerge.silent;
             notMerge = notMerge.notMerge;
@@ -2273,6 +2277,22 @@ export function getMap(mapName: string) {
     };
 }
 
+/**
+ * Globa dispatchAction to a specified chart instance.
+ */
+// export function dispatchAction(payload: { chartId: string } & Payload, opt?: Parameters<ECharts['dispatchAction']>[1]) {
+//     if (!payload || !payload.chartId) {
+//         // Must have chartId to find chart
+//         return;
+//     }
+//     const chart = instances[payload.chartId];
+//     if (chart) {
+//         chart.dispatchAction(payload, opt);
+//     }
+// }
+
+
+
 registerVisual(PRIORITY_VISUAL_GLOBAL, seriesColor);
 registerPreprocessor(backwardCompat);
 registerProcessor(PRIORITY_PROCESSOR_DATASTACK, dataStack);
diff --git a/src/model/Component.ts b/src/model/Component.ts
index 955a858..73c2ed7 100644
--- a/src/model/Component.ts
+++ b/src/model/Component.ts
@@ -41,7 +41,9 @@ import {
     BoxLayoutOptionMixin
 } from '../util/types';
 
-var inner = makeInner();
+var inner = makeInner<{
+    defaultOption: ComponentOption
+}>();
 
 class ComponentModel<Opt extends ComponentOption = ComponentOption> extends Model<Opt> {
 
diff --git a/src/model/Model.ts b/src/model/Model.ts
index 4646e3d..9f2d70f 100644
--- a/src/model/Model.ts
+++ b/src/model/Model.ts
@@ -40,7 +40,7 @@ import { ModelOption } from '../util/types';
 import { Dictionary } from 'zrender/src/core/types';
 
 var mixin = zrUtil.mixin;
-var inner = makeInner();
+var inner = makeInner<{getParent(path: string | string[]): Model}>();
 
 // Since model.option can be not only `Dictionary` but also primary types,
 // we do this conditional type to avoid getting type 'never';
@@ -57,7 +57,7 @@ type Value<Opt, R> = Opt extends Dictionary<any>
  * @param {module:echarts/model/Model} [parentModel]
  * @param {module:echarts/model/Global} [ecModel]
  */
-class Model<Opt extends ModelOption = ModelOption> {
+class Model<Opt extends ModelOption = ModelOption> {    // TODO: TYPE use unkown insteadof any?
 
     // [Caution]: for compat the previous "class extend"
     // publich and protected fields must be initialized on
@@ -210,11 +210,6 @@ class Model<Opt extends ModelOption = ModelOption> {
         return path;
     }
 
-    /**
-     * @param {Function} getParentMethod
-     *        param {Array.<string>|string} path
-     *        return {module:echarts/model/Model}
-     */
     customizeGetParent(
         getParentMethod: (path: string | string[]) => Model
     ): void {
diff --git a/src/model/Series.ts b/src/model/Series.ts
index 3e3ffc7..32cc466 100644
--- a/src/model/Series.ts
+++ b/src/model/Series.ts
@@ -47,14 +47,17 @@ import {
 } from '../data/helper/sourceHelper';
 import {retrieveRawValue} from '../data/helper/dataProvider';
 import GlobalModel from './Global';
-import { CoordinateSystemExecutive } from '../coord/CoordinateSystem';
+import { CoordinateSystem } from '../coord/CoordinateSystem';
 import { ExtendableConstructor, mountExtend, Constructor } from '../util/clazz';
 import { PipelineContext, SeriesTaskContext, GeneralTask, OverallTask, SeriesTask } from '../stream/Scheduler';
 import LegendVisualProvider from '../visual/LegendVisualProvider';
 import List from '../data/List';
 import Source from '../data/Source';
 
-var inner = modelUtil.makeInner();
+var inner = modelUtil.makeInner<{
+    data: List
+    dataBeforeProcessed: List
+}>();
 
 class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentModel<Opt> {
 
@@ -74,7 +77,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
     seriesIndex: number;
 
     // coodinateSystem will be injected in the echarts/CoordinateSystem
-    coordinateSystem: CoordinateSystemExecutive;
+    coordinateSystem: CoordinateSystem;
 
     // Injected outside
     dataTask: SeriesTask;
@@ -249,7 +252,7 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
             // restore or setOption with not merge mode), series data may
             // be still need to judge animation or something when graphic
             // elements want to know whether fade out.
-            return inner(this).data;
+            return inner(this).data as List<SeriesModel<Opt>>;
         }
     }
 
diff --git a/src/model/mixin/colorPalette.ts b/src/model/mixin/colorPalette.ts
index 526a9c1..510e731 100644
--- a/src/model/mixin/colorPalette.ts
+++ b/src/model/mixin/colorPalette.ts
@@ -20,8 +20,12 @@
 import {makeInner, normalizeToArray} from '../../util/model';
 import Model from '../Model';
 import { ZRColor, ColorPaletteOptionMixin } from '../../util/types';
+import { Dictionary } from 'zrender/src/core/types';
 
-var inner = makeInner();
+var inner = makeInner<{
+    colorIdx: number
+    colorNameMap: Dictionary<ZRColor>
+}>();
 
 function getNearestColorPalette(
     colors: ZRColor[][], requestColorNum: number
diff --git a/src/util/format.ts b/src/util/format.ts
index fc1548d..217aef6 100644
--- a/src/util/format.ts
+++ b/src/util/format.ts
@@ -37,7 +37,7 @@ export function addCommas(x: string | number): string {
             + (parts.length > 1 ? ('.' + parts[1]) : '');
 }
 
-export function toCamelCase(str: string, upperCaseFirst: boolean): string {
+export function toCamelCase(str: string, upperCaseFirst?: boolean): string {
     str = (str || '').toLowerCase().replace(/-(.)/g, function (match, group1) {
         return group1.toUpperCase();
     });
diff --git a/src/util/graphic.ts b/src/util/graphic.ts
index 58ccb1b..5ea19bd 100644
--- a/src/util/graphic.ts
+++ b/src/util/graphic.ts
@@ -39,7 +39,7 @@ import Arc from 'zrender/src/graphic/shape/Arc';
 import CompoundPath from 'zrender/src/graphic/CompoundPath';
 import LinearGradient from 'zrender/src/graphic/LinearGradient';
 import RadialGradient from 'zrender/src/graphic/RadialGradient';
-import BoundingRect, { RectLike } from 'zrender/src/core/BoundingRect';
+import BoundingRect from 'zrender/src/core/BoundingRect';
 import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable';
 import * as subPixelOptimizeUtil from 'zrender/src/graphic/helper/subPixelOptimize';
 import { Dictionary, ImageLike } from 'zrender/src/core/types';
@@ -50,7 +50,14 @@ import { PatternObject } from 'zrender/src/graphic/Pattern';
 import { GradientObject } from 'zrender/src/graphic/Gradient';
 import Element, { ElementEvent } from 'zrender/src/Element';
 import Model from '../model/Model';
-import { AnimationOptionMixin, LabelOption, AnimationDelayCallbackParam, DisplayState, ECElement } from './types';
+import {
+    AnimationOptionMixin,
+    LabelOption,
+    AnimationDelayCallbackParam,
+    DisplayState,
+    ECElement,
+    ZRRectLike
+} from './types';
 import GlobalModel from '../model/Global';
 
 
@@ -207,7 +214,7 @@ export function getShapeClass(name: string): {new(): Path} {
 export function makePath(
     pathData: string,
     opts: SVGPathOption,
-    rect: RectLike,
+    rect: ZRRectLike,
     layout?: 'center' | 'cover'
 ): SVGPath {
     var path = pathTool.createFromString(pathData, opts);
@@ -229,7 +236,7 @@ export function makePath(
  */
 export function makeImage(
     imageUrl: string,
-    rect: RectLike,
+    rect: ZRRectLike,
     layout?: 'center' | 'cover'
 ) {
     var path = new ZImage({
@@ -260,10 +267,10 @@ export function makeImage(
  * @param  boundingRect constraint bounding box
  * @return element position containing x, y, width, and height
  */
-function centerGraphic(rect: RectLike, boundingRect: {
+function centerGraphic(rect: ZRRectLike, boundingRect: {
     width: number
     height: number
-}): RectLike {
+}): ZRRectLike {
     // Set rect to center, keep width / height ratio.
     var aspect = boundingRect.width / boundingRect.height;
     var width = rect.height * aspect;
@@ -293,7 +300,7 @@ export var mergePath = pathTool.mergePath;
  * @param path
  * @param rect
  */
-export function resizePath(path: SVGPath, rect: RectLike): void {
+export function resizePath(path: SVGPath, rect: ZRRectLike): void {
     if (!path.applyTransform) {
         return;
     }
@@ -1379,7 +1386,7 @@ export function groupTransition(
     });
 }
 
-export function clipPointsByRect(points: vector.VectorArray[], rect: RectLike): vector.VectorArray[] {
+export function clipPointsByRect(points: vector.VectorArray[], rect: ZRRectLike): vector.VectorArray[] {
     // FIXME: this way migth be incorrect when grpahic clipped by a corner.
     // and when element have border.
     return zrUtil.map(points, function (point) {
@@ -1396,7 +1403,7 @@ export function clipPointsByRect(points: vector.VectorArray[], rect: RectLike):
 /**
  * Return a new clipped rect. If rect size are negative, return undefined.
  */
-export function clipRectByRect(targetRect: RectLike, rect: RectLike): RectLike {
+export function clipRectByRect(targetRect: ZRRectLike, rect: ZRRectLike): ZRRectLike {
     var x = mathMax(targetRect.x, rect.x);
     var x2 = mathMin(targetRect.x + targetRect.width, rect.x + rect.width);
     var y = mathMax(targetRect.y, rect.y);
@@ -1417,7 +1424,7 @@ export function clipRectByRect(targetRect: RectLike, rect: RectLike): RectLike {
 export function createIcon(
     iconStr: string,    // Support 'image://' or 'path://' or direct svg path.
     opt?: Omit<DisplayableProps, 'style'>,
-    rect?: RectLike
+    rect?: ZRRectLike
 ): SVGPath | ZImage {
     const innerOpts: DisplayableProps = zrUtil.extend({rectHover: true}, opt);
     const style: StyleProps = innerOpts.style = {strokeNoScale: true};
diff --git a/src/util/layout.ts b/src/util/layout.ts
index 714b2c0..c83c238 100644
--- a/src/util/layout.ts
+++ b/src/util/layout.ts
@@ -20,10 +20,10 @@
 // Layout helpers for each component positioning
 
 import * as zrUtil from 'zrender/src/core/util';
-import BoundingRect, { RectLike } from 'zrender/src/core/BoundingRect';
+import BoundingRect from 'zrender/src/core/BoundingRect';
 import {parsePercent} from './number';
 import * as formatUtil from './format';
-import { BoxLayoutOptionMixin, ComponentLayoutMode } from './types';
+import { BoxLayoutOptionMixin, ComponentLayoutMode, ZRRectLike } from './types';
 import { Group } from 'zrender/src/export';
 import Element from 'zrender/src/Element';
 import { Dictionary } from 'zrender/src/core/types';
@@ -165,7 +165,7 @@ export function getAvailableSize(
         x2: number | string
         y2: number | string
     },
-    containerRect: RectLike,
+    containerRect: ZRRectLike,
     margin: number[] | number
 ) {
     var containerWidth = containerRect.width;
diff --git a/src/util/model.ts b/src/util/model.ts
index e4e408b..10fcc11 100644
--- a/src/util/model.ts
+++ b/src/util/model.ts
@@ -462,10 +462,10 @@ export function queryDataIndex(data: List, payload: Payload): number | number[]
  *
  * @return {Function}
  */
-export function makeInner() {
+export function makeInner<T>() {
     // Consider different scope by es module import.
     var key = '__\0ec_inner_' + innerUniqueIndex++ + '_' + Math.random().toFixed(5);
-    return function (hostObj: any) {
+    return function (hostObj: any): T {
         return hostObj[key] || (hostObj[key] = {});
     };
 }
diff --git a/src/util/types.ts b/src/util/types.ts
index 120615f..02a2464 100644
--- a/src/util/types.ts
+++ b/src/util/types.ts
@@ -41,6 +41,8 @@ import { TooltipMarker } from './format';
 import { easingType } from 'zrender/src/animation/easing';
 import { LinearGradientObject } from 'zrender/src/graphic/LinearGradient';
 import { RadialGradientObject } from 'zrender/src/graphic/RadialGradient';
+import { RectLike } from 'zrender/src/core/BoundingRect';
+import ZRText from 'zrender/src/graphic/Text';
 
 
 
@@ -65,6 +67,9 @@ export type ZREasing = easingType
 export type ZRTextAlign = TextAlign
 export type ZRTextVerticalAlign = TextVerticalAlign
 
+export type ZRElementEvent = ElementEvent
+
+export type ZRRectLike = RectLike
 
 // ComponentFullType can be:
 //     'xxx.yyy': means ComponentMainType.ComponentSubType.
@@ -91,6 +96,11 @@ export interface ECElement extends Element {
     eventData?: ECEventData;
     seriesIndex?: number;
     dataType?: string;
+    tooltip?: CommonTooltipOption & {
+        content?: string
+        // TODO: TYPE
+        formatterParams?: any
+    }
 }
 
 export interface DataHost {
@@ -724,6 +734,136 @@ export interface LabelLineOption {
     lineStyle?: LineStyleOption
 }
 
+interface TooltipFormatterCallback {
+    /**
+     * For sync callback
+     * params will be an array on axis trigger.
+     */
+    (params: CallbackDataParams | CallbackDataParams[], asyncTicket: string): string
+    /**
+     * For async callback.
+     * Returned html string will be a placeholder when callback is not invoked.
+     */
+    (params: CallbackDataParams | CallbackDataParams[], asyncTicket: string, callback: (cbTicket: string, html: string) => void): string
+}
+
+type TooltipBuiltinPosition = 'inside' | 'top' | 'left' | 'right' | 'bottom'
+type TooltipBoxLayoutOption = Pick<
+    BoxLayoutOptionMixin, 'top' | 'left' | 'right' | 'bottom'
+>
+/**
+ * Position relative to the hoverred element. Only available when trigger is item.
+ */
+interface PositionCallback {
+    (
+        point: [number, number],
+        /**
+         * params will be an array on axis trigger.
+         */
+        params: CallbackDataParams | CallbackDataParams[],
+        /**
+         * Will be HTMLDivElement when renderMode is html
+         * Otherwise it's graphic.Text
+         */
+        el: HTMLDivElement | ZRText | null,
+        /**
+         * Rect of hover elements. Will be null if not hovered
+         */
+        rect: RectLike | null,
+        size: {
+            /**
+             * Size of popup content
+             */
+            contentSize: [number, number]
+            /**
+             * Size of the chart view
+             */
+            viewSize: [number, number]
+        }
+    ): number[] | string[] | TooltipBuiltinPosition | TooltipBoxLayoutOption
+}
+/**
+ * Common tooltip option
+ */
+export interface CommonTooltipOption {
+
+    show?: boolean
+
+    /**
+     * When to trigger
+     */
+    triggerOn?: 'mousemove' | 'click' | 'none' | 'mousemove|click'
+    /**
+     * Whether to not hide popup content automatically
+     */
+    alwaysShowContent?: boolean
+
+    formatter?: string | TooltipFormatterCallback
+    /**
+     * Absolution pixel [x, y] array. Or relative percent string [x, y] array.
+     * If trigger is 'item'. position can be set to 'inside' / 'top' / 'left' / 'right' / 'bottom',
+     * which is relative to the hovered element.
+     *
+     * Support to be a callback
+     */
+    position?: number[] | string[] | TooltipBuiltinPosition | PositionCallback | TooltipBoxLayoutOption
+
+    confine?: boolean
+
+    /**
+     * Consider triggered from axisPointer handle, verticalAlign should be 'middle'
+     */
+    align?: ZRAlign
+
+    verticalAlign?: ZRVerticalAlign
+    /**
+     * Delay of show. milesecond.
+     */
+    showDelay?: number
+
+    /**
+     * Delay of hide. milesecond.
+     */
+    hideDelay?: number
+
+    transitionDuration?: number
+    /**
+     * Whether mouse is allowed to enter the floating layer of tooltip
+     * If you need to interact in the tooltip like with links or buttons, it can be set as true.
+     */
+    enterable?: boolean
+
+    backgroundColor?: ColorString
+    borderColor?: ColorString
+    borderRadius?: number
+    borderWidth?: number
+
+    /**
+     * Padding between tooltip content and tooltip border.
+     */
+    padding?: number | number[]
+
+    /**
+     * Available when renderMode is 'html'
+     */
+    extraCssText?: string
+
+    textStyle?: Pick<LabelOption,
+        'color' | 'fontStyle' | 'fontWeight' | 'fontFamily' | 'fontSize' |
+        'lineHeight' | 'width' | 'height' | 'textBorderColor' | 'textBorderWidth' |
+        'textShadowColor' | 'textShadowBlur' | 'textShadowOffsetX' | 'textShadowOffsetY'
+        | 'align'> & {
+
+        // Available when renderMode is html
+        decoration?: string
+    }
+}
+
+/**
+ * Tooltip option configured on each series
+ */
+export type SeriesTooltipOption = CommonTooltipOption
+
 export interface ComponentOption {
     type?: string;
     id?: string;
@@ -755,4 +895,4 @@ export interface SeriesOption extends
     coordinateSystem?: string
 
     // FIXME:TS more
-}
\ No newline at end of file
+}
diff --git a/src/view/Chart.ts b/src/view/Chart.ts
index a104ca3..d206483 100644
--- a/src/view/Chart.ts
+++ b/src/view/Chart.ts
@@ -36,7 +36,9 @@ import {
 import { SeriesTaskContext, SeriesTask } from '../stream/Scheduler';
 import List from '../data/List';
 
-var inner = modelUtil.makeInner();
+var inner = modelUtil.makeInner<{
+    updateMethod: keyof Chart
+}>();
 var renderPlanner = createRenderPlanner();
 
 
@@ -176,7 +178,7 @@ class Chart {
         this.render(seriesModel, ecModel, api, payload);
     }
 
-    static markUpdateMethod(payload: Payload, methodName: string): void {
+    static markUpdateMethod(payload: Payload, methodName: keyof Chart): void {
         inner(payload).updateMethod = methodName;
     }
 
@@ -241,7 +243,7 @@ function renderTaskReset(context: SeriesTaskContext): TaskResetCallbackReturn<Se
     var progressiveRender = seriesModel.pipelineContext.progressiveRender;
     var view = context.view;
 
-    var updateMethod: keyof Chart = payload && inner(payload).updateMethod;
+    var updateMethod = payload && inner(payload).updateMethod;
     var methodName: keyof Chart = progressiveRender
         ? 'incrementalPrepareRender'
         : (updateMethod && view[updateMethod])


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