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/21 15:06:06 UTC

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

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 92b03cd  ts: add types for visualMap model
92b03cd is described below

commit 92b03cdab8a682904cabf615f78d7bd2ac376945
Author: pissang <bm...@gmail.com>
AuthorDate: Fri Feb 21 23:05:38 2020 +0800

    ts: add types for visualMap model
---
 src/chart/pie/PieSeries.ts                 |   6 +-
 src/component/visualMap/ContinuousModel.ts | 156 ++++++----
 src/component/visualMap/PiecewiseModel.ts  | 319 +++++++++++---------
 src/component/visualMap/VisualMapModel.ts  | 449 ++++++++++++++++-------------
 src/component/visualMap/helper.ts          |  35 ++-
 src/component/visualMap/typeDefaulter.ts   |  18 +-
 src/component/visualMap/visualMapAction.ts |   6 +-
 src/data/DataDimensionInfo.ts              |   4 +
 src/echarts.ts                             |   4 +-
 src/util/number.ts                         |   6 +-
 src/util/types.ts                          |  16 +-
 src/visual/VisualMapping.ts                |   8 +-
 12 files changed, 598 insertions(+), 429 deletions(-)

diff --git a/src/chart/pie/PieSeries.ts b/src/chart/pie/PieSeries.ts
index ab7992a..9679944 100644
--- a/src/chart/pie/PieSeries.ts
+++ b/src/chart/pie/PieSeries.ts
@@ -62,6 +62,8 @@ export interface PieSeriesOption extends
     CircleLayoutOptionMixin,
     BoxLayoutOptionMixin {
 
+    type: 'pie'
+
     hoverAnimation?: boolean
 
     roseType?: 'radius' | 'area'
@@ -98,7 +100,7 @@ export interface PieSeriesOption extends
 
 class PieSeries extends SeriesModel<PieSeriesOption> {
 
-    static type = 'series.pie';
+    static type = 'series.pie' as const;
 
     /**
      * @overwrite
@@ -186,7 +188,7 @@ class PieSeries extends SeriesModel<PieSeriesOption> {
             && option.emphasis.label.show;
     }
 
-    static defaultOption: PieSeriesOption = {
+    static defaultOption: Omit<PieSeriesOption, 'type'> = {
         zlevel: 0,
         z: 2,
         legendHoverLink: true,
diff --git a/src/component/visualMap/ContinuousModel.ts b/src/component/visualMap/ContinuousModel.ts
index 6e91c6a..d9cecc0 100644
--- a/src/component/visualMap/ContinuousModel.ts
+++ b/src/component/visualMap/ContinuousModel.ts
@@ -17,80 +17,98 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as zrUtil from 'zrender/src/core/util';
-import VisualMapModel from './VisualMapModel';
+import VisualMapModel, { VisualMapOption, VisualMeta } from './VisualMapModel';
 import * as numberUtil from '../../util/number';
+import ComponentModel from '../../model/Component';
+import { VisualMappingOption } from '../../visual/VisualMapping';
 
 // Constant
 var DEFAULT_BAR_BOUND = [20, 140];
 
-var ContinuousModel = VisualMapModel.extend({
+type RangeWithAuto = {
+    auto?: 0 | 1
+}
+
+type VisualState = VisualMapModel['stateList'][number]
 
-    type: 'visualMap.continuous',
+export interface ContinousVisualMapOption extends VisualMapOption {
+
+    align?: 'auto' | 'left' | 'right' | 'top' | 'bottom'
 
     /**
-     * @protected
+     * This prop effect default component type determine
+     * @see echarts/component/visualMap/typeDefaulter.
      */
-    defaultOption: {
-        align: 'auto',           // 'auto', 'left', 'right', 'top', 'bottom'
-        calculable: false,       // This prop effect default component type determine,
-                                 // See echarts/component/visualMap/typeDefaulter.
-        range: null,             // selected range. In default case `range` is [min, max]
-                                 // and can auto change along with modification of min max,
-                                 // util use specifid a range.
-        realtime: true,          // Whether realtime update.
-        itemHeight: null,        // The length of the range control edge.
-        itemWidth: null,         // The length of the other side.
-        hoverLink: true,         // Enable hover highlight.
-        hoverLinkDataSize: null, // The size of hovered data.
-        hoverLinkOnHandle: null  // Whether trigger hoverLink when hover handle.
-                                 // If not specified, follow the value of `realtime`.
-    },
+    calculable?: boolean
+
+    /**
+     * selected range. In default case `range` is [min, max]
+     * and can auto change along with modification of min max,
+     * util user specifid a range.
+     */
+    range?: number[]
+    /**
+     * Whether to enable hover highlight.
+     */
+    hoverLink?: boolean
+
+    /**
+     * The extent of hovered data.
+     */
+    hoverLinkDataSize?: number
+    /**
+     * Whether trigger hoverLink when hover handle.
+     * If not specified, follow the value of `realtime`.
+     */
+    hoverLinkOnHandle?: boolean
+}
+
+class ContinuousModel extends VisualMapModel<ContinousVisualMapOption> {
+
+    static type = 'visualMap.continuous' as const
+    type = ContinuousModel.type
 
     /**
      * @override
      */
-    optionUpdated: function (newOption, isInit) {
-        ContinuousModel.superApply(this, 'optionUpdated', arguments);
+    optionUpdated(newOption: ContinousVisualMapOption, isInit: boolean) {
+        super.optionUpdated.apply(this, arguments as any);
 
         this.resetExtent();
 
-        this.resetVisual(function (mappingOption) {
+        this.resetVisual(function (mappingOption?: VisualMappingOption) {
             mappingOption.mappingMethod = 'linear';
             mappingOption.dataExtent = this.getExtent();
         });
 
         this._resetRange();
-    },
+    }
 
     /**
      * @protected
      * @override
      */
-    resetItemSize: function () {
-        ContinuousModel.superApply(this, 'resetItemSize', arguments);
+    resetItemSize() {
+        super.resetItemSize.apply(this, arguments as any);
 
         var itemSize = this.itemSize;
 
-        this._orient === 'horizontal' && itemSize.reverse();
-
         (itemSize[0] == null || isNaN(itemSize[0])) && (itemSize[0] = DEFAULT_BAR_BOUND[0]);
         (itemSize[1] == null || isNaN(itemSize[1])) && (itemSize[1] = DEFAULT_BAR_BOUND[1]);
-    },
+    }
 
     /**
      * @private
      */
-    _resetRange: function () {
+    _resetRange() {
         var dataExtent = this.getExtent();
         var range = this.option.range;
 
-        if (!range || range.auto) {
+        if (!range || (range as RangeWithAuto).auto) {
             // `range` should always be array (so we dont use other
             // value like 'auto') for user-friend. (consider getOption).
-            dataExtent.auto = 1;
+            (dataExtent as RangeWithAuto).auto = 1;
             this.option.range = dataExtent;
         }
         else if (zrUtil.isArray(range)) {
@@ -100,40 +118,40 @@ var ContinuousModel = VisualMapModel.extend({
             range[0] = Math.max(range[0], dataExtent[0]);
             range[1] = Math.min(range[1], dataExtent[1]);
         }
-    },
+    }
 
     /**
      * @protected
      * @override
      */
-    completeVisualOption: function () {
-        VisualMapModel.prototype.completeVisualOption.apply(this, arguments);
+    completeVisualOption() {
+        super.completeVisualOption.apply(this, arguments as any);
 
-        zrUtil.each(this.stateList, function (state) {
+        zrUtil.each(this.stateList, function (state: VisualState) {
             var symbolSize = this.option.controller[state].symbolSize;
             if (symbolSize && symbolSize[0] !== symbolSize[1]) {
                 symbolSize[0] = 0; // For good looking.
             }
         }, this);
-    },
+    }
 
     /**
      * @override
      */
-    setSelected: function (selected) {
+    setSelected(selected: number[]) {
         this.option.range = selected.slice();
         this._resetRange();
-    },
+    }
 
     /**
      * @public
      */
-    getSelected: function () {
+    getSelected(): [number, number] {
         var dataExtent = this.getExtent();
 
         var dataInterval = numberUtil.asc(
             (this.get('range') || []).slice()
-        );
+        ) as [number, number];
 
         // Clamp
         dataInterval[0] > dataExtent[1] && (dataInterval[0] = dataExtent[1]);
@@ -142,12 +160,12 @@ var ContinuousModel = VisualMapModel.extend({
         dataInterval[1] < dataExtent[0] && (dataInterval[1] = dataExtent[0]);
 
         return dataInterval;
-    },
+    }
 
     /**
      * @override
      */
-    getValueState: function (value) {
+    getValueState(value: number): VisualState {
         var range = this.option.range;
         var dataExtent = this.getExtent();
 
@@ -157,38 +175,44 @@ var ContinuousModel = VisualMapModel.extend({
             (range[0] <= dataExtent[0] || range[0] <= value)
             && (range[1] >= dataExtent[1] || value <= range[1])
         ) ? 'inRange' : 'outOfRange';
-    },
+    }
 
-    /**
-     * @params {Array.<number>} range target value: range[0] <= value && value <= range[1]
-     * @return {Array.<Object>} [{seriesId, dataIndices: <Array.<number>>}, ...]
-     */
-    findTargetDataIndices: function (range) {
-        var result = [];
+    findTargetDataIndices(range: number[]) {
+        type DataIndices = {
+            seriesId: string
+            dataIndex: number[]
+        }
+        var result: DataIndices[] = [];
 
         this.eachTargetSeries(function (seriesModel) {
-            var dataIndices = [];
+            var dataIndices: number[] = [];
             var data = seriesModel.getData();
 
             data.each(this.getDataDimension(data), function (value, dataIndex) {
                 range[0] <= value && value <= range[1] && dataIndices.push(dataIndex);
             }, this);
 
-            result.push({seriesId: seriesModel.id, dataIndex: dataIndices});
+            result.push({
+                seriesId: seriesModel.id,
+                dataIndex: dataIndices
+            });
         }, this);
 
         return result;
-    },
+    }
 
     /**
      * @implement
      */
-    getVisualMeta: function (getColorVisual) {
+    getVisualMeta(
+        getColorVisual: (value: number, valueState: VisualState) => string
+    ) {
+        type ColorStop = VisualMeta['stops'][number];
         var oVals = getColorStopValues(this, 'outOfRange', this.getExtent());
         var iVals = getColorStopValues(this, 'inRange', this.option.range.slice());
-        var stops = [];
+        var stops: ColorStop[] = [];
 
-        function setStop(value, valueState) {
+        function setStop(value: number, valueState: VisualState) {
             stops.push({
                 value: value,
                 color: getColorVisual(value, valueState)
@@ -231,13 +255,23 @@ var ContinuousModel = VisualMapModel.extend({
             outerColors: [
                 stopsLen ? stops[0].color : 'transparent',
                 stopsLen ? stops[stopsLen - 1].color : 'transparent'
-            ]
+            ] as VisualMeta['outerColors']
         };
     }
 
-});
+    static defaultOption: ContinousVisualMapOption = {
+        align: 'auto',           // 'auto', 'left', 'right', 'top', 'bottom'
+        calculable: false,
+        hoverLink: true
+    }
+}
 
-function getColorStopValues(visualMapModel, valueState, dataExtent) {
+
+function getColorStopValues(
+    visualMapModel: ContinuousModel,
+    valueState: VisualState,
+    dataExtent: number[]
+) {
     if (dataExtent[0] === dataExtent[1]) {
         return dataExtent.slice();
     }
@@ -260,4 +294,6 @@ function getColorStopValues(visualMapModel, valueState, dataExtent) {
     return stopValues;
 }
 
+ComponentModel.registerClass(ContinuousModel);
+
 export default ContinuousModel;
diff --git a/src/component/visualMap/PiecewiseModel.ts b/src/component/visualMap/PiecewiseModel.ts
index 744ea70..bcd547e 100644
--- a/src/component/visualMap/PiecewiseModel.ts
+++ b/src/component/visualMap/PiecewiseModel.ts
@@ -17,99 +17,128 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import {__DEV__} from '../../config';
 import * as zrUtil from 'zrender/src/core/util';
-import VisualMapModel from './VisualMapModel';
-import VisualMapping from '../../visual/VisualMapping';
+import VisualMapModel, { VisualMapOption, VisualMeta } from './VisualMapModel';
+import VisualMapping, { VisualMappingOption } from '../../visual/VisualMapping';
 import visualDefault from '../../visual/visualDefault';
 import {reformIntervals} from '../../util/number';
+import { VisualOptionPiecewise, BuiltinVisualProperty } from '../../util/types';
+import { Dictionary } from 'zrender/src/core/types';
+
+
+interface VisualPiece extends VisualOptionPiecewise {
+    min?: number
+    max?: number
+    lt?: number
+    gt?: number
+    lte?: number
+    gte?: number
+    value?: number
+
+    label?: string
+}
 
-var PiecewiseModel = VisualMapModel.extend({
+type VisualState = VisualMapModel['stateList'][number]
+
+type InnerVisualPiece = VisualMappingOption['pieceList'][number];
+
+type GetPieceValueType<T extends InnerVisualPiece>
+    = T extends { interval: InnerVisualPiece['interval'] } ? number : string
+
+/**
+ * Order Rule:
+ *
+ * option.categories / option.pieces / option.text / option.selected:
+ *     If !option.inverse,
+ *     Order when vertical: ['top', ..., 'bottom'].
+ *     Order when horizontal: ['left', ..., 'right'].
+ *     If option.inverse, the meaning of
+ *     the order should be reversed.
+ *
+ * this._pieceList:
+ *     The order is always [low, ..., high].
+ *
+ * Mapping from location to low-high:
+ *     If !option.inverse
+ *     When vertical, top is high.
+ *     When horizontal, right is high.
+ *     If option.inverse, reverse.
+ */
 
-    type: 'visualMap.piecewise',
+export interface PiecewiseVisualMapOption extends VisualMapOption {
+    align?: 'auto' | 'left' | 'right'
+
+    minOpen?: boolean
+    maxOpen?: boolean
 
     /**
-     * Order Rule:
-     *
-     * option.categories / option.pieces / option.text / option.selected:
-     *     If !option.inverse,
-     *     Order when vertical: ['top', ..., 'bottom'].
-     *     Order when horizontal: ['left', ..., 'right'].
-     *     If option.inverse, the meaning of
-     *     the order should be reversed.
-     *
-     * this._pieceList:
-     *     The order is always [low, ..., high].
-     *
-     * Mapping from location to low-high:
-     *     If !option.inverse
-     *     When vertical, top is high.
-     *     When horizontal, right is high.
-     *     If option.inverse, reverse.
+     * When put the controller vertically, it is the length of
+     * horizontal side of each item. Otherwise, vertical side.
+     * When put the controller vertically, it is the length of
+     * vertical side of each item. Otherwise, horizontal side.
      */
+    itemWidth?: number
+    itemHeight?: number
+
+    itemSymbol?: string
+    pieces?: VisualPiece[]
 
     /**
-     * @protected
+     * category names, like: ['some1', 'some2', 'some3'].
+     * Attr min/max are ignored when categories set. See "Order Rule"
      */
-    defaultOption: {
-        selected: null,             // Object. If not specified, means selected.
-                                    // When pieces and splitNumber: {'0': true, '5': true}
-                                    // When categories: {'cate1': false, 'cate3': true}
-                                    // When selected === false, means all unselected.
+    categories?: string[]
 
-        minOpen: false,             // Whether include values that smaller than `min`.
-        maxOpen: false,             // Whether include values that bigger than `max`.
+    /**
+     * If set to 5, auto split five pieces equally.
+     * If set to 0 and component type not set, component type will be
+     * determined as "continuous". (It is less reasonable but for ec2
+     * compatibility, see echarts/component/visualMap/typeDefaulter)
+     */
+    splitNumber?: number
 
-        align: 'auto',              // 'auto', 'left', 'right'
-        itemWidth: 20,              // When put the controller vertically, it is the length of
-                                    // horizontal side of each item. Otherwise, vertical side.
-        itemHeight: 14,             // When put the controller vertically, it is the length of
-                                    // vertical side of each item. Otherwise, horizontal side.
-        itemSymbol: 'roundRect',
-        pieceList: null,            // Each item is Object, with some of those attrs:
-                                    // {min, max, lt, gt, lte, gte, value,
-                                    // color, colorSaturation, colorAlpha, opacity,
-                                    // symbol, symbolSize}, which customize the range or visual
-                                    // coding of the certain piece. Besides, see "Order Rule".
-        categories: null,           // category names, like: ['some1', 'some2', 'some3'].
-                                    // Attr min/max are ignored when categories set. See "Order Rule"
-        splitNumber: 5,             // If set to 5, auto split five pieces equally.
-                                    // If set to 0 and component type not set, component type will be
-                                    // determined as "continuous". (It is less reasonable but for ec2
-                                    // compatibility, see echarts/component/visualMap/typeDefaulter)
-        selectedMode: 'multiple',   // Can be 'multiple' or 'single'.
-        itemGap: 10,                // The gap between two items, in px.
-        hoverLink: true,            // Enable hover highlight.
+    /**
+     * Object. If not specified, means selected. When pieces and splitNumber: {'0': true, '5': true}
+     * When categories: {'cate1': false, 'cate3': true} When selected === false, means all unselected.
+     */
+    selected?: Dictionary<boolean>
+    selectedMode?: 'multiple' | 'single'
 
-        showLabel: null             // By default, when text is used, label will hide (the logic
-                                    // is remained for compatibility reason)
-    },
+    /**
+     * By default, when text is used, label will hide (the logic
+     * is remained for compatibility reason)
+     */
+    showLabel?: boolean
+
+    itemGap?: number
+
+    hoverLink?: boolean
+}
+
+class PiecewiseModel extends VisualMapModel<PiecewiseVisualMapOption> {
+
+    static type = 'visualMap.piecewise' as const
+    type = PiecewiseModel.type
 
     /**
-     * @override
+     * The order is always [low, ..., high].
+     * [{text: string, interval: Array.<number>}, ...]
      */
-    optionUpdated: function (newOption, isInit) {
-        PiecewiseModel.superApply(this, 'optionUpdated', arguments);
+    private _pieceList: InnerVisualPiece[] = [];
 
-        /**
-         * The order is always [low, ..., high].
-         * [{text: string, interval: Array.<number>}, ...]
-         * @private
-         * @type {Array.<Object>}
-         */
-        this._pieceList = [];
+    private _mode: 'pieces' | 'categories' | 'splitNumber'
+    /**
+     * @override
+     */
+    optionUpdated(newOption: PiecewiseVisualMapOption, isInit?: boolean) {
+        super.optionUpdated.apply(this, arguments as any);
 
         this.resetExtent();
 
-        /**
-         * 'pieces', 'categories', 'splitNumber'
-         * @type {string}
-         */
         var mode = this._mode = this._determineMode();
 
-        resetMethods[this._mode].call(this);
+        resetMethods[this._mode].call(this, this._pieceList);
 
         this._resetSelected(newOption, isInit);
 
@@ -134,13 +163,13 @@ var PiecewiseModel = VisualMapModel.extend({
                 });
             }
         });
-    },
+    }
 
     /**
      * @protected
      * @override
      */
-    completeVisualOption: function () {
+    completeVisualOption() {
         // Consider this case:
         // visualMap: {
         //      pieces: [{symbol: 'circle', lt: 0}, {symbol: 'rect', gte: 0}]
@@ -150,7 +179,7 @@ var PiecewiseModel = VisualMapModel.extend({
         // appear in `pieces` will not be taken into account in visual encoding.
 
         var option = this.option;
-        var visualTypesInPieces = {};
+        var visualTypesInPieces: {[key in BuiltinVisualProperty]?: 0 | 1} = {};
         var visualTypes = VisualMapping.listVisualTypes();
         var isCategory = this.isCategory();
 
@@ -162,32 +191,28 @@ var PiecewiseModel = VisualMapModel.extend({
             });
         });
 
-        zrUtil.each(visualTypesInPieces, function (v, visualType) {
-            var exists = 0;
-            zrUtil.each(this.stateList, function (state) {
-                exists |= has(option, state, visualType)
+        zrUtil.each(visualTypesInPieces, function (v, visualType: BuiltinVisualProperty) {
+            var exists = false;
+            zrUtil.each(this.stateList, function (state: VisualState) {
+                exists = exists || has(option, state, visualType)
                     || has(option.target, state, visualType);
             }, this);
 
-            !exists && zrUtil.each(this.stateList, function (state) {
+            !exists && zrUtil.each(this.stateList, function (state: VisualState) {
                 (option[state] || (option[state] = {}))[visualType] = visualDefault.get(
                     visualType, state === 'inRange' ? 'active' : 'inactive', isCategory
                 );
             });
         }, this);
 
-        function has(obj, state, visualType) {
-            return obj && obj[state] && (
-                zrUtil.isObject(obj[state])
-                    ? obj[state].hasOwnProperty(visualType)
-                    : obj[state] === visualType // e.g., inRange: 'symbol'
-            );
+        function has(obj: PiecewiseVisualMapOption['target'], state: VisualState, visualType: BuiltinVisualProperty) {
+            return obj && obj[state] && obj[state].hasOwnProperty(visualType);
         }
 
-        VisualMapModel.prototype.completeVisualOption.apply(this, arguments);
-    },
+        super.completeVisualOption.apply(this, arguments as any);
+    }
 
-    _resetSelected: function (newOption, isInit) {
+    _resetSelected(newOption: PiecewiseVisualMapOption, isInit?: boolean) {
         var thisOption = this.option;
         var pieceList = this._pieceList;
 
@@ -217,28 +242,28 @@ var PiecewiseModel = VisualMapModel.extend({
             }, this);
         }
         // thisOption.selectedMode === 'multiple', default: all selected.
-    },
+    }
 
     /**
      * @public
      */
-    getSelectedMapKey: function (piece) {
+    getSelectedMapKey(piece: InnerVisualPiece) {
         return this._mode === 'categories'
             ? piece.value + '' : piece.index + '';
-    },
+    }
 
     /**
      * @public
      */
-    getPieceList: function () {
+    getPieceList(): InnerVisualPiece[] {
         return this._pieceList;
-    },
+    }
 
     /**
      * @private
      * @return {string}
      */
-    _determineMode: function () {
+    _determineMode() {
         var option = this.option;
 
         return option.pieces && option.pieces.length > 0
@@ -246,21 +271,21 @@ var PiecewiseModel = VisualMapModel.extend({
             : this.option.categories
             ? 'categories'
             : 'splitNumber';
-    },
+    }
 
     /**
      * @public
      * @override
      */
-    setSelected: function (selected) {
+    setSelected(selected: this['option']['selected']) {
         this.option.selected = zrUtil.clone(selected);
-    },
+    }
 
     /**
      * @public
      * @override
      */
-    getValueState: function (value) {
+    getValueState(value: number): VisualState {
         var index = VisualMapping.findPieceIndex(value, this._pieceList);
 
         return index != null
@@ -268,23 +293,29 @@ var PiecewiseModel = VisualMapModel.extend({
                 ? 'inRange' : 'outOfRange'
             )
             : 'outOfRange';
-    },
+    }
 
     /**
      * @public
      * @params {number} pieceIndex piece index in visualMapModel.getPieceList()
      * @return {Array.<Object>} [{seriesId, dataIndex: <Array.<number>>}, ...]
      */
-    findTargetDataIndices: function (pieceIndex) {
-        var result = [];
+    findTargetDataIndices(pieceIndex: number) {
+        type DataIndices = {
+            seriesId: string
+            dataIndex: number[]
+        }
+
+        const result: DataIndices[] = [];
+        const pieceList = this._pieceList;
 
         this.eachTargetSeries(function (seriesModel) {
-            var dataIndices = [];
+            var dataIndices: number[] = [];
             var data = seriesModel.getData();
 
-            data.each(this.getDataDimension(data), function (value, dataIndex) {
+            data.each(this.getDataDimension(data), function (value: number, dataIndex: number) {
                 // Should always base on model pieceList, because it is order sensitive.
-                var pIdx = VisualMapping.findPieceIndex(value, this._pieceList);
+                var pIdx = VisualMapping.findPieceIndex(value, pieceList);
                 pIdx === pieceIndex && dataIndices.push(dataIndex);
             }, this);
 
@@ -292,14 +323,14 @@ var PiecewiseModel = VisualMapModel.extend({
         }, this);
 
         return result;
-    },
+    }
 
     /**
      * @private
-     * @param {Object} piece piece.value or piece.interval is required.
-     * @return {number} Can be Infinity or -Infinity
+     * @param piece piece.value or piece.interval is required.
+     * @return  Can be Infinity or -Infinity
      */
-    getRepresentValue: function (piece) {
+    getRepresentValue<T extends InnerVisualPiece>(piece: T) {
         var representValue;
         if (this.isCategory()) {
             representValue = piece.value;
@@ -315,21 +346,26 @@ var PiecewiseModel = VisualMapModel.extend({
                     : (pieceInterval[0] + pieceInterval[1]) / 2;
             }
         }
-        return representValue;
-    },
+        //
+        return representValue as GetPieceValueType<T>;
+    }
 
-    getVisualMeta: function (getColorVisual) {
+    getVisualMeta(
+        getColorVisual: (value: number, valueState: VisualState) => string
+    ): VisualMeta {
         // Do not support category. (category axis is ordinal, numerical)
         if (this.isCategory()) {
             return;
         }
 
-        var stops = [];
-        var outerColors = [];
+        var stops: VisualMeta['stops'] = [];
+        var outerColors: VisualMeta['outerColors'] = ['', ''];
         var visualMapModel = this;
 
-        function setStop(interval, valueState) {
-            var representValue = visualMapModel.getRepresentValue({interval: interval});
+        function setStop(interval: [number, number], valueState?: VisualState) {
+            var representValue = visualMapModel.getRepresentValue({
+                interval: interval
+            });// Not category
             if (!valueState) {
                 valueState = visualMapModel.getValueState(representValue);
             }
@@ -366,7 +402,7 @@ var PiecewiseModel = VisualMapModel.extend({
             if (interval) {
                 // Fulfill gap.
                 interval[0] > curr && setStop([curr, interval[0]], 'outOfRange');
-                setStop(interval.slice());
+                setStop(interval.slice() as [number, number]);
                 curr = interval[1];
             }
         }, this);
@@ -374,22 +410,42 @@ var PiecewiseModel = VisualMapModel.extend({
         return {stops: stops, outerColors: outerColors};
     }
 
-});
 
+    static defaultOption: PiecewiseVisualMapOption = {
+        selected: null,
+        minOpen: false,             // Whether include values that smaller than `min`.
+        maxOpen: false,             // Whether include values that bigger than `max`.
+
+        align: 'auto',              // 'auto', 'left', 'right'
+        itemWidth: 20,
+
+        itemHeight: 14,
+
+        itemSymbol: 'roundRect',
+        pieces: null,
+        categories: null,
+        splitNumber: 5,
+        selectedMode: 'multiple',   // Can be 'multiple' or 'single'.
+        itemGap: 10,                // The gap between two items, in px.
+        hoverLink: true             // Enable hover highlight.
+    }
+
+};
+
+type ResetMethod = (pieceList: InnerVisualPiece[]) => void;
 /**
  * Key is this._mode
  * @type {Object}
  * @this {module:echarts/component/viusalMap/PiecewiseMode}
  */
-var resetMethods = {
+var resetMethods: Dictionary<ResetMethod> & ThisType<PiecewiseModel> = {
 
-    splitNumber: function () {
+    splitNumber(pieceList) {
         var thisOption = this.option;
-        var pieceList = this._pieceList;
         var precision = Math.min(thisOption.precision, 20);
         var dataExtent = this.getExtent();
         var splitNumber = thisOption.splitNumber;
-        splitNumber = Math.max(parseInt(splitNumber, 10), 1);
+        splitNumber = Math.max(parseInt(splitNumber as unknown as string, 10), 1);
         thisOption.splitNumber = splitNumber;
 
         var splitStep = (dataExtent[1] - dataExtent[0]) / splitNumber;
@@ -432,31 +488,30 @@ var resetMethods = {
             });
         }
 
-        reformIntervals(pieceList);
+        reformIntervals(pieceList as Required<InnerVisualPiece>[]);
 
         zrUtil.each(pieceList, function (piece) {
             piece.text = this.formatValueText(piece.interval);
         }, this);
     },
 
-    categories: function () {
+    categories(pieceList) {
         var thisOption = this.option;
         zrUtil.each(thisOption.categories, function (cate) {
             // FIXME category模式也使用pieceList,但在visualMapping中不是使用pieceList。
             // 是否改一致。
-            this._pieceList.push({
+            pieceList.push({
                 text: this.formatValueText(cate, true),
                 value: cate
             });
         }, this);
 
         // See "Order Rule".
-        normalizeReverse(thisOption, this._pieceList);
+        normalizeReverse(thisOption, pieceList);
     },
 
-    pieces: function () {
+    pieces(pieceList) {
         var thisOption = this.option;
-        var pieceList = this._pieceList;
 
         zrUtil.each(thisOption.pieces, function (pieceListItem, index) {
 
@@ -464,7 +519,7 @@ var resetMethods = {
                 pieceListItem = {value: pieceListItem};
             }
 
-            var item = {text: '', index: index};
+            var item: InnerVisualPiece = {text: '', index: index};
 
             if (pieceListItem.label != null) {
                 item.text = pieceListItem.label;
@@ -478,15 +533,15 @@ var resetMethods = {
             else {
                 // `min` `max` is legacy option.
                 // `lt` `gt` `lte` `gte` is recommanded.
-                var interval = item.interval = [];
-                var close = item.close = [0, 0];
+                var interval = item.interval = [0, 0];
+                var close: typeof item.close = item.close = [0, 0];
 
-                var closeList = [1, 0, 1];
+                var closeList = [1, 0, 1] as const;
                 var infinityList = [-Infinity, Infinity];
 
                 var useMinMax = [];
                 for (var lg = 0; lg < 2; lg++) {
-                    var names = [['gte', 'gt', 'min'], ['lte', 'lt', 'max']][lg];
+                    var names = ([['gte', 'gt', 'min'], ['lte', 'lt', 'max']] as const)[lg];
                     for (var i = 0; i < 3 && interval[lg] == null; i++) {
                         interval[lg] = pieceListItem[names[i]];
                         close[lg] = closeList[i];
@@ -522,7 +577,7 @@ var resetMethods = {
         // See "Order Rule".
         normalizeReverse(thisOption, pieceList);
         // Only pieces
-        reformIntervals(pieceList);
+        reformIntervals(pieceList as Required<InnerVisualPiece>[]);
 
         zrUtil.each(pieceList, function (piece) {
             var close = piece.close;
@@ -536,7 +591,7 @@ var resetMethods = {
     }
 };
 
-function normalizeReverse(thisOption, pieceList) {
+function normalizeReverse(thisOption: PiecewiseVisualMapOption, pieceList: InnerVisualPiece[]) {
     var inverse = thisOption.inverse;
     if (thisOption.orient === 'vertical' ? !inverse : inverse) {
             pieceList.reverse();
diff --git a/src/component/visualMap/VisualMapModel.ts b/src/component/visualMap/VisualMapModel.ts
index 262b22c..b3594a5 100644
--- a/src/component/visualMap/VisualMapModel.ts
+++ b/src/component/visualMap/VisualMapModel.ts
@@ -17,151 +17,182 @@
 * 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 visualDefault from '../../visual/visualDefault';
-import VisualMapping from '../../visual/VisualMapping';
+import VisualMapping, { VisualMappingOption } from '../../visual/VisualMapping';
 import * as visualSolution from '../../visual/visualSolution';
 import * as modelUtil from '../../util/model';
 import * as numberUtil from '../../util/number';
+import {
+    ComponentOption,
+    BoxLayoutOptionMixin,
+    LabelOption,
+    ColorString,
+    ZRColor,
+    BorderOptionMixin,
+    OptionDataPrimitive,
+    BuiltinVisualProperty
+} from '../../util/types';
+import ComponentModel from '../../model/Component';
+import Model from '../../model/Model';
+import GlobalModel from '../../model/Global';
+import SeriesModel from '../../model/Series';
+import List from '../../data/List';
+
+const mapVisual = VisualMapping.mapVisual;
+const eachVisual = VisualMapping.eachVisual;
+const isArray = zrUtil.isArray;
+const each = zrUtil.each;
+const asc = numberUtil.asc;
+const linearMap = numberUtil.linearMap;
+
+type VisualOptionBase = {[key in BuiltinVisualProperty]?: any}
+
+type LabelFormatter = (min: OptionDataPrimitive, max?: OptionDataPrimitive) => string
+
+type VisualState = VisualMapModel['stateList'][number];
+export interface VisualMapOption<T extends VisualOptionBase = VisualOptionBase> extends
+    ComponentOption,
+    BoxLayoutOptionMixin,
+    BorderOptionMixin {
+
+    align?: string
+
+    realtime?: boolean
+    /**
+     * 'all' or null/undefined: all series.
+     * A number or an array of number: the specified series.
+     * set min: 0, max: 200, only for campatible with ec2.
+     * In fact min max should not have default value.
+     */
+    seriesIndex?: 'all' | number[] | number
 
-var mapVisual = VisualMapping.mapVisual;
-var eachVisual = VisualMapping.eachVisual;
-var isArray = zrUtil.isArray;
-var each = zrUtil.each;
-var asc = numberUtil.asc;
-var linearMap = numberUtil.linearMap;
-var noop = zrUtil.noop;
+    /**
+     * min value, must specified if pieces is not specified.
+     */
+    min?: number
 
-var VisualMapModel = echarts.extendComponentModel({
+    /**
+     * max value, must specified if pieces is not specified.
+     */
+    max?: number
+    /**
+     * Dimension to be encoded
+     */
+    dimension?: number
 
-    type: 'visualMap',
+    /**
+     * Visual configuration for the data in selection
+     */
+    inRange?: T
+    /**
+     * Visual configuration for the out of selection
+     */
+    outOfRange?: T
 
-    dependencies: ['series'],
+    controller?: {
+        inRange?: T
+        outOfRange?: T
+    }
+    target?: {
+        inRange?: T
+        outOfRange?: T
+    }
 
     /**
-     * @readOnly
-     * @type {Array.<string>}
+     * Width of the display item
      */
-    stateList: ['inRange', 'outOfRange'],
-
+    itemWidth?: number
     /**
-     * @readOnly
-     * @type {Array.<string>}
+     * Height of the display item
      */
-    replacableOptionKeys: [
-        'inRange', 'outOfRange', 'target', 'controller', 'color'
-    ],
+    itemHeight?: number
+
+    inverse?: boolean
+
+    orient?: 'horizontal' | 'vertical'
+
+    backgroundColor?: ZRColor
+    contentColor?: ZRColor
+
+    inactiveColor?: ZRColor
 
     /**
-     * [lowerBound, upperBound]
-     *
-     * @readOnly
-     * @type {Array.<number>}
+     * Padding of the component. Can be an array similar to CSS
+     */
+    padding?: number[] | number
+    /**
+     * Gap between text and item
      */
-    dataBound: [-Infinity, Infinity],
+    textGap?: number
+
+    precision?: number
 
     /**
-     * @readOnly
-     * @type {string|Object}
+     * @deprecated
+     * Option from version 2
      */
-    layoutMode: {type: 'box', ignoreSize: true},
+    color?: ColorString[]
+
+    formatter?: string | LabelFormatter
 
     /**
-     * @protected
+     * Text on the both end. Such as ['High', 'Low']
      */
-    defaultOption: {
-        show: true,
+    text?: string[]
 
-        zlevel: 0,
-        z: 4,
+    textStyle?: LabelOption
 
-        seriesIndex: 'all',     // 'all' or null/undefined: all series.
-                                // A number or an array of number: the specified series.
 
-                                // set min: 0, max: 200, only for campatible with ec2.
-                                // In fact min max should not have default value.
-        min: 0,                 // min value, must specified if pieces is not specified.
-        max: 200,               // max value, must specified if pieces is not specified.
+    categories?: unknown
+}
 
-        dimension: null,
-        inRange: null,          // 'color', 'colorHue', 'colorSaturation', 'colorLightness', 'colorAlpha',
-                                // 'symbol', 'symbolSize'
-        outOfRange: null,       // 'color', 'colorHue', 'colorSaturation',
-                                // 'colorLightness', 'colorAlpha',
-                                // 'symbol', 'symbolSize'
+export interface VisualMeta {
+    stops: { value: number, color: ColorString}[]
+    outerColors: [ColorString, ColorString]
+}
 
-        left: 0,                // 'center' ¦ 'left' ¦ 'right' ¦ {number} (px)
-        right: null,            // The same as left.
-        top: null,              // 'top' ¦ 'bottom' ¦ 'center' ¦ {number} (px)
-        bottom: 0,              // The same as top.
+class VisualMapModel<Opts extends VisualMapOption = VisualMapOption> extends ComponentModel<Opts> {
 
-        itemWidth: null,
-        itemHeight: null,
-        inverse: false,
-        orient: 'vertical',        // 'horizontal' ¦ 'vertical'
+    static type = 'visualMap'
+    type = VisualMapModel.type
 
-        backgroundColor: 'rgba(0,0,0,0)',
-        borderColor: '#ccc',       // 值域边框颜色
-        contentColor: '#5793f3',
-        inactiveColor: '#aaa',
-        borderWidth: 0,            // 值域边框线宽,单位px,默认为0(无边框)
-        padding: 5,                // 值域内边距,单位px,默认各方向内边距为5,
-                                    // 接受数组分别设定上右下左边距,同css
-        textGap: 10,               //
-        precision: 0,              // 小数精度,默认为0,无小数点
-        color: null,               //颜色(deprecated,兼容ec2,顺序同pieces,不同于inRange/outOfRange)
+    readonly dependencies = ['series']
 
-        formatter: null,
-        text: null,                // 文本,如['高', '低'],兼容ec2,text[0]对应高值,text[1]对应低值
-        textStyle: {
-            color: '#333'          // 值域文字颜色
-        }
-    },
+    readonly stateList = ['inRange', 'outOfRange'] as const
+
+    readonly replacableOptionKeys = [
+        'inRange', 'outOfRange', 'target', 'controller', 'color'
+    ] as const
+
+    readonly layoutMode = {
+        type: 'box', ignoreSize: true
+    } as const
 
     /**
-     * @protected
+     * [lowerBound, upperBound]
      */
-    init: function (option, parentModel, ecModel) {
-
-        /**
-         * @private
-         * @type {Array.<number>}
-         */
-        this._dataExtent;
-
-        /**
-         * @readOnly
-         */
-        this.targetVisuals = {};
-
-        /**
-         * @readOnly
-         */
-        this.controllerVisuals = {};
-
-        /**
-         * @readOnly
-         */
-        this.textStyleModel;
-
-        /**
-         * [width, height]
-         * @readOnly
-         * @type {Array.<number>}
-         */
-        this.itemSize;
+    dataBound = [-Infinity, Infinity]
+
+    protected _dataExtent: [number, number]
+
+    targetVisuals = {}
 
+    controllerVisuals = {}
+
+    textStyleModel: Model<LabelOption>
+
+    itemSize: number[]
+
+    init(option: Opts, parentModel: Model, ecModel: GlobalModel) {
         this.mergeDefaultAndTheme(option, ecModel);
-    },
+    }
 
     /**
      * @protected
      */
-    optionUpdated: function (newOption, isInit) {
+    optionUpdated(newOption: Opts, isInit?: boolean) {
         var thisOption = this.option;
 
         // FIXME
@@ -180,12 +211,14 @@ var VisualMapModel = echarts.extendComponentModel({
         this.resetItemSize();
 
         this.completeVisualOption();
-    },
+    }
 
     /**
      * @protected
      */
-    resetVisual: function (supplementVisualOption) {
+    resetVisual(
+        supplementVisualOption: (this: this, mappingOption: VisualMappingOption, state: string) => void
+    ) {
         var stateList = this.stateList;
         supplementVisualOption = zrUtil.bind(supplementVisualOption, this);
 
@@ -195,15 +228,15 @@ var VisualMapModel = echarts.extendComponentModel({
         this.targetVisuals = visualSolution.createVisualMappings(
             this.option.target, stateList, supplementVisualOption
         );
-    },
+    }
 
     /**
      * @protected
      * @return {Array.<number>} An array of series indices.
      */
-    getTargetSeriesIndices: function () {
+    getTargetSeriesIndices() {
         var optionSeriesIndex = this.option.seriesIndex;
-        var seriesIndices = [];
+        var seriesIndices: number[] = [];
 
         if (optionSeriesIndex == null || optionSeriesIndex === 'all') {
             this.ecModel.eachSeries(function (seriesModel, index) {
@@ -215,27 +248,30 @@ var VisualMapModel = echarts.extendComponentModel({
         }
 
         return seriesIndices;
-    },
+    }
 
     /**
      * @public
      */
-    eachTargetSeries: function (callback, context) {
+    eachTargetSeries<Ctx>(
+        callback: (this: Ctx, series: SeriesModel) => void,
+        context?: Ctx
+    ) {
         zrUtil.each(this.getTargetSeriesIndices(), function (seriesIndex) {
             callback.call(context, this.ecModel.getSeriesByIndex(seriesIndex));
         }, this);
-    },
+    }
 
     /**
      * @pubilc
      */
-    isTargetSeries: function (seriesModel) {
+    isTargetSeries(seriesModel: SeriesModel) {
         var is = false;
         this.eachTargetSeries(function (model) {
             model === seriesModel && (is = true);
         });
         return is;
-    },
+    }
 
     /**
      * @example
@@ -245,20 +281,23 @@ var VisualMapModel = echarts.extendComponentModel({
      * this.formatValueText([this.dataBound[0], max]); // using data lower bound.
      * this.formatValueText([min, this.dataBound[1]]); // using data upper bound.
      *
-     * @param {number|Array.<number>} value Real value, or this.dataBound[0 or 1].
-     * @param {boolean} [isCategory=false] Only available when value is number.
-     * @param {Array.<string>} edgeSymbols Open-close symbol when value is interval.
-     * @return {string}
+     * @param value Real value, or this.dataBound[0 or 1].
+     * @param isCategory Only available when value is number.
+     * @param edgeSymbols Open-close symbol when value is interval.
      * @protected
      */
-    formatValueText: function (value, isCategory, edgeSymbols) {
+    formatValueText(
+        value: number | string | number[],
+        isCategory?: boolean,
+        edgeSymbols?: string[]
+    ): string {
         var option = this.option;
         var precision = option.precision;
         var dataBound = this.dataBound;
         var formatter = option.formatter;
-        var isMinMax;
-        var textValue;
-        edgeSymbols = edgeSymbols || ['<', '>'];
+        var isMinMax: boolean;
+        var textValue: string | string[];
+        edgeSymbols = edgeSymbols || ['<', '>'] as [string, string];
 
         if (zrUtil.isArray(value)) {
             value = value.slice();
@@ -266,28 +305,28 @@ var VisualMapModel = echarts.extendComponentModel({
         }
 
         textValue = isCategory
-            ? value
+            ? value as string   // Value is string when isCategory
             : (isMinMax
-                ? [toFixed(value[0]), toFixed(value[1])]
-                : toFixed(value)
+                ? [toFixed((value as number[])[0]), toFixed((value as number[])[1])]
+                : toFixed(value as number)
             );
 
         if (zrUtil.isString(formatter)) {
             return formatter
-                .replace('{value}', isMinMax ? textValue[0] : textValue)
-                .replace('{value2}', isMinMax ? textValue[1] : textValue);
+                .replace('{value}', isMinMax ? (textValue as string[])[0] : textValue as string)
+                .replace('{value2}', isMinMax ? (textValue as string[])[1] : textValue as string);
         }
         else if (zrUtil.isFunction(formatter)) {
             return isMinMax
-                ? formatter(value[0], value[1])
-                : formatter(value);
+                ? formatter((value as number[])[0], (value as number[])[1])
+                : formatter(value as number);
         }
 
         if (isMinMax) {
-            if (value[0] === dataBound[0]) {
+            if ((value as number[])[0] === dataBound[0]) {
                 return edgeSymbols[0] + ' ' + textValue[1];
             }
-            else if (value[1] === dataBound[1]) {
+            else if ((value as number[])[1] === dataBound[1]) {
                 return edgeSymbols[1] + ' ' + textValue[0];
             }
             else {
@@ -295,40 +334,37 @@ var VisualMapModel = echarts.extendComponentModel({
             }
         }
         else { // Format single value (includes category case).
-            return textValue;
+            return textValue as string;
         }
 
-        function toFixed(val) {
+        function toFixed(val: number) {
             return val === dataBound[0]
                 ? 'min'
                 : val === dataBound[1]
                 ? 'max'
                 : (+val).toFixed(Math.min(precision, 20));
         }
-    },
+    }
 
     /**
      * @protected
      */
-    resetExtent: function () {
+    resetExtent() {
         var thisOption = this.option;
 
         // Can not calculate data extent by data here.
         // Because series and data may be modified in processing stage.
         // So we do not support the feature "auto min/max".
 
-        var extent = asc([thisOption.min, thisOption.max]);
+        var extent = asc([thisOption.min, thisOption.max] as [number, number]);
 
         this._dataExtent = extent;
-    },
+    }
 
     /**
-     * @public
-     * @param {module:echarts/data/List} list
-     * @return {string} Concrete dimention. If return null/undefined,
-     *                  no dimension used.
+     * Return  Concrete dimention. If return null/undefined, no dimension used.
      */
-    getDataDimension: function (list) {
+    getDataDimension(list: List) {
         var optDim = this.option.dimension;
         var listDimensions = list.dimensions;
         if (optDim == null && !listDimensions.length) {
@@ -347,20 +383,14 @@ var VisualMapModel = echarts.extendComponentModel({
                 return dimName;
             }
         }
-    },
+    }
 
-    /**
-     * @public
-     * @override
-     */
-    getExtent: function () {
-        return this._dataExtent.slice();
-    },
+    getExtent() {
+        return this._dataExtent.slice() as [number, number];
+    }
+
+    completeVisualOption() {
 
-    /**
-     * @protected
-     */
-    completeVisualOption: function () {
         var ecModel = this.ecModel;
         var thisOption = this.option;
         var base = {inRange: thisOption.inRange, outOfRange: thisOption.outOfRange};
@@ -379,7 +409,7 @@ var VisualMapModel = echarts.extendComponentModel({
         // completeInactive.call(this, target, 'outOfRange', 'inRange');
         completeController.call(this, controller);
 
-        function completeSingle(base) {
+        function completeSingle(this: VisualMapModel, base?: VisualMapOption['target']) {
             // Compatible with ec2 dataRange.color.
             // The mapping order of dataRange.color is: [high value, ..., low value]
             // whereas inRange.color and outOfRange.color is [low value, ..., high value]
@@ -399,32 +429,20 @@ var VisualMapModel = echarts.extendComponentModel({
             // constant DEFAULT_COLOR.
             // If user do not want the defualt color, set inRange: {color: null}.
             base.inRange = base.inRange || {color: ecModel.get('gradientColor')};
-
-            // If using shortcut like: {inRange: 'symbol'}, complete default value.
-            each(this.stateList, function (state) {
-                var visualType = base[state];
-
-                if (zrUtil.isString(visualType)) {
-                    var defa = visualDefault.get(visualType, 'active', isCategory);
-                    if (defa) {
-                        base[state] = {};
-                        base[state][visualType] = defa;
-                    }
-                    else {
-                        // Mark as not specified.
-                        delete base[state];
-                    }
-                }
-            }, this);
         }
 
-        function completeInactive(base, stateExist, stateAbsent) {
+        function completeInactive(
+            this: VisualMapModel,
+            base: VisualMapOption['target'],
+            stateExist: VisualState,
+            stateAbsent: VisualState
+        ) {
             var optExist = base[stateExist];
             var optAbsent = base[stateAbsent];
 
             if (optExist && !optAbsent) {
                 optAbsent = base[stateAbsent] = {};
-                each(optExist, function (visualData, visualType) {
+                each(optExist, function (visualData, visualType: BuiltinVisualProperty) {
                     if (!VisualMapping.isValidType(visualType)) {
                         return;
                     }
@@ -448,14 +466,14 @@ var VisualMapModel = echarts.extendComponentModel({
             }
         }
 
-        function completeController(controller) {
+        function completeController(this: VisualMapModel, controller?: VisualMapOption['controller']) {
             var symbolExists = (controller.inRange || {}).symbol
                 || (controller.outOfRange || {}).symbol;
             var symbolSizeExists = (controller.inRange || {}).symbolSize
                 || (controller.outOfRange || {}).symbolSize;
             var inactiveColor = this.get('inactiveColor');
 
-            each(this.stateList, function (state) {
+            each(this.stateList, function (state: VisualState) {
 
                 var itemSize = this.itemSize;
                 var visuals = controller[state];
@@ -501,39 +519,36 @@ var VisualMapModel = echarts.extendComponentModel({
 
             }, this);
         }
-    },
+    }
 
-    /**
-     * @protected
-     */
-    resetItemSize: function () {
+    resetItemSize() {
         this.itemSize = [
-            parseFloat(this.get('itemWidth')),
-            parseFloat(this.get('itemHeight'))
+            parseFloat(this.get('itemWidth') as unknown as string),
+            parseFloat(this.get('itemHeight') as unknown as string)
         ];
-    },
+    }
 
-    /**
-     * @public
-     */
-    isCategory: function () {
+    isCategory() {
         return !!this.option.categories;
-    },
+    }
 
     /**
      * @public
      * @abstract
      */
-    setSelected: noop,
+    setSelected(selected?: any) {}
+
+    getSelected(): any {
+        return null;
+    }
 
     /**
      * @public
      * @abstract
-     * @param {*|module:echarts/data/List} valueOrData
-     * @param {number} dataIndex
-     * @return {string} state See this.stateList
      */
-    getValueState: noop,
+    getValueState(value: any): VisualMapModel['stateList'][number] {
+        return null;
+    }
 
     /**
      * FIXME
@@ -543,15 +558,55 @@ var VisualMapModel = echarts.extendComponentModel({
      *
      * @pubilc
      * @abstract
-     * @param {Function} getColorVisual
+     * @param getColorVisual
      *        params: value, valueState
      *        return: color
      * @return {Object} visualMeta
      *        should includes {stops, outerColors}
      *        outerColor means [colorBeyondMinValue, colorBeyondMaxValue]
      */
-    getVisualMeta: noop
+    getVisualMeta(getColorVisual: (value: number, valueState: VisualState) => string): VisualMeta {
+        return null;
+    }
+
+
+    defaultOption: VisualMapOption = {
+        show: true,
+
+        zlevel: 0,
+        z: 4,
+
+        seriesIndex: 'all',
+
+        min: 0,
+        max: 200,
+
+        left: 0,
+        right: null,
+        top: null,
+        bottom: 0,
+
+        itemWidth: null,
+        itemHeight: null,
+        inverse: false,
+        orient: 'vertical',        // 'horizontal' ¦ 'vertical'
+
+        backgroundColor: 'rgba(0,0,0,0)',
+        borderColor: '#ccc',       // 值域边框颜色
+        contentColor: '#5793f3',
+        inactiveColor: '#aaa',
+        borderWidth: 0,
+        padding: 5,
+                                    // 接受数组分别设定上右下左边距,同css
+        textGap: 10,               //
+        precision: 0,              // 小数精度,默认为0,无小数点
+
+        textStyle: {
+            color: '#333'          // 值域文字颜色
+        }
+    }
+}
 
-});
+ComponentModel.registerClass(VisualMapModel);
 
 export default VisualMapModel;
diff --git a/src/component/visualMap/helper.ts b/src/component/visualMap/helper.ts
index 8c50dca..e3bb71b 100644
--- a/src/component/visualMap/helper.ts
+++ b/src/component/visualMap/helper.ts
@@ -17,18 +17,29 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as zrUtil from 'zrender/src/core/util';
 import {getLayoutRect} from '../../util/layout';
+import VisualMapModel from './VisualMapModel';
+import ExtensionAPI from '../../ExtensionAPI';
+import { Payload } from '../../util/types';
+
+const paramsSet = [
+    ['left', 'right', 'width'],
+    ['top', 'bottom', 'height']
+] as const;
+type LayoutKey = (typeof paramsSet)[number][number];
 
 /**
- * @param {module:echarts/component/visualMap/VisualMapModel} visualMapModel\
- * @param {module:echarts/ExtensionAPI} api
- * @param {Array.<number>} itemSize always [short, long]
+ * @param visualMapModel
+ * @param api
+ * @param itemSize always [short, long]
  * @return {string} 'left' or 'right' or 'top' or 'bottom'
  */
-export function getItemAlign(visualMapModel, api, itemSize) {
+export function getItemAlign(
+    visualMapModel: VisualMapModel,
+    api: ExtensionAPI,
+    itemSize: number[]
+) {
     var modelOption = visualMapModel.option;
     var itemAlign = modelOption.align;
 
@@ -40,20 +51,18 @@ export function getItemAlign(visualMapModel, api, itemSize) {
     var ecSize = {width: api.getWidth(), height: api.getHeight()};
     var realIndex = modelOption.orient === 'horizontal' ? 1 : 0;
 
-    var paramsSet = [
-        ['left', 'right', 'width'],
-        ['top', 'bottom', 'height']
-    ];
     var reals = paramsSet[realIndex];
     var fakeValue = [0, null, 10];
 
-    var layoutInput = {};
+    var layoutInput = {} as {
+        [key in LayoutKey]: number | string
+    };
     for (var i = 0; i < 3; i++) {
         layoutInput[paramsSet[1 - realIndex][i]] = fakeValue[i];
         layoutInput[reals[i]] = i === 2 ? itemSize[0] : modelOption[reals[i]];
     }
 
-    var rParam = [['x', 'width', 3], ['y', 'height', 0]][realIndex];
+    var rParam = ([['x', 'width', 3], ['y', 'height', 0]] as const)[realIndex];
     var rect = getLayoutRect(layoutInput, ecSize, modelOption.padding);
 
     return reals[
@@ -66,7 +75,7 @@ export function getItemAlign(visualMapModel, api, itemSize) {
  * Prepare dataIndex for outside usage, where dataIndex means rawIndex, and
  * dataIndexInside means filtered index.
  */
-export function makeHighDownBatch(batch, visualMapModel) {
+export function makeHighDownBatch(batch: Payload['batch'], visualMapModel: VisualMapModel): Payload['batch'] {
     zrUtil.each(batch || [], function (batchItem) {
         if (batchItem.dataIndex != null) {
             batchItem.dataIndexInside = batchItem.dataIndex;
diff --git a/src/component/visualMap/typeDefaulter.ts b/src/component/visualMap/typeDefaulter.ts
index e3b9993..0576b92 100644
--- a/src/component/visualMap/typeDefaulter.ts
+++ b/src/component/visualMap/typeDefaulter.ts
@@ -17,21 +17,23 @@
 * under the License.
 */
 
-// @ts-nocheck
+import Component, { ComponentModelConstructor } from '../../model/Component';
+import {VisualMapOption} from './VisualMapModel';
+import {PiecewiseVisualMapOption} from './PiecewiseModel';
+import {ContinousVisualMapOption} from './ContinuousModel';
 
-import Component from '../../model/Component';
-
-Component.registerSubTypeDefaulter('visualMap', function (option) {
+(Component as ComponentModelConstructor).registerSubTypeDefaulter(
+    'visualMap', function (option: VisualMapOption) {
     // Compatible with ec2, when splitNumber === 0, continuous visualMap will be used.
     return (
             !option.categories
             && (
                 !(
-                    option.pieces
-                        ? option.pieces.length > 0
-                        : option.splitNumber > 0
+                    (option as PiecewiseVisualMapOption).pieces
+                        ? ((option as PiecewiseVisualMapOption)).pieces.length > 0
+                        : ((option as PiecewiseVisualMapOption)).splitNumber > 0
                 )
-                || option.calculable
+                || (option as ContinousVisualMapOption).calculable
             )
         )
         ? 'continuous' : 'piecewise';
diff --git a/src/component/visualMap/visualMapAction.ts b/src/component/visualMap/visualMapAction.ts
index 9cb2e88..b38640f 100644
--- a/src/component/visualMap/visualMapAction.ts
+++ b/src/component/visualMap/visualMapAction.ts
@@ -17,9 +17,8 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as echarts from '../../echarts';
+import VisualMapModel from './VisualMapModel';
 
 var actionInfo = {
     type: 'selectDataRange',
@@ -31,7 +30,6 @@ var actionInfo = {
 echarts.registerAction(actionInfo, function (payload, ecModel) {
 
     ecModel.eachComponent({mainType: 'visualMap', query: payload}, function (model) {
-        model.setSelected(payload.selected);
+        (model as VisualMapModel).setSelected(payload.selected);
     });
-
 });
diff --git a/src/data/DataDimensionInfo.ts b/src/data/DataDimensionInfo.ts
index f27a5b4..71aa57b 100644
--- a/src/data/DataDimensionInfo.ts
+++ b/src/data/DataDimensionInfo.ts
@@ -109,6 +109,10 @@ class DataDimensionInfo {
      * Mandatory.
      */
     isExtraCoord: boolean;
+    /**
+     * If this dimension if for calculated value like stacking
+     */
+    isCalculationCoord: boolean
 
     defaultTooltip: boolean;
 
diff --git a/src/echarts.ts b/src/echarts.ts
index a073a31..85929b9 100644
--- a/src/echarts.ts
+++ b/src/echarts.ts
@@ -52,7 +52,7 @@ import {CoordinateSystem, CoordinateSystemCreator} from './coord/CoordinateSyste
 import { parseClassType } from './util/clazz';
 import {ECEventProcessor} from './util/ECEventProcessor';
 import {
-    Payload, PayloadItem, ECElement, RendererType, ECEvent,
+    Payload, ECElement, RendererType, ECEvent,
     ActionHandler, ActionInfo, OptionPreprocessor, PostUpdater,
     LoadingEffect, LoadingEffectCreator, StageHandlerInternal,
     StageHandlerOverallReset, StageHandler,
@@ -1462,7 +1462,7 @@ class ECharts {
             // Batch action
             if (payload.batch) {
                 batched = true;
-                payloads = zrUtil.map<PayloadItem, Payload, unknown>(payload.batch, function (item) {
+                payloads = zrUtil.map<Payload['batch'][0], Payload, unknown>(payload.batch, function (item) {
                     item = zrUtil.defaults(zrUtil.extend({}, item), payload);
                     item.batch = null;
                     return item as Payload;
diff --git a/src/util/number.ts b/src/util/number.ts
index 1a9d0ba..6d16208 100644
--- a/src/util/number.ts
+++ b/src/util/number.ts
@@ -142,7 +142,7 @@ export function round(x: number | string, precision?: number, returnStr?: boolea
  * Inplacd asc sort arr.
  * The input arr will be modified.
  */
-export function asc(arr: number[]): number[] {
+export function asc<T extends number[]>(arr: T): T {
     arr.sort(function (a, b) {
         return a - b;
     });
@@ -469,7 +469,7 @@ export function quantile(ascArr: number[], p: number): number {
 
 type IntervalItem = {
     interval: [number, number]
-    close: [number, number]
+    close: [0 | 1, 0 | 1]
 }
 /**
  * Order intervals asc, and split them when overlap.
@@ -507,7 +507,7 @@ export function reformIntervals(list: IntervalItem[]): IntervalItem[] {
         for (var lg = 0; lg < 2; lg++) {
             if (interval[lg] <= curr) {
                 interval[lg] = curr;
-                close[lg] = !lg ? 1 - currClose : 1;
+                close[lg] = (!lg ? 1 - currClose : 1) as 0 | 1;
             }
             curr = interval[lg];
             currClose = close[lg];
diff --git a/src/util/types.ts b/src/util/types.ts
index c20978c..4baa462 100644
--- a/src/util/types.ts
+++ b/src/util/types.ts
@@ -96,7 +96,7 @@ export interface DataModel extends DataHost, DataFormatMixin {}
     // Pick<DataHost, 'getData'>,
     // Pick<DataFormatMixin, 'getDataParams' | 'formatTooltip'> {}
 
-export interface PayloadItem {
+interface PayloadItem {
     excludeSeriesId?: string | string[];
     [other: string]: any;
 }
@@ -505,6 +505,12 @@ export interface ShadowOptionMixin {
     shadowOffsetY?: number
 }
 
+export interface BorderOptionMixin {
+    borderColor?: string
+    borderWidth?: number
+    borderType?: ZRLineType
+}
+
 export type AnimationDelayCallbackParam = {
     count: number
     index: number
@@ -579,11 +585,8 @@ export interface SymbolOptionMixin {
  * ItemStyleOption is a most common used set to config element styles.
  * It includes both fill and stroke style.
  */
-export interface ItemStyleOption extends ShadowOptionMixin {
+export interface ItemStyleOption extends ShadowOptionMixin, BorderOptionMixin {
     color?: ZRColor
-    borderColor?: string
-    borderWidth?: number
-    borderType?: ZRLineType
     opacity?: number
 }
 
@@ -709,6 +712,7 @@ export interface LabelLineOption {
 }
 
 export interface ComponentOption {
+    show?: boolean
     type?: string;
     id?: string;
     name?: string;
@@ -737,4 +741,4 @@ export interface SeriesOption extends
     progressiveChunkMode?: 'mod'
 
     // FIXME:TS more
-}
+}
\ No newline at end of file
diff --git a/src/visual/VisualMapping.ts b/src/visual/VisualMapping.ts
index 49601e5..291ff5b 100644
--- a/src/visual/VisualMapping.ts
+++ b/src/visual/VisualMapping.ts
@@ -87,10 +87,14 @@ interface VisualHandler {
 }
 
 interface VisualMappingPiece {
-    value?: number
+    index?: number
+
+    value?: number | string
     interval?: [number, number]
     close?: [0 | 1, 0 | 1]
 
+    text?: string
+
     visual?: VisualOptionPiecewise
 }
 
@@ -479,7 +483,7 @@ class VisualMapping<VisualOption
                 ) {
                     return i;
                 }
-                findClosestWhenOutside && updatePossible(pieceValue, i);
+                findClosestWhenOutside && updatePossible(pieceValue as number, i);
             }
         }
 


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