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

[incubator-echarts] 01/03: ts: add type to brush and parallel.

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

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

commit 9e5b639637edd9f400634dc9927fd7e4580c4518
Author: 100pah <su...@gmail.com>
AuthorDate: Wed Mar 18 02:37:09 2020 +0800

    ts: add type to brush and parallel.
---
 src/chart/bar/BarSeries.ts                     |   8 +-
 src/chart/candlestick/CandlestickSeries.ts     |   4 +-
 src/chart/effectScatter/EffectScatterSeries.ts |   6 +-
 src/chart/parallel/ParallelSeries.ts           |  80 ++-
 src/chart/parallel/ParallelView.ts             |   5 +-
 src/chart/parallel/parallelVisual.ts           |  42 +-
 src/chart/pie/PieSeries.ts                     |   4 +-
 src/chart/scatter/ScatterSeries.ts             |   6 +-
 src/component/axis/ParallelAxisView.ts         |  98 ++--
 src/component/axis/parallelAxisAction.ts       |  29 +-
 src/component/axisPointer/BaseAxisPointer.ts   |  16 +-
 src/component/brush.ts                         |   2 +-
 src/component/brush/BrushModel.ts              | 190 +++++--
 src/component/brush/BrushView.ts               | 108 ++--
 src/component/brush/brushAction.ts             |  31 +-
 src/component/brush/preprocessor.ts            |  15 +-
 src/component/brush/selector.ts                |  98 +++-
 src/component/brush/visualEncoding.ts          | 190 +++----
 src/component/dataZoom/history.ts              |   8 +-
 src/component/geo/GeoView.ts                   |   2 +
 src/component/helper/BrushController.ts        | 741 ++++++++++++++-----------
 src/component/helper/BrushTargetManager.ts     | 534 ++++++++++--------
 src/component/helper/brushHelper.ts            |  24 +-
 src/component/helper/cursorHelper.ts           |  14 +-
 src/component/parallel.ts                      |  71 ++-
 src/component/toolbox/feature/Brush.ts         |  13 +-
 src/component/toolbox/feature/DataZoom.ts      |  90 +--
 src/coord/CoordinateSystem.ts                  |  31 +-
 src/coord/cartesian/Grid.ts                    |  11 +-
 src/coord/cartesian/cartesianAxisHelper.ts     |  12 +-
 src/coord/geo/GeoModel.ts                      |   7 +-
 src/coord/parallel/AxisModel.ts                | 110 ++--
 src/coord/parallel/Parallel.ts                 | 240 ++++----
 src/coord/parallel/ParallelAxis.ts             |  67 +--
 src/coord/parallel/ParallelModel.ts            | 125 +++--
 src/coord/parallel/parallelCreator.ts          |  24 +-
 src/coord/parallel/parallelPreprocessor.ts     |  10 +-
 src/data/Source.ts                             |   3 +-
 src/echarts.ts                                 |  12 +-
 src/model/Series.ts                            |  17 +-
 src/util/graphic.ts                            |   4 +-
 src/util/model.ts                              |  72 +--
 src/util/throttle.ts                           |  63 ++-
 src/view/Component.ts                          |   4 +-
 44 files changed, 1869 insertions(+), 1372 deletions(-)

diff --git a/src/chart/bar/BarSeries.ts b/src/chart/bar/BarSeries.ts
index 50c8653..850e994 100644
--- a/src/chart/bar/BarSeries.ts
+++ b/src/chart/bar/BarSeries.ts
@@ -23,6 +23,8 @@ import { ItemStyleOption, OptionDataValue, LabelOption, SeriesStackOptionMixin }
 import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
 import type Polar from '../../coord/polar/Polar';
 import { inheritDefaultOption } from '../../util/component';
+import List from '../../data/List';
+import { BrushCommonSelectorsForSeries } from '../../component/brush/selector';
 
 type BarDataValue = OptionDataValue | OptionDataValue[]
 
@@ -86,8 +88,6 @@ class BarSeriesModel extends BaseBarSeriesModel<BarSeriesOption> {
 
     static dependencies = ['grid', 'polar']
 
-    readonly brushSelector = 'rect'
-
     coordinateSystem: Cartesian2D | Polar
 
     /**
@@ -113,6 +113,10 @@ class BarSeriesModel extends BaseBarSeriesModel<BarSeriesOption> {
         return progressiveThreshold;
     }
 
+    brushSelector(dataIndex: number, data: List, selectors: BrushCommonSelectorsForSeries): boolean {
+        return selectors.rect(data.getItemLayout(dataIndex));
+    }
+
     static defaultOption: BarSeriesOption = inheritDefaultOption(BaseBarSeriesModel.defaultOption, {
         // If clipped
         // Only available on cartesian2d
diff --git a/src/chart/candlestick/CandlestickSeries.ts b/src/chart/candlestick/CandlestickSeries.ts
index 172c303..862fbfd 100644
--- a/src/chart/candlestick/CandlestickSeries.ts
+++ b/src/chart/candlestick/CandlestickSeries.ts
@@ -33,6 +33,7 @@ import {
 } from '../../util/types';
 import List from '../../data/List';
 import Cartesian2D from '../../coord/cartesian/Cartesian2D';
+import { BrushCommonSelectorsForSeries } from '../../component/brush/selector';
 
 type CandlestickDataValue = OptionDataValueNumeric[];
 export interface CandlestickDataItemOption {
@@ -149,8 +150,7 @@ class CandlestickSeriesModel extends SeriesModel<CandlestickSeriesOption> {
         return 'open';
     }
 
-    // @ts-ignore
-    brushSelector(dataIndex: number, data: List, selectors) {
+    brushSelector(dataIndex: number, data: List, selectors: BrushCommonSelectorsForSeries): boolean {
         var itemLayout = data.getItemLayout(dataIndex);
         return itemLayout && selectors.rect(itemLayout.brushRect);
     }
diff --git a/src/chart/effectScatter/EffectScatterSeries.ts b/src/chart/effectScatter/EffectScatterSeries.ts
index b9b2ac2..c513d27 100644
--- a/src/chart/effectScatter/EffectScatterSeries.ts
+++ b/src/chart/effectScatter/EffectScatterSeries.ts
@@ -34,6 +34,7 @@ import {
 import GlobalModel from '../../model/Global';
 import List from '../../data/List';
 import type { SymbolDrawItemModelOption } from '../helper/SymbolDraw';
+import { BrushCommonSelectorsForSeries } from '../../component/brush/selector';
 
 type ScatterDataValue = OptionDataValue | OptionDataValue[]
 
@@ -80,12 +81,15 @@ class EffectScatterSeriesModel extends SeriesModel<EffectScatterSeriesOption> {
 
     static readonly dependencies = ['grid', 'polar']
 
-    brushSelector = 'point'
 
     getInitialData(option: EffectScatterSeriesOption, ecModel: GlobalModel): List {
         return createListFromArray(this.getSource(), this, {useEncodeDefaulter: true});
     }
 
+    brushSelector(dataIndex: number, data: List, selectors: BrushCommonSelectorsForSeries): boolean {
+        return selectors.point(data.getItemLayout(dataIndex));
+    }
+
     static defaultOption: EffectScatterSeriesOption = {
         coordinateSystem: 'cartesian2d',
         zlevel: 0,
diff --git a/src/chart/parallel/ParallelSeries.ts b/src/chart/parallel/ParallelSeries.ts
index 9f0d6f3..d610f2c 100644
--- a/src/chart/parallel/ParallelSeries.ts
+++ b/src/chart/parallel/ParallelSeries.ts
@@ -17,39 +17,72 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import {each, createHashMap} from 'zrender/src/core/util';
 import SeriesModel from '../../model/Series';
 import createListFromArray from '../helper/createListFromArray';
+import { SeriesOption, SeriesEncodeOptionMixin, LineStyleOption, LabelOption, AnimationOptionMixin, SeriesTooltipOption, DimensionName } from '../../util/types';
+import GlobalModel from '../../model/Global';
+import List from '../../data/List';
+import { ParallelActiveState } from '../../coord/parallel/AxisModel';
+import Parallel from '../../coord/parallel/Parallel';
+import Source from '../../data/Source';
+import ParallelModel from '../../coord/parallel/ParallelModel';
 
-export default SeriesModel.extend({
 
-    type: 'series.parallel',
+export interface ParallelSeriesOption extends
+    SeriesOption,
+    SeriesEncodeOptionMixin {
 
-    dependencies: ['parallel'],
+    coordinateSystem?: string;
+    parallelIndex?: number;
+    parallelId?: string;
 
-    visualColorAccessPath: ['lineStyle', 'color'],
+    label?: LabelOption;
+    lineStyle?: LineStyleOption;
 
-    getInitialData: function (option, ecModel) {
+    inactiveOpacity?: number;
+    activeOpacity?: number;
+
+    smooth?: boolean | number;
+    realtime?: boolean;
+    tooltip?: SeriesTooltipOption;
+
+    emphasis?: {
+        label?: LabelOption;
+        lineStyle?: LineStyleOption;
+    }
+}
+
+class ParallelSeries extends SeriesModel<ParallelSeriesOption> {
+
+    static type = 'series.parallel';
+    readonly type = ParallelSeries.type;
+
+    static dependencies = ['parallel'];
+
+    visualColorAccessPath = ['lineStyle', 'color'];
+
+    coordinateSystem: Parallel;
+
+
+    getInitialData(option: ParallelSeriesOption, ecModel: GlobalModel): List {
         var source = this.getSource();
 
         setEncodeAndDimensions(source, this);
 
         return createListFromArray(source, this);
-    },
+    }
 
     /**
      * User can get data raw indices on 'axisAreaSelected' event received.
      *
-     * @public
-     * @param {string} activeState 'active' or 'inactive' or 'normal'
-     * @return {Array.<number>} Raw indices
+     * @return Raw indices
      */
-    getRawIndicesByActiveState: function (activeState) {
+    getRawIndicesByActiveState(activeState: ParallelActiveState): number[] {
         var coordSys = this.coordinateSystem;
         var data = this.getData();
-        var indices = [];
+        var indices = [] as number[];
 
         coordSys.eachActiveState(data, function (theActiveState, dataIndex) {
             if (activeState === theActiveState) {
@@ -58,11 +91,11 @@ export default SeriesModel.extend({
         });
 
         return indices;
-    },
+    }
 
-    defaultOption: {
-        zlevel: 0,                  // 一级层叠
-        z: 2,                       // 二级层叠
+    static defaultOption: ParallelSeriesOption = {
+        zlevel: 0,
+        z: 2,
 
         coordinateSystem: 'parallel',
         parallelIndex: 0,
@@ -89,10 +122,13 @@ export default SeriesModel.extend({
         smooth: false, // true | false | number
 
         animationEasing: 'linear'
-    }
-});
+    };
 
-function setEncodeAndDimensions(source, seriesModel) {
+}
+
+SeriesModel.registerClass(ParallelSeries);
+
+function setEncodeAndDimensions(source: Source, seriesModel: ParallelSeries): void {
     // The mapping of parallelAxis dimension to data dimension can
     // be specified in parallelAxis.option.dim. For example, if
     // parallelAxis.option.dim is 'dim3', it mapping to the third
@@ -106,7 +142,7 @@ function setEncodeAndDimensions(source, seriesModel) {
 
     var parallelModel = seriesModel.ecModel.getComponent(
         'parallel', seriesModel.get('parallelIndex')
-    );
+    ) as ParallelModel;
     if (!parallelModel) {
         return;
     }
@@ -118,6 +154,8 @@ function setEncodeAndDimensions(source, seriesModel) {
     });
 }
 
-function convertDimNameToNumber(dimName) {
+function convertDimNameToNumber(dimName: DimensionName): number {
     return +dimName.replace('dim', '');
 }
+
+export default ParallelSeries;
diff --git a/src/chart/parallel/ParallelView.ts b/src/chart/parallel/ParallelView.ts
index f0b8dc1..7bbeb2f 100644
--- a/src/chart/parallel/ParallelView.ts
+++ b/src/chart/parallel/ParallelView.ts
@@ -259,12 +259,11 @@ function updateElCommon(el, data, dataIndex, seriesScope) {
 //     return false;
 // }
 
-// FIXME
-// 公用方法?
+// FIXME put in common util?
 function isEmptyValue(val, axisType) {
     return axisType === 'category'
         ? val == null
         : (val == null || isNaN(val)); // axisType === 'value'
 }
 
-export default ParallelView;
\ No newline at end of file
+export default ParallelView;
diff --git a/src/chart/parallel/parallelVisual.ts b/src/chart/parallel/parallelVisual.ts
index 25d82d1..fd68909 100644
--- a/src/chart/parallel/parallelVisual.ts
+++ b/src/chart/parallel/parallelVisual.ts
@@ -17,22 +17,25 @@
 * under the License.
 */
 
-// @ts-nocheck
 
-var opacityAccessPath = ['lineStyle', 'normal', 'opacity'];
+import ParallelSeries, { ParallelSeriesOption } from './ParallelSeries';
+import { StageHandler } from '../../util/types';
 
-export default {
+
+var opacityAccessPath = ['lineStyle', 'opacity'] as const;
+
+var parallelVisual: StageHandler = {
 
     seriesType: 'parallel',
 
-    reset: function (seriesModel, ecModel, api) {
+    reset: function (seriesModel: ParallelSeries, ecModel) {
 
-        var itemStyleModel = seriesModel.getModel('itemStyle');
+        // var itemStyleModel = seriesModel.getModel('itemStyle');
         var lineStyleModel = seriesModel.getModel('lineStyle');
         var globalColors = ecModel.get('color');
 
         var color = lineStyleModel.get('color')
-            || itemStyleModel.get('color')
+            // || itemStyleModel.get('color')
             || globalColors[seriesModel.seriesIndex % globalColors.length];
         var inactiveOpacity = seriesModel.get('inactiveOpacity');
         var activeOpacity = seriesModel.get('activeOpacity');
@@ -49,18 +52,21 @@ export default {
 
         data.setVisual('color', color);
 
-        function progress(params, data) {
-            coordSys.eachActiveState(data, function (activeState, dataIndex) {
-                var opacity = opacityMap[activeState];
-                if (activeState === 'normal' && data.hasItemOption) {
-                    var itemOpacity = data.getItemModel(dataIndex).get(opacityAccessPath, true);
-                    itemOpacity != null && (opacity = itemOpacity);
-                }
-                data.setItemVisual(dataIndex, 'opacity', opacity);
-            }, params.start, params.end);
-        }
-
-        return {progress: progress};
+        return {
+            progress(params, data) {
+                coordSys.eachActiveState(data, function (activeState, dataIndex) {
+                    var opacity = opacityMap[activeState];
+                    if (activeState === 'normal' && data.hasItemOption) {
+                        var itemOpacity = data.getItemModel<ParallelSeriesOption>(dataIndex).get(
+                            opacityAccessPath, true
+                        );
+                        itemOpacity != null && (opacity = itemOpacity);
+                    }
+                    data.setItemVisual(dataIndex, 'opacity', opacity);
+                }, params.start, params.end);
+            }
+        };
     }
 };
 
+export default parallelVisual;
diff --git a/src/chart/pie/PieSeries.ts b/src/chart/pie/PieSeries.ts
index c62cad2..cc3b40f 100644
--- a/src/chart/pie/PieSeries.ts
+++ b/src/chart/pie/PieSeries.ts
@@ -42,7 +42,7 @@ import List from '../../data/List';
 
 
 interface PieLabelOption extends Omit<LabelOption, 'rotate'> {
-    rotate?: number | boolean
+    rotate?: number
     alignTo?: 'none' | 'labelLine' | 'edge'
     margin?: string | number
     bleedMargin?: number
@@ -243,7 +243,7 @@ class PieSeriesModel extends SeriesModel<PieSeriesOption> {
 
         label: {
             // If rotate around circle
-            rotate: false,
+            rotate: 0,
             show: true,
             // 'outer', 'inside', 'center'
             position: 'outer',
diff --git a/src/chart/scatter/ScatterSeries.ts b/src/chart/scatter/ScatterSeries.ts
index 94b2179..e0d9d8e 100644
--- a/src/chart/scatter/ScatterSeries.ts
+++ b/src/chart/scatter/ScatterSeries.ts
@@ -35,6 +35,7 @@ import {
 } from '../../util/types';
 import GlobalModel from '../../model/Global';
 import List from '../../data/List';
+import { BrushCommonSelectorsForSeries } from '../../component/brush/selector';
 
 type ScatterDataValue = OptionDataValue | OptionDataValue[]
 
@@ -84,7 +85,6 @@ class ScatterSeriesModel extends SeriesModel<ScatterSeriesOption> {
     type = ScatterSeriesModel.type
 
     static readonly dependencies = ['grid', 'polar', 'geo', 'singleAxis', 'calendar']
-    readonly brushSelector = 'point'
 
     getInitialData(option: ScatterSeriesOption, ecModel: GlobalModel): List {
         return createListFromArray(this.getSource(), this, {
@@ -111,6 +111,10 @@ class ScatterSeriesModel extends SeriesModel<ScatterSeriesOption> {
         return progressiveThreshold;
     }
 
+    brushSelector(dataIndex: number, data: List, selectors: BrushCommonSelectorsForSeries): boolean {
+        return selectors.point(data.getItemLayout(dataIndex));
+    }
+
     static defaultOption: ScatterSeriesOption = {
         coordinateSystem: 'cartesian2d',
         zlevel: 0,
diff --git a/src/component/axis/ParallelAxisView.ts b/src/component/axis/ParallelAxisView.ts
index 44a597b..9d6dbd3 100644
--- a/src/component/axis/ParallelAxisView.ts
+++ b/src/component/axis/ParallelAxisView.ts
@@ -17,38 +17,49 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import * as echarts from '../../echarts';
 import * as zrUtil from 'zrender/src/core/util';
 import AxisBuilder from './AxisBuilder';
-import BrushController from '../helper/BrushController';
+import BrushController, { BrushCoverConfig, BrushControllerEvents, BrushDimensionMinMax } from '../helper/BrushController';
 import * as brushHelper from '../helper/brushHelper';
 import * as graphic from '../../util/graphic';
+import ComponentView from '../../view/Component';
+import ExtensionAPI from '../../ExtensionAPI';
+import GlobalModel from '../../model/Global';
+import ParallelAxisModel, { ParallelAreaSelectStyleProps } from '../../coord/parallel/AxisModel';
+import { Payload } from '../../util/types';
+import ParallelModel from '../../coord/parallel/ParallelModel';
+import { ParallelAxisLayoutInfo } from '../../coord/parallel/Parallel';
+
 
 var elementList = ['axisLine', 'axisTickLabel', 'axisName'];
 
-var AxisView = echarts.extendComponentView({
+class ParallelAxisView extends ComponentView {
+
+    static type = 'parallelAxis';
+    readonly type = ParallelAxisView.type;
+
+    private _brushController: BrushController;
+    private _axisGroup: graphic.Group;
 
-    type: 'parallelAxis',
+    axisModel: ParallelAxisModel;
+    api: ExtensionAPI;
 
-    /**
-     * @override
-     */
-    init: function (ecModel, api) {
-        AxisView.superApply(this, 'init', arguments);
 
-        /**
-         * @type {module:echarts/component/helper/BrushController}
-         */
+    init(ecModel: GlobalModel, api: ExtensionAPI): void {
+        super.init.apply(this, arguments as any);
+
         (this._brushController = new BrushController(api.getZr()))
             .on('brush', zrUtil.bind(this._onBrush, this));
-    },
+    }
 
-    /**
-     * @override
-     */
-    render: function (axisModel, ecModel, api, payload) {
+    render(
+        axisModel: ParallelAxisModel,
+        ecModel: GlobalModel,
+        api: ExtensionAPI,
+        payload: Payload
+    ): void {
         if (fromAxisAreaSelect(axisModel, ecModel, payload)) {
             return;
         }
@@ -92,19 +103,24 @@ var AxisView = echarts.extendComponentView({
 
         var animationModel = (payload && payload.animation === false) ? null : axisModel;
         graphic.groupTransition(oldAxisGroup, this._axisGroup, animationModel);
-    },
+    }
 
     // /**
     //  * @override
     //  */
-    // updateVisual: function (axisModel, ecModel, api, payload) {
+    // updateVisual(axisModel, ecModel, api, payload) {
     //     this._brushController && this._brushController
     //         .updateCovers(getCoverInfoList(axisModel));
-    // },
-
-    _refreshBrushController: function (
-        builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api
-    ) {
+    // }
+
+    _refreshBrushController(
+        builderOpt: Pick<ParallelAxisLayoutInfo, 'position' | 'rotation'>,
+        areaSelectStyle: ParallelAreaSelectStyleProps,
+        axisModel: ParallelAxisModel,
+        coordSysModel: ParallelModel,
+        areaWidth: ParallelAreaSelectStyleProps['width'],
+        api: ExtensionAPI
+    ): void {
         // After filtering, axis may change, select area needs to be update.
         var extent = axisModel.axis.getExtent();
         var extentLen = extent[1] - extent[0];
@@ -139,40 +155,42 @@ var AxisView = echarts.extendComponentView({
                 removeOnClick: true
             })
             .updateCovers(getCoverInfoList(axisModel));
-    },
+    }
 
-    _onBrush: function (coverInfoList, opt) {
+    _onBrush(eventParam: BrushControllerEvents['brush']): void {
+        var coverInfoList = eventParam.areas;
         // Do not cache these object, because the mey be changed.
         var axisModel = this.axisModel;
         var axis = axisModel.axis;
         var intervals = zrUtil.map(coverInfoList, function (coverInfo) {
             return [
-                axis.coordToData(coverInfo.range[0], true),
-                axis.coordToData(coverInfo.range[1], true)
+                axis.coordToData((coverInfo.range as BrushDimensionMinMax)[0], true),
+                axis.coordToData((coverInfo.range as BrushDimensionMinMax)[1], true)
             ];
         });
 
         // If realtime is true, action is not dispatched on drag end, because
         // the drag end emits the same params with the last drag move event,
         // and may have some delay when using touch pad.
-        if (!axisModel.option.realtime === opt.isEnd || opt.removeOnClick) { // jshint ignore:line
+        if (!axisModel.option.realtime === eventParam.isEnd || eventParam.removeOnClick) { // jshint ignore:line
             this.api.dispatchAction({
                 type: 'axisAreaSelect',
                 parallelAxisId: axisModel.id,
                 intervals: intervals
             });
         }
-    },
+    }
 
-    /**
-     * @override
-     */
-    dispose: function () {
+    dispose(): void {
         this._brushController.dispose();
     }
-});
+}
+
+ComponentView.registerClass(ParallelAxisView);
 
-function fromAxisAreaSelect(axisModel, ecModel, payload) {
+function fromAxisAreaSelect(
+    axisModel: ParallelAxisModel, ecModel: GlobalModel, payload: Payload
+): boolean {
     return payload
         && payload.type === 'axisAreaSelect'
         && ecModel.findComponents(
@@ -180,7 +198,7 @@ function fromAxisAreaSelect(axisModel, ecModel, payload) {
         )[0] === axisModel;
 }
 
-function getCoverInfoList(axisModel) {
+function getCoverInfoList(axisModel: ParallelAxisModel): BrushCoverConfig[] {
     var axis = axisModel.axis;
     return zrUtil.map(axisModel.activeIntervals, function (interval) {
         return {
@@ -194,10 +212,10 @@ function getCoverInfoList(axisModel) {
     });
 }
 
-function getCoordSysModel(axisModel, ecModel) {
+function getCoordSysModel(axisModel: ParallelAxisModel, ecModel: GlobalModel): ParallelModel {
     return ecModel.getComponent(
         'parallel', axisModel.get('parallelIndex')
-    );
+    ) as ParallelModel;
 }
 
-export default AxisView;
\ No newline at end of file
+export default ParallelAxisView;
diff --git a/src/component/axis/parallelAxisAction.ts b/src/component/axis/parallelAxisAction.ts
index 45c7b55..9a4927d 100644
--- a/src/component/axis/parallelAxisAction.ts
+++ b/src/component/axis/parallelAxisAction.ts
@@ -17,37 +17,46 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import * as echarts from '../../echarts';
+import { Payload } from '../../util/types';
+import ParallelAxisModel, { ParallelAxisInterval } from '../../coord/parallel/AxisModel';
+import GlobalModel from '../../model/Global';
+import ParallelModel from '../../coord/parallel/ParallelModel';
+
+interface ParallelAxisAreaSelectPayload extends Payload {
+    parallelAxisId: string;
+    intervals: ParallelAxisInterval[]
+}
 
-/**
- * @payload
- * @property {string} parallelAxisId
- * @property {Array.<Array.<number>>} intervals
- */
 var actionInfo = {
     type: 'axisAreaSelect',
     event: 'axisAreaSelected'
     // update: 'updateVisual'
 };
 
-echarts.registerAction(actionInfo, function (payload, ecModel) {
+echarts.registerAction(actionInfo, function (payload: ParallelAxisAreaSelectPayload, ecModel: GlobalModel) {
     ecModel.eachComponent(
         {mainType: 'parallelAxis', query: payload},
-        function (parallelAxisModel) {
+        function (parallelAxisModel: ParallelAxisModel) {
             parallelAxisModel.axis.model.setActiveIntervals(payload.intervals);
         }
     );
 });
 
+export interface ParallelAxisExpandPayload extends Payload {
+    axisExpandWindow?: number[];
+    // Jumping uses animation, and sliding suppresses animation.
+    animation?: boolean;
+}
+
 /**
  * @payload
  */
-echarts.registerAction('parallelAxisExpand', function (payload, ecModel) {
+echarts.registerAction('parallelAxisExpand', function (payload: ParallelAxisExpandPayload, ecModel) {
     ecModel.eachComponent(
         {mainType: 'parallel', query: payload},
-        function (parallelModel) {
+        function (parallelModel: ParallelModel) {
             parallelModel.setAxisExpand(payload);
         }
     );
diff --git a/src/component/axisPointer/BaseAxisPointer.ts b/src/component/axisPointer/BaseAxisPointer.ts
index e9582cd..c57df43 100644
--- a/src/component/axisPointer/BaseAxisPointer.ts
+++ b/src/component/axisPointer/BaseAxisPointer.ts
@@ -393,10 +393,7 @@ class BaseAxisPointer implements AxisPointer {
         this._moveHandleToValue(value, isInit);
     }
 
-    /**
-     * @private
-     */
-    _moveHandleToValue(value: AxisValue, isInit?: boolean) {
+    private _moveHandleToValue(value: AxisValue, isInit?: boolean) {
         updateProps(
             this._axisPointerModel,
             !isInit && this._moveAnimation,
@@ -407,10 +404,7 @@ class BaseAxisPointer implements AxisPointer {
         );
     }
 
-    /**
-     * @private
-     */
-    _onHandleDragMove(dx: number, dy: number) {
+    private _onHandleDragMove(dx: number, dy: number) {
         var handle = this._handle;
         if (!handle) {
             return;
@@ -436,7 +430,6 @@ class BaseAxisPointer implements AxisPointer {
 
     /**
      * Throttled method.
-     * @private
      */
     _doDispatchAxisPointer() {
         var handle = this._handle;
@@ -458,10 +451,7 @@ class BaseAxisPointer implements AxisPointer {
         });
     }
 
-    /**
-     * @private
-     */
-    _onHandleDragEnd() {
+    private _onHandleDragEnd() {
         this._dragging = false;
         var handle = this._handle;
         if (!handle) {
diff --git a/src/component/brush.ts b/src/component/brush.ts
index 29e8e91..a39dc85 100644
--- a/src/component/brush.ts
+++ b/src/component/brush.ts
@@ -30,4 +30,4 @@ import './brush/BrushView';
 import './brush/brushAction';
 import './toolbox/feature/Brush';
 
-echarts.registerPreprocessor(preprocessor);
\ No newline at end of file
+echarts.registerPreprocessor(preprocessor);
diff --git a/src/component/brush/BrushModel.ts b/src/component/brush/BrushModel.ts
index 5241fc9..935499f 100644
--- a/src/component/brush/BrushModel.ts
+++ b/src/component/brush/BrushModel.ts
@@ -17,86 +17,150 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import {__DEV__} from '../../config';
-import * as echarts from '../../echarts';
 import * as zrUtil from 'zrender/src/core/util';
 import * as visualSolution from '../../visual/visualSolution';
 import Model from '../../model/Model';
+import { ComponentOption, ZRColor, VisualOptionFixed } from '../../util/types';
+import ComponentModel from '../../model/Component';
+import BrushTargetManager from '../helper/BrushTargetManager';
+import { BrushCoverCreatorConfig, BrushMode, BrushCoverConfig, BrushDimensionMinMax, BrushAreaRange, BrushTypeUncertain, BrushType } from '../helper/BrushController';
+import { ModelFinderObject } from '../../util/model';
+
+
+var DEFAULT_OUT_OF_BRUSH_COLOR = '#ddd';
+
+/**
+ * The input to define brush areas.
+ * (1) Can be created by user when calling dispatchAction.
+ * (2) Can be created by `BrushController`
+ * for brush behavior. area params are picked from `cover.__brushOptoin`.
+ * In `BrushController`, "covers" are create or updated for each "area".
+ */
+export interface BrushAreaParam extends ModelFinderObject {
+    brushType: BrushCoverConfig['brushType'];
+    id?: BrushCoverConfig['id'];
+    range?: BrushCoverConfig['range'];
+
+    // `ModelFinderObject` and `panelId` are used to match "coord sys target"
+    // for this area. See `BrushTargetManager['setInputRanges']`.
+    // If panelId specified, use it to match panel firstly.
+    // If not specified, use `ModelFinderObject` to match panel,
+    // and then assign the panelId to the area.
+    // If finally no panel matched, panelId keep null/undefined,
+    // means global area.
+    // PENDING: this feature should better belong to BrushController
+    // rather than brush component?
+    panelId?: BrushCoverConfig['panelId'];
+
+    // Range in local coordinates of certain coordinate system.
+    // When dispatchAction, if the area is the global area,
+    // `range` is the input. if the area is not the global area,
+    // `coordRange` is the input, and then convert to `range`.
+    coordRange?: BrushAreaRange;
+    // coord ranges, used in multiple cartesian in one grid.
+    // Only for output to users.
+    coordRanges?: BrushAreaRange[];
+}
 
-var DEFAULT_OUT_OF_BRUSH_COLOR = ['#ddd'];
+/**
+ * Generated by `brushModel.setAreas`, which merges
+ * `area: BrushAreaParam` and `brushModel.option: BrushOption`.
+ * See `generateBrushOption`.
+ */
+export interface BrushAreaParamInternal extends BrushAreaParam {
+    brushMode: BrushMode;
+    brushStyle: BrushCoverConfig['brushStyle'];
+    transformable: BrushCoverConfig['transformable'];
+    removeOnClick: BrushCoverConfig['removeOnClick'];
+    z: BrushCoverConfig['z'];
+
+    __rangeOffset?: {
+        offset: BrushDimensionMinMax | BrushDimensionMinMax[];
+        xyMinMax: BrushDimensionMinMax[]
+    };
+}
 
-var BrushModel = echarts.extendComponentModel({
+export type BrushToolboxIconType = BrushType | 'keep' | 'clear';
+
+export interface BrushOption extends ComponentOption, ModelFinderObject {
+    // Default value see preprocessor.
+    toolbox?: BrushToolboxIconType[];
+
+    // Series indices array, broadcast using dataIndex.
+    // or 'all', which means all series. 'none'/null/undefined means no series.
+    brushLink?: number[] | 'all' | 'none';
+
+    // Throttle in brushSelected event. 'fixRate' or 'debounce'.
+    // If null, no throttle. Valid only in the first brush component
+    throttleType?: 'fixRate' | 'debounce';
+    // Unit: ms, 0 means every event will be triggered.
+    throttleDelay?: number;
+
+    inBrush?: VisualOptionFixed;
+    outOfBrush?: VisualOptionFixed;
+
+    // --- Current painting brush options ---
+    // Default type of brush
+    brushType: BrushTypeUncertain;
+    brushStyle: {
+        borderWidth: number;
+        color: ZRColor;
+        borderColor: ZRColor;
+    };
+    transformable: boolean;
+    brushMode: BrushMode;
+    removeOnClick: boolean;
+}
 
-    type: 'brush',
+class BrushModel extends ComponentModel<BrushOption> {
 
-    dependencies: ['geo', 'grid', 'xAxis', 'yAxis', 'parallel', 'series'],
+    static type = 'brush' as const;
+    type = BrushModel.type;
 
-    /**
-     * @protected
-     */
-    defaultOption: {
-        // inBrush: null,
-        // outOfBrush: null,
-        toolbox: null,          // Default value see preprocessor.
-        brushLink: null,        // Series indices array, broadcast using dataIndex.
-                                // or 'all', which means all series. 'none' or null means no series.
-        seriesIndex: 'all',     // seriesIndex array, specify series controlled by this brush component.
-        geoIndex: null,         //
-        xAxisIndex: null,
-        yAxisIndex: null,
-
-        brushType: 'rect',      // Default brushType, see BrushController.
-        brushMode: 'single',    // Default brushMode, 'single' or 'multiple'
-        transformable: true,    // Default transformable.
-        brushStyle: {           // Default brushStyle
+    static dependencies = ['geo', 'grid', 'xAxis', 'yAxis', 'parallel', 'series'];
+
+    static defaultOption: BrushOption = {
+        seriesIndex: 'all',
+        brushType: 'rect',
+        brushMode: 'single',
+        transformable: true,
+        brushStyle: {
             borderWidth: 1,
             color: 'rgba(120,140,180,0.3)',
             borderColor: 'rgba(120,140,180,0.8)'
         },
-
-        throttleType: 'fixRate', // Throttle in brushSelected event. 'fixRate' or 'debounce'.
-                                 // If null, no throttle. Valid only in the first brush component
-        throttleDelay: 0,        // Unit: ms, 0 means every event will be triggered.
-
-        // FIXME
-        // 试验效果
+        throttleType: 'fixRate',
+        throttleDelay: 0,
         removeOnClick: true,
-
         z: 10000
-    },
+    };
 
     /**
      * @readOnly
-     * @type {Array.<Object>}
      */
-    areas: [],
+    areas: BrushAreaParamInternal[] = [];
 
     /**
      * Current activated brush type.
      * If null, brush is inactived.
      * see module:echarts/component/helper/BrushController
      * @readOnly
-     * @type {string}
      */
-    brushType: null,
+    brushType: BrushTypeUncertain;
 
     /**
-     * Current brush opt.
-     * see module:echarts/component/helper/BrushController
+     * Current brush painting area settings.
      * @readOnly
-     * @type {Object}
      */
-    brushOption: {},
+    brushOption: BrushCoverCreatorConfig = {} as BrushCoverCreatorConfig;
 
-    /**
-     * @readOnly
-     * @type {Array.<Object>}
-     */
-    coordInfoList: [],
+    // Inject
+    brushTargetManager: BrushTargetManager;
 
-    optionUpdated: function (newOption, isInit) {
+
+    optionUpdated(newOption: BrushOption, isInit: boolean): void {
         var thisOption = this.option;
 
         !isInit && visualSolution.replaceVisualOption(
@@ -112,14 +176,12 @@ var BrushModel = echarts.extendComponentModel({
             // be effected by the highlight z when brush.
             inBrush.liftZ = 5;
         }
-    },
+    }
 
     /**
-     * If ranges is null/undefined, range state remain.
-     *
-     * @param {Array.<Object>} [ranges]
+     * If `areas` is null/undefined, range state remain.
      */
-    setAreas: function (areas) {
+    setAreas(areas?: BrushAreaParam[]): void {
         if (__DEV__) {
             zrUtil.assert(zrUtil.isArray(areas));
             zrUtil.each(areas, function (area) {
@@ -127,7 +189,7 @@ var BrushModel = echarts.extendComponentModel({
             });
         }
 
-        // If ranges is null/undefined, range state remain.
+        // If areas is null/undefined, range state remain.
         // This helps user to dispatchAction({type: 'brush'}) with no areas
         // set but just want to get the current brush select info from a `brush` event.
         if (!areas) {
@@ -137,20 +199,30 @@ var BrushModel = echarts.extendComponentModel({
         this.areas = zrUtil.map(areas, function (area) {
             return generateBrushOption(this.option, area);
         }, this);
-    },
+    }
 
     /**
-     * see module:echarts/component/helper/BrushController
-     * @param {Object} brushOption
+     * Set the current painting brush option.
      */
-    setBrushOption: function (brushOption) {
+    setBrushOption(brushOption: BrushCoverCreatorConfig): void {
         this.brushOption = generateBrushOption(this.option, brushOption);
         this.brushType = this.brushOption.brushType;
     }
 
-});
+}
+
+ComponentModel.registerClass(BrushModel);
+
 
-function generateBrushOption(option, brushOption) {
+function generateBrushOption(
+    option: BrushOption, brushOption: BrushAreaParam
+): BrushAreaParamInternal;
+function generateBrushOption(
+    option: BrushOption, brushOption: BrushCoverCreatorConfig
+): BrushCoverCreatorConfig;
+function generateBrushOption(
+    option: BrushOption, brushOption: BrushAreaParam | BrushCoverCreatorConfig
+): BrushAreaParamInternal | BrushCoverCreatorConfig {
     return zrUtil.merge(
         {
             brushType: option.brushType,
diff --git a/src/component/brush/BrushView.ts b/src/component/brush/BrushView.ts
index 5b55c0f..da1ef48 100644
--- a/src/component/brush/BrushView.ts
+++ b/src/component/brush/BrushView.ts
@@ -17,106 +17,86 @@
 * under the License.
 */
 
-// @ts-nocheck
 
-import * as echarts from '../../echarts';
 import * as zrUtil from 'zrender/src/core/util';
-import BrushController from '../helper/BrushController';
+import BrushController, { BrushControllerEvents, BrushCoverConfig } from '../helper/BrushController';
 import {layoutCovers} from './visualEncoding';
+import BrushModel from './BrushModel';
+import GlobalModel from '../../model/Global';
+import ExtensionAPI from '../../ExtensionAPI';
+import { Payload } from '../../util/types';
+import ComponentView from '../../view/Component';
 
-export default echarts.extendComponentView({
 
-    type: 'brush',
+class BrushView extends ComponentView {
 
-    init: function (ecModel, api) {
+    static type = 'brush';
+    readonly type = BrushView.type;
 
-        /**
-         * @readOnly
-         * @type {module:echarts/model/Global}
-         */
-        this.ecModel = ecModel;
+    ecModel: GlobalModel;
+    api: ExtensionAPI;
+    model: BrushModel;
+    private _brushController: BrushController;
 
-        /**
-         * @readOnly
-         * @type {module:echarts/ExtensionAPI}
-         */
+    init(ecModel: GlobalModel, api: ExtensionAPI): void {
+        this.ecModel = ecModel;
         this.api = api;
-
-        /**
-         * @readOnly
-         * @type {module:echarts/component/brush/BrushModel}
-         */
         this.model;
 
-        /**
-         * @private
-         * @type {module:echarts/component/helper/BrushController}
-         */
         (this._brushController = new BrushController(api.getZr()))
             .on('brush', zrUtil.bind(this._onBrush, this))
             .mount();
-    },
+    }
 
-    /**
-     * @override
-     */
-    render: function (brushModel) {
+    render(brushModel: BrushModel, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload): void {
         this.model = brushModel;
-        return updateController.apply(this, arguments);
-    },
+        this._updateController(brushModel, ecModel, api, payload);
+    }
 
-    /**
-     * @override
-     */
-    updateTransform: function (brushModel, ecModel) {
+    updateTransform(brushModel: BrushModel, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload) {
         // PENDING: `updateTransform` is a little tricky, whose layout need
         // to be calculate mandatorily and other stages will not be performed.
         // Take care the correctness of the logic. See #11754 .
         layoutCovers(ecModel);
-        return updateController.apply(this, arguments);
-    },
+        this._updateController(brushModel, ecModel, api, payload);
+    }
 
-    /**
-     * @override
-     */
-    updateView: updateController,
+    updateView(brushModel: BrushModel, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload) {
+        this._updateController(brushModel, ecModel, api, payload);
+    }
+
+    private _updateController(brushModel: BrushModel, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload) {
+        // Do not update controller when drawing.
+        (!payload || payload.$from !== brushModel.id) && this._brushController
+            .setPanels(brushModel.brushTargetManager.makePanelOpts(api))
+            .enableBrush(brushModel.brushOption)
+            .updateCovers(brushModel.areas.slice() as BrushCoverConfig[]);
+    }
 
-    // /**
-    //  * @override
-    //  */
     // updateLayout: updateController,
 
-    // /**
-    //  * @override
-    //  */
     // updateVisual: updateController,
 
-    /**
-     * @override
-     */
-    dispose: function () {
+    dispose() {
         this._brushController.dispose();
-    },
+    }
 
-    /**
-     * @private
-     */
-    _onBrush: function (areas, opt) {
+    private _onBrush(eventParam: BrushControllerEvents['brush']): void {
         var modelId = this.model.id;
 
-        this.model.brushTargetManager.setOutputRanges(areas, this.ecModel);
+        var areas = this.model.brushTargetManager.setOutputRanges(eventParam.areas, this.ecModel);
 
         // Action is not dispatched on drag end, because the drag end
         // emits the same params with the last drag move event, and
         // may have some delay when using touch pad, which makes
         // animation not smooth (when using debounce).
-        (!opt.isEnd || opt.removeOnClick) && this.api.dispatchAction({
+        (!eventParam.isEnd || eventParam.removeOnClick) && this.api.dispatchAction({
             type: 'brush',
             brushId: modelId,
             areas: zrUtil.clone(areas),
             $from: modelId
         });
-        opt.isEnd && this.api.dispatchAction({
+        eventParam.isEnd && this.api.dispatchAction({
             type: 'brushEnd',
             brushId: modelId,
             areas: zrUtil.clone(areas),
@@ -124,12 +104,6 @@ export default echarts.extendComponentView({
         });
     }
 
-});
-
-function updateController(brushModel, ecModel, api, payload) {
-    // Do not update controller when drawing.
-    (!payload || payload.$from !== brushModel.id) && this._brushController
-        .setPanels(brushModel.brushTargetManager.makePanelOpts(api))
-        .enableBrush(brushModel.brushOption)
-        .updateCovers(brushModel.areas.slice());
 }
+
+ComponentView.registerClass(BrushView);
diff --git a/src/component/brush/brushAction.ts b/src/component/brush/brushAction.ts
index 2e86a50..bb407f0 100644
--- a/src/component/brush/brushAction.ts
+++ b/src/component/brush/brushAction.ts
@@ -17,24 +17,25 @@
 * under the License.
 */
 
-// @ts-nocheck
-
 import * as echarts from '../../echarts';
+import GlobalModel from '../../model/Global';
+import { Payload } from '../../util/types';
+import BrushModel, { BrushAreaParam } from './BrushModel';
+
+interface BrushPayload extends Payload {
+    // If "areas" is empty, all of the select-boxes will be deleted
+    areas?: BrushAreaParam[];
+}
 
-/**
- * payload: {
- *      brushIndex: number, or,
- *      brushId: string, or,
- *      brushName: string,
- *      globalRanges: Array
- * }
- */
 echarts.registerAction(
-        {type: 'brush', event: 'brush' /*, update: 'updateView' */},
-    function (payload, ecModel) {
-        ecModel.eachComponent({mainType: 'brush', query: payload}, function (brushModel) {
-            brushModel.setAreas(payload.areas);
-        });
+    {type: 'brush', event: 'brush' /*, update: 'updateView' */},
+    function (payload: BrushPayload, ecModel: GlobalModel) {
+        ecModel.eachComponent(
+            {mainType: 'brush', query: payload},
+            function (brushModel: BrushModel) {
+                brushModel.setAreas(payload.areas);
+            }
+        );
     }
 );
 
diff --git a/src/component/brush/preprocessor.ts b/src/component/brush/preprocessor.ts
index 3aac2f9..ce6735e 100644
--- a/src/component/brush/preprocessor.ts
+++ b/src/component/brush/preprocessor.ts
@@ -17,13 +17,14 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import * as zrUtil from 'zrender/src/core/util';
+import { ECUnitOption, Dictionary } from '../../util/types';
+import { BrushOption, BrushToolboxIconType } from './BrushModel';
 
-var DEFAULT_TOOLBOX_BTNS = ['rect', 'polygon', 'keep', 'clear'];
+var DEFAULT_TOOLBOX_BTNS: BrushToolboxIconType[] = ['rect', 'polygon', 'keep', 'clear'];
 
-export default function (option, isNew) {
+export default function (option: ECUnitOption, isNew: boolean): void {
     var brushComponents = option && option.brush;
     if (!zrUtil.isArray(brushComponents)) {
         brushComponents = brushComponents ? [brushComponents] : [];
@@ -33,9 +34,9 @@ export default function (option, isNew) {
         return;
     }
 
-    var brushComponentSpecifiedBtns = [];
+    var brushComponentSpecifiedBtns = [] as string[];
 
-    zrUtil.each(brushComponents, function (brushOpt) {
+    zrUtil.each(brushComponents, function (brushOpt: BrushOption) {
         var tbs = brushOpt.hasOwnProperty('toolbox')
             ? brushOpt.toolbox : [];
 
@@ -67,8 +68,8 @@ export default function (option, isNew) {
     }
 }
 
-function removeDuplicate(arr) {
-    var map = {};
+function removeDuplicate(arr: string[]): void {
+    var map = {} as Dictionary<number>;
     zrUtil.each(arr, function (val) {
         map[val] = 1;
     });
diff --git a/src/component/brush/selector.ts b/src/component/brush/selector.ts
index 56f54e4..9bb45de 100644
--- a/src/component/brush/selector.ts
+++ b/src/component/brush/selector.ts
@@ -17,22 +17,74 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import * as polygonContain from 'zrender/src/contain/polygon';
-import BoundingRect from 'zrender/src/core/BoundingRect';
+import BoundingRect, { RectLike } from 'zrender/src/core/BoundingRect';
 import {linePolygonIntersect} from '../../util/graphic';
+import { BrushType, BrushDimensionMinMax } from '../helper/BrushController';
+import { BrushAreaParamInternal } from './BrushModel';
 
-// Key of the first level is brushType: `line`, `rect`, `polygon`.
-// Key of the second level is chart element type: `point`, `rect`.
-// See moudule:echarts/component/helper/BrushController
-// function param:
-//      {Object} itemLayout fetch from data.getItemLayout(dataIndex)
-//      {Object} selectors {point: selector, rect: selector, ...}
-//      {Object} area {range: [[], [], ..], boudingRect}
-// function return:
-//      {boolean} Whether in the given brush.
-var selector = {
+
+export interface BrushSelectableArea extends BrushAreaParamInternal {
+    boundingRect: BoundingRect;
+    selectors: BrushCommonSelectorsForSeries
+}
+
+/**
+ * Key of the first level is brushType: `line`, `rect`, `polygon`.
+ * See moudule:echarts/component/helper/BrushController
+ * function param:
+ *      {Object} itemLayout fetch from data.getItemLayout(dataIndex)
+ *      {Object} selectors {point: selector, rect: selector, ...}
+ *      {Object} area {range: [[], [], ..], boudingRect}
+ * function return:
+ *      {boolean} Whether in the given brush.
+ */
+interface BrushSelectorOnBrushType {
+    // For chart element type "point"
+    point(
+        // fetch from data.getItemLayout(dataIndex)
+        itemLayout: number[],
+        selectors: BrushCommonSelectorsForSeries,
+        area: BrushSelectableArea
+    ): boolean;
+    // For chart element type "rect"
+    rect(
+        // fetch from data.getItemLayout(dataIndex)
+        itemLayout: RectLike,
+        selectors: BrushCommonSelectorsForSeries,
+        area: BrushSelectableArea
+    ): boolean;
+}
+
+/**
+ * This methods are corresponding to `BrushSelectorOnBrushType`,
+ * but `area: BrushSelectableArea` is binded to each method.
+ */
+export interface BrushCommonSelectorsForSeries {
+    // For chart element type "point"
+    point(itemLayout: number[]): boolean;
+    // For chart element type "rect"
+    rect(itemLayout: RectLike): boolean;
+}
+
+export function makeBrushCommonSelectorForSeries(
+    area: BrushSelectableArea
+): BrushCommonSelectorsForSeries {
+    var brushType = area.brushType;
+    // Do not use function binding or curry for performance.
+    var selectors: BrushCommonSelectorsForSeries = {
+        point(itemLayout: number[]) {
+            return selector[brushType].point(itemLayout, selectors, area);
+        },
+        rect(itemLayout: RectLike) {
+            return selector[brushType].rect(itemLayout, selectors, area);
+        }
+    };
+    return selectors;
+}
+
+var selector: Record<BrushType, BrushSelectorOnBrushType> = {
     lineX: getLineSelectors(0),
     lineY: getLineSelectors(1),
     rect: {
@@ -46,11 +98,15 @@ var selector = {
     polygon: {
         point: function (itemLayout, selectors, area) {
             return itemLayout
-                && area.boundingRect.contain(itemLayout[0], itemLayout[1])
-                && polygonContain.contain(area.range, itemLayout[0], itemLayout[1]);
+                && area.boundingRect.contain(
+                    itemLayout[0], itemLayout[1]
+                )
+                && polygonContain.contain(
+                    area.range as BrushDimensionMinMax[], itemLayout[0], itemLayout[1]
+                );
         },
         rect: function (itemLayout, selectors, area) {
-            var points = area.range;
+            var points = area.range as BrushDimensionMinMax[];
 
             if (!itemLayout || points.length <= 1) {
                 return false;
@@ -78,21 +134,21 @@ var selector = {
     }
 };
 
-function getLineSelectors(xyIndex) {
-    var xy = ['x', 'y'];
-    var wh = ['width', 'height'];
+function getLineSelectors(xyIndex: 0 | 1): BrushSelectorOnBrushType {
+    var xy = ['x', 'y'] as const;
+    var wh = ['width', 'height'] as const;
 
     return {
         point: function (itemLayout, selectors, area) {
             if (itemLayout) {
-                var range = area.range;
+                var range = area.range as BrushDimensionMinMax;
                 var p = itemLayout[xyIndex];
                 return inLineRange(p, range);
             }
         },
         rect: function (itemLayout, selectors, area) {
             if (itemLayout) {
-                var range = area.range;
+                var range = area.range as BrushDimensionMinMax;
                 var layoutRange = [
                     itemLayout[xy[xyIndex]],
                     itemLayout[xy[xyIndex]] + itemLayout[wh[xyIndex]]
@@ -107,7 +163,7 @@ function getLineSelectors(xyIndex) {
     };
 }
 
-function inLineRange(p, range) {
+function inLineRange(p: number, range: BrushDimensionMinMax): boolean {
     return range[0] <= p && p <= range[1];
 }
 
diff --git a/src/component/brush/visualEncoding.ts b/src/component/brush/visualEncoding.ts
index d955948..a1aec76 100644
--- a/src/component/brush/visualEncoding.ts
+++ b/src/component/brush/visualEncoding.ts
@@ -17,26 +17,53 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import * as echarts from '../../echarts';
 import * as zrUtil from 'zrender/src/core/util';
 import BoundingRect from 'zrender/src/core/BoundingRect';
 import * as visualSolution from '../../visual/visualSolution';
-import selector from './selector';
+import selector, { BrushSelectableArea, makeBrushCommonSelectorForSeries } from './selector';
 import * as throttleUtil from '../../util/throttle';
 import BrushTargetManager from '../helper/BrushTargetManager';
-
-var STATE_LIST = ['inBrush', 'outOfBrush'];
-var DISPATCH_METHOD = '__ecBrushSelect';
-var DISPATCH_FLAG = '__ecInBrushSelectEvent';
+import GlobalModel from '../../model/Global';
+import ExtensionAPI from '../../ExtensionAPI';
+import { Payload } from '../../util/types';
+import BrushModel, { BrushAreaParamInternal } from './BrushModel';
+import SeriesModel from '../../model/Series';
+import ParallelSeries from '../../chart/parallel/ParallelSeries';
+import { ZRenderType } from 'zrender/src/zrender';
+import { BrushType, BrushDimensionMinMax } from '../helper/BrushController';
+
+type BrushVisualState = 'inBrush' | 'outOfBrush';
+
+var STATE_LIST = ['inBrush', 'outOfBrush'] as const;
+var DISPATCH_METHOD = '__ecBrushSelect' as const;
+var DISPATCH_FLAG = '__ecInBrushSelectEvent' as const;
 var PRIORITY_BRUSH = echarts.PRIORITY.VISUAL.BRUSH;
 
+interface BrushGlobalDispatcher extends ZRenderType  {
+    [DISPATCH_FLAG]: boolean;
+    [DISPATCH_METHOD]: typeof doDispatch
+}
+
+interface BrushSelectedItem {
+    brushId: string;
+    brushIndex: number;
+    brushName: string;
+    areas: BrushAreaParamInternal[];
+    selected: {
+        seriesId: string;
+        seriesIndex: number;
+        seriesName: string;
+        dataIndex: number[];
+    }[]
+};
+
 /**
  * Layout for visual, the priority higher than other layout, and before brush visual.
  */
-echarts.registerLayout(PRIORITY_BRUSH, function (ecModel, api, payload) {
-    ecModel.eachComponent({mainType: 'brush'}, function (brushModel) {
+echarts.registerLayout(PRIORITY_BRUSH, function (ecModel: GlobalModel, api: ExtensionAPI, payload: Payload) {
+    ecModel.eachComponent({mainType: 'brush'}, function (brushModel: BrushModel) {
         payload && payload.type === 'takeGlobalCursor' && brushModel.setBrushOption(
             payload.key === 'brush' ? payload.brushOption : {brushType: false}
         );
@@ -44,8 +71,8 @@ echarts.registerLayout(PRIORITY_BRUSH, function (ecModel, api, payload) {
     layoutCovers(ecModel);
 });
 
-export function layoutCovers(ecModel) {
-    ecModel.eachComponent({mainType: 'brush'}, function (brushModel) {
+export function layoutCovers(ecModel: GlobalModel): void {
+    ecModel.eachComponent({mainType: 'brush'}, function (brushModel: BrushModel) {
         var brushTargetManager = brushModel.brushTargetManager = new BrushTargetManager(brushModel.option, ecModel);
         brushTargetManager.setInputRanges(brushModel.areas, ecModel);
     });
@@ -54,15 +81,15 @@ export function layoutCovers(ecModel) {
 /**
  * Register the visual encoding if this modules required.
  */
-echarts.registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) {
+echarts.registerVisual(PRIORITY_BRUSH, function (ecModel: GlobalModel, api: ExtensionAPI, payload: Payload) {
 
-    var brushSelected = [];
+    var brushSelected: BrushSelectedItem[] = [];
     var throttleType;
     var throttleDelay;
 
-    ecModel.eachComponent({mainType: 'brush'}, function (brushModel, brushIndex) {
+    ecModel.eachComponent({mainType: 'brush'}, function (brushModel: BrushModel, brushIndex) {
 
-        var thisBrushSelected = {
+        var thisBrushSelected: BrushSelectedItem = {
             brushId: brushModel.id,
             brushIndex: brushIndex,
             brushName: brushModel.name,
@@ -75,10 +102,10 @@ echarts.registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) {
 
         var brushOption = brushModel.option;
         var brushLink = brushOption.brushLink;
-        var linkedSeriesMap = [];
-        var selectedDataIndexForLink = [];
-        var rangeInfoBySeries = [];
-        var hasBrushExists = 0;
+        var linkedSeriesMap: {[seriesIndex: number]: 0 | 1} = [];
+        var selectedDataIndexForLink: {[dataIndex: number]: 0 | 1} = [];
+        var rangeInfoBySeries: {[seriesIndex: number]: BrushSelectableArea[]} = [];
+        var hasBrushExists = false;
 
         if (!brushIndex) { // Only the first throttle setting works.
             throttleType = brushOption.throttleType;
@@ -86,13 +113,14 @@ echarts.registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) {
         }
 
         // Add boundingRect and selectors to range.
-        var areas = zrUtil.map(brushModel.areas, function (area) {
-            return bindSelector(
-                zrUtil.defaults(
-                    {boundingRect: boundingRectBuilders[area.brushType](area)},
-                    area
-                )
-            );
+        var areas: BrushSelectableArea[] = zrUtil.map(brushModel.areas, function (area) {
+            var builder = boundingRectBuilders[area.brushType];
+            var selectableArea = zrUtil.defaults(
+                {boundingRect: builder ? builder(area) : void 0},
+                area
+            ) as BrushSelectableArea;
+            selectableArea.selectors = makeBrushCommonSelectorForSeries(selectableArea);
+            return selectableArea;
         });
 
         var visualMappings = visualSolution.createVisualMappings(
@@ -105,13 +133,13 @@ echarts.registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) {
             linkedSeriesMap[seriesIndex] = 1;
         });
 
-        function linkOthers(seriesIndex) {
-            return brushLink === 'all' || linkedSeriesMap[seriesIndex];
+        function linkOthers(seriesIndex: number): boolean {
+            return brushLink === 'all' || !!linkedSeriesMap[seriesIndex];
         }
 
         // If no supported brush or no brush on the series,
         // all visuals should be in original state.
-        function brushed(rangeInfoList) {
+        function brushed(rangeInfoList: BrushSelectableArea[]): boolean {
             return !!rangeInfoList.length;
         }
 
@@ -130,16 +158,16 @@ echarts.registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) {
 
         // Step A
         ecModel.eachSeries(function (seriesModel, seriesIndex) {
-            var rangeInfoList = rangeInfoBySeries[seriesIndex] = [];
+            var rangeInfoList: BrushSelectableArea[] = rangeInfoBySeries[seriesIndex] = [];
 
             seriesModel.subType === 'parallel'
-                ? stepAParallel(seriesModel, seriesIndex, rangeInfoList)
+                ? stepAParallel(seriesModel as ParallelSeries, seriesIndex)
                 : stepAOthers(seriesModel, seriesIndex, rangeInfoList);
         });
 
-        function stepAParallel(seriesModel, seriesIndex) {
+        function stepAParallel(seriesModel: ParallelSeries, seriesIndex: number): void {
             var coordSys = seriesModel.coordinateSystem;
-            hasBrushExists |= coordSys.hasAxisBrushed();
+            hasBrushExists = hasBrushExists || coordSys.hasAxisBrushed();
 
             linkOthers(seriesIndex) && coordSys.eachActiveState(
                 seriesModel.getData(),
@@ -149,23 +177,24 @@ echarts.registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) {
             );
         }
 
-        function stepAOthers(seriesModel, seriesIndex, rangeInfoList) {
-            var selectorsByBrushType = getSelectorsByBrushType(seriesModel);
-            if (!selectorsByBrushType || brushModelNotControll(brushModel, seriesIndex)) {
+        function stepAOthers(
+            seriesModel: SeriesModel, seriesIndex: number, rangeInfoList: BrushSelectableArea[]
+        ): void {
+            if (!seriesModel.brushSelector || brushModelNotControll(brushModel, seriesIndex)) {
                 return;
             }
 
             zrUtil.each(areas, function (area) {
-                selectorsByBrushType[area.brushType]
-                    && brushModel.brushTargetManager.controlSeries(area, seriesModel, ecModel)
-                    && rangeInfoList.push(area);
-                hasBrushExists |= brushed(rangeInfoList);
+                if (brushModel.brushTargetManager.controlSeries(area, seriesModel, ecModel)) {
+                    rangeInfoList.push(area);
+                }
+                hasBrushExists = hasBrushExists || brushed(rangeInfoList);
             });
 
             if (linkOthers(seriesIndex) && brushed(rangeInfoList)) {
                 var data = seriesModel.getData();
                 data.each(function (dataIndex) {
-                    if (checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex)) {
+                    if (checkInRange(seriesModel, rangeInfoList, data, dataIndex)) {
                         selectedDataIndexForLink[dataIndex] = 1;
                     }
                 });
@@ -174,7 +203,7 @@ echarts.registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) {
 
         // Step B
         ecModel.eachSeries(function (seriesModel, seriesIndex) {
-            var seriesBrushSelected = {
+            var seriesBrushSelected: BrushSelectedItem['selected'][0] = {
                 seriesId: seriesModel.id,
                 seriesIndex: seriesIndex,
                 seriesName: seriesModel.name,
@@ -184,18 +213,17 @@ echarts.registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) {
             // for user to find series by seriesIndex.
             thisBrushSelected.selected.push(seriesBrushSelected);
 
-            var selectorsByBrushType = getSelectorsByBrushType(seriesModel);
             var rangeInfoList = rangeInfoBySeries[seriesIndex];
 
             var data = seriesModel.getData();
             var getValueState = linkOthers(seriesIndex)
-                ? function (dataIndex) {
+                ? function (dataIndex: number): BrushVisualState {
                     return selectedDataIndexForLink[dataIndex]
                         ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush')
                         : 'outOfBrush';
                 }
-                : function (dataIndex) {
-                    return checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex)
+                : function (dataIndex: number): BrushVisualState {
+                    return checkInRange(seriesModel, rangeInfoList, data, dataIndex)
                         ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush')
                         : 'outOfBrush';
                 };
@@ -212,7 +240,13 @@ echarts.registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) {
     dispatchAction(api, throttleType, throttleDelay, brushSelected, payload);
 });
 
-function dispatchAction(api, throttleType, throttleDelay, brushSelected, payload) {
+function dispatchAction(
+    api: ExtensionAPI,
+    throttleType: throttleUtil.ThrottleType,
+    throttleDelay: number,
+    brushSelected: BrushSelectedItem[],
+    payload: Payload
+): void {
     // This event will not be triggered when `setOpion`, otherwise dead lock may
     // triggered when do `setOption` in event listener, which we do not find
     // satisfactory way to solve yet. Some considered resolutions:
@@ -225,7 +259,7 @@ function dispatchAction(api, throttleType, throttleDelay, brushSelected, payload
         return;
     }
 
-    var zr = api.getZr();
+    var zr = api.getZr() as BrushGlobalDispatcher;
     if (zr[DISPATCH_FLAG]) {
         return;
     }
@@ -239,9 +273,9 @@ function dispatchAction(api, throttleType, throttleDelay, brushSelected, payload
     fn(api, brushSelected);
 }
 
-function doDispatch(api, brushSelected) {
+function doDispatch(api: ExtensionAPI, brushSelected: BrushSelectedItem[]): void {
     if (!api.isDisposed()) {
-        var zr = api.getZr();
+        var zr = api.getZr() as BrushGlobalDispatcher;
         zr[DISPATCH_FLAG] = true;
         api.dispatchAction({
             type: 'brushSelect',
@@ -251,10 +285,15 @@ function doDispatch(api, brushSelected) {
     }
 }
 
-function checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex) {
+function checkInRange(
+    seriesModel: SeriesModel,
+    rangeInfoList: BrushSelectableArea[],
+    data: ReturnType<SeriesModel['getData']>,
+    dataIndex: number
+) {
     for (var i = 0, len = rangeInfoList.length; i < len; i++) {
         var area = rangeInfoList[i];
-        if (selectorsByBrushType[area.brushType](
+        if (seriesModel.brushSelector(
             dataIndex, data, area.selectors, area
         )) {
             return true;
@@ -262,29 +301,7 @@ function checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex) {
     }
 }
 
-function getSelectorsByBrushType(seriesModel) {
-    var brushSelector = seriesModel.brushSelector;
-    if (zrUtil.isString(brushSelector)) {
-        var sels = [];
-        zrUtil.each(selector, function (selectorsByElementType, brushType) {
-            sels[brushType] = function (dataIndex, data, selectors, area) {
-                var itemLayout = data.getItemLayout(dataIndex);
-                return selectorsByElementType[brushSelector](itemLayout, selectors, area);
-            };
-        });
-        return sels;
-    }
-    else if (zrUtil.isFunction(brushSelector)) {
-        var bSelector = {};
-        zrUtil.each(selector, function (sel, brushType) {
-            bSelector[brushType] = brushSelector;
-        });
-        return bSelector;
-    }
-    return brushSelector;
-}
-
-function brushModelNotControll(brushModel, seriesIndex) {
+function brushModelNotControll(brushModel: BrushModel, seriesIndex: number): boolean {
     var seriesIndices = brushModel.option.seriesIndex;
     return seriesIndices != null
         && seriesIndices !== 'all'
@@ -295,30 +312,16 @@ function brushModelNotControll(brushModel, seriesIndex) {
         );
 }
 
-function bindSelector(area) {
-    var selectors = area.selectors = {};
-    zrUtil.each(selector[area.brushType], function (selFn, elType) {
-        // Do not use function binding or curry for performance.
-        selectors[elType] = function (itemLayout) {
-            return selFn(itemLayout, selectors, area);
-        };
-    });
-    return area;
-}
-
-var boundingRectBuilders = {
-
-    lineX: zrUtil.noop,
-
-    lineY: zrUtil.noop,
+type AreaBoundingRectBuilder = (area: BrushAreaParamInternal) => BoundingRect;
+var boundingRectBuilders: Partial<Record<BrushType, AreaBoundingRectBuilder>> = {
 
     rect: function (area) {
-        return getBoundingRectFromMinMax(area.range);
+        return getBoundingRectFromMinMax(area.range as BrushDimensionMinMax[]);
     },
 
     polygon: function (area) {
         var minMax;
-        var range = area.range;
+        var range = area.range as BrushDimensionMinMax[];
 
         for (var i = 0, len = range.length; i < len; i++) {
             minMax = minMax || [[Infinity, -Infinity], [Infinity, -Infinity]];
@@ -333,7 +336,8 @@ var boundingRectBuilders = {
     }
 };
 
-function getBoundingRectFromMinMax(minMax) {
+
+function getBoundingRectFromMinMax(minMax: BrushDimensionMinMax[]): BoundingRect {
     return new BoundingRect(
         minMax[0][0],
         minMax[1][0],
diff --git a/src/component/dataZoom/history.ts b/src/component/dataZoom/history.ts
index 6ab1efa..2252dcc 100644
--- a/src/component/dataZoom/history.ts
+++ b/src/component/dataZoom/history.ts
@@ -26,10 +26,10 @@ import { DataZoomPayloadBatchItem } from './helper';
 
 var each = zrUtil.each;
 
-type StoreSnapshot = Dictionary<DataZoomPayloadBatchItem>
+export type DataZoomStoreSnapshot = Dictionary<DataZoomPayloadBatchItem>
 
 type Store = {
-    snapshots: StoreSnapshot[]
+    snapshots: DataZoomStoreSnapshot[]
 }
 
 const inner = makeInner<Store, GlobalModel>();
@@ -38,7 +38,7 @@ const inner = makeInner<Store, GlobalModel>();
  * @param ecModel
  * @param newSnapshot key is dataZoomId
  */
-export function push(ecModel: GlobalModel, newSnapshot: StoreSnapshot) {
+export function push(ecModel: GlobalModel, newSnapshot: DataZoomStoreSnapshot) {
     var storedSnapshots = getStoreSnapshots(ecModel);
 
     // If previous dataZoom can not be found,
@@ -76,7 +76,7 @@ export function pop(ecModel: GlobalModel) {
     storedSnapshots.length > 1 && storedSnapshots.pop();
 
     // Find top for all dataZoom.
-    var snapshot: StoreSnapshot = {};
+    var snapshot: DataZoomStoreSnapshot = {};
     each(head, function (batchItem, dataZoomId) {
         for (var i = storedSnapshots.length - 1; i >= 0; i--) {
             var batchItem = storedSnapshots[i][dataZoomId];
diff --git a/src/component/geo/GeoView.ts b/src/component/geo/GeoView.ts
index e8b32b7..e1b03e8 100644
--- a/src/component/geo/GeoView.ts
+++ b/src/component/geo/GeoView.ts
@@ -66,4 +66,6 @@ class GeoView extends ComponentView {
 
 }
 
+ComponentView.registerClass(GeoView);
+
 export default GeoView;
diff --git a/src/component/helper/BrushController.ts b/src/component/helper/BrushController.ts
index 92d5c47..a94ba32 100644
--- a/src/component/helper/BrushController.ts
+++ b/src/component/helper/BrushController.ts
@@ -17,18 +17,116 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import {__DEV__} from '../../config';
-import * as zrUtil from 'zrender/src/core/util';
+import {curry, each, map, bind, merge, clone, defaults, assert} from 'zrender/src/core/util';
 import Eventful from 'zrender/src/core/Eventful';
 import * as graphic from '../../util/graphic';
 import * as interactionMutex from './interactionMutex';
 import DataDiffer from '../../data/DataDiffer';
+import { Dictionary } from '../../util/types';
+import { StyleProps } from 'zrender/src/graphic/Style';
+import { ZRenderType } from 'zrender/src/zrender';
+import { ElementEvent } from 'zrender/src/Element';
+import * as matrix from 'zrender/src/core/matrix';
+import Displayable from 'zrender/src/graphic/Displayable';
+
+
+/**
+ * BrushController not only used in "brush component",
+ * but also used in "tooltip DataZoom", and other possible
+ * futher brush behavior related scenarios.
+ * So `BrushController` should not depends on "brush component model".
+ */
+
+
+export type BrushType = 'polygon' | 'rect' | 'lineX' | 'lineY';
+/**
+ * Only for drawing (after enabledBrush).
+ * 'line', 'rect', 'polygon' or false
+ * If passing false/null/undefined, disable brush.
+ * If passing 'auto', determined by panel.defaultBrushType
+ */
+export type BrushTypeUncertain = BrushType | false | 'auto';
+
+export type BrushMode = 'single' | 'multiple';
+// MinMax: Range of linear brush.
+// MinMax[]: Range of multi-dimension like rect/polygon, which is a MinMax
+//     list for each dimension of the coord sys. For example:
+//     cartesian: [[xMin, xMax], [yMin, yMax]]
+//     geo: [[lngMin, lngMin], [latMin, latMax]]
+export type BrushDimensionMinMax = number[];
+export type BrushAreaRange = BrushDimensionMinMax | BrushDimensionMinMax[];
+
+export interface BrushCoverConfig {
+    // Mandatory. determine how to convert to/from coord('rect' or 'polygon' or 'lineX/Y')
+    brushType: BrushType;
+    // Can be specified by user to map covers in `updateCovers`
+    // in `dispatchAction({type: 'brush', areas: [{id: ...}, ...]})`
+    id?: string;
+    // Range in global coordinate (pixel).
+    range?: BrushAreaRange;
+    // When create a new area by `updateCovers`, panelId should be specified.
+    // If not null/undefined, means global panel.
+    // Also see `BrushAreaParam['panelId']`.
+    panelId?: string;
+
+    brushMode?: BrushMode;
+    // `brushStyle`, `transformable` is not mandatory, use DEFAULT_BRUSH_OPT by default.
+    brushStyle?: Pick<StyleProps, BrushStyleKey>;
+    transformable?: boolean;
+    removeOnClick?: boolean;
+    z?: number;
+}
+
+/**
+ * `BrushAreaCreatorOption` input to brushModel via `setBrushOption`,
+ * merge and convert to `BrushCoverCreatorConfig`.
+ */
+export interface BrushCoverCreatorConfig extends Pick<
+    BrushCoverConfig,
+    'brushMode' | 'transformable' | 'removeOnClick' | 'brushStyle' | 'z'
+> {
+    brushType: BrushTypeUncertain;
+}
+
+type BrushStyleKey =
+    'fill'
+    | 'stroke'
+    | 'lineWidth'
+    | 'opacity'
+    | 'shadowBlur'
+    | 'shadowOffsetX'
+    | 'shadowOffsetY'
+    | 'shadowColor';
+
+
+const BRUSH_PANEL_GLOBAL = true as const;
+
+export interface BrushPanelConfig {
+    // mandatory.
+    panelId: string;
+    // mandatory.
+    clipPath(localPoints: number[][], transform: matrix.MatrixArray): number[][];
+    // mandatory.
+    isTargetByCursor(e: ElementEvent, localCursorPoint: number[], transform: matrix.MatrixArray): boolean;
+    // optional, only used when brushType is 'auto'.
+    defaultBrushType?: BrushType;
+    // optional.
+    getLinearBrushOtherExtent?(xyIndex: number): number[];
+}
+// `true` represents global panel;
+type BrushPanelConfigOrGlobal = BrushPanelConfig | typeof BRUSH_PANEL_GLOBAL;
+
+interface BrushPanelElement extends graphic.Group {
+}
+
+interface BrushCover extends graphic.Group {
+    __brushOption: BrushCoverConfig;
+}
+
+type Point = number[];
 
-var curry = zrUtil.curry;
-var each = zrUtil.each;
-var map = zrUtil.map;
 var mathMin = Math.min;
 var mathMax = Math.max;
 var mathPow = Math.pow;
@@ -38,12 +136,15 @@ var UNSELECT_THRESHOLD = 6;
 var MIN_RESIZE_LINE_WIDTH = 6;
 var MUTEX_RESOURCE_KEY = 'globalPan';
 
+type DirectionName = 'w' | 'e' | 'n' | 's';
+type DirectionNameSequence = DirectionName[];
+
 var DIRECTION_MAP = {
     w: [0, 0],
     e: [0, 1],
     n: [1, 0],
     s: [1, 1]
-};
+} as const;
 var CURSOR_MAP = {
     w: 'ew',
     e: 'ew',
@@ -53,7 +154,7 @@ var CURSOR_MAP = {
     sw: 'nesw',
     nw: 'nwse',
     se: 'nwse'
-};
+} as const;
 var DEFAULT_BRUSH_OPT = {
     brushStyle: {
         lineWidth: 2,
@@ -67,187 +168,180 @@ var DEFAULT_BRUSH_OPT = {
 
 var baseUID = 0;
 
+export interface BrushControllerEvents {
+    brush: {
+        areas: {
+            brushType: BrushType;
+            panelId: string;
+            range: BrushAreaRange;
+        }[];
+        isEnd: boolean;
+        removeOnClick: boolean;
+    }
+}
+
 /**
- * @alias module:echarts/component/helper/BrushController
- * @constructor
- * @mixin {module:zrender/mixin/Eventful}
- * @event module:echarts/component/helper/BrushController#brush
- *        params:
- *            areas: Array.<Array>, coord relates to container group,
- *                                    If no container specified, to global.
- *            opt {
- *                isEnd: boolean,
- *                removeOnClick: boolean
- *            }
- *
- * @param {module:zrender/zrender~ZRender} zr
+ * params:
+ *     areas: Array.<Array>, coord relates to container group,
+ *                             If no container specified, to global.
+ *     opt {
+ *         isEnd: boolean,
+ *         removeOnClick: boolean
+ *     }
  */
-function BrushController(zr) {
-
-    if (__DEV__) {
-        zrUtil.assert(zr);
-    }
+class BrushController extends Eventful<BrushControllerEvents> {
 
-    Eventful.call(this);
+    readonly group: graphic.Group;
 
     /**
-     * @type {module:zrender/zrender~ZRender}
-     * @private
+     * @internal
      */
-    this._zr = zr;
+    _zr: ZRenderType;
 
     /**
-     * @type {module:zrender/container/Group}
-     * @readOnly
+     * @internal
      */
-    this.group = new graphic.Group();
+    _brushType: BrushTypeUncertain;
 
     /**
+     * @internal
      * Only for drawing (after enabledBrush).
-     *     'line', 'rect', 'polygon' or false
-     *     If passing false/null/undefined, disable brush.
-     *     If passing 'auto', determined by panel.defaultBrushType
-     * @private
-     * @type {string}
      */
-    this._brushType;
+    _brushOption: BrushCoverCreatorConfig;
 
     /**
-     * Only for drawing (after enabledBrush).
-     *
-     * @private
-     * @type {Object}
+     * @internal
+     * Key: panelId
      */
-    this._brushOption;
+    _panels: Dictionary<BrushPanelConfig>;
 
     /**
-     * @private
-     * @type {Object}
+     * @internal
      */
-    this._panels;
+    _track: number[][] = [];
 
     /**
-     * @private
-     * @type {Array.<nubmer>}
+     * @internal
      */
-    this._track = [];
+    _dragging: boolean;
 
     /**
-     * @private
-     * @type {boolean}
+     * @internal
      */
-    this._dragging;
+    _covers: BrushCover[] = [];
 
     /**
-     * @private
-     * @type {Array}
+     * @internal
      */
-    this._covers = [];
+    _creatingCover: BrushCover;
 
     /**
-     * @private
-     * @type {moudule:zrender/container/Group}
+     * @internal
      */
-    this._creatingCover;
+    _creatingPanel: BrushPanelConfigOrGlobal;
 
-    /**
-     * `true` means global panel
-     * @private
-     * @type {module:zrender/container/Group|boolean}
-     */
-    this._creatingPanel;
+    private _enableGlobalPan: boolean;
 
-    /**
-     * @private
-     * @type {boolean}
-     */
-    this._enableGlobalPan;
+    private _mounted: boolean;
 
     /**
-     * @private
-     * @type {boolean}
+     * @internal
      */
-    if (__DEV__) {
-        this._mounted;
-    }
+    _transform: matrix.MatrixArray;
 
-    /**
-     * @private
-     * @type {string}
-     */
-    this._uid = 'brushController_' + baseUID++;
+    private _uid: string;
 
-    /**
-     * @private
-     * @type {Object}
-     */
-    this._handlers = {};
+    private _handlers: {
+        [eventName: string]: (this: BrushController, e: ElementEvent) => void
+    } = {};
 
-    each(pointerHandlers, function (handler, eventName) {
-        this._handlers[eventName] = zrUtil.bind(handler, this);
-    }, this);
-}
 
-BrushController.prototype = {
+    constructor(zr: ZRenderType) {
+        super();
 
-    constructor: BrushController,
+        if (__DEV__) {
+            assert(zr);
+        }
+
+        this._zr = zr;
+
+        this.group = new graphic.Group();
+
+        this._uid = 'brushController_' + baseUID++;
+
+        each(pointerHandlers, function (this: BrushController, handler, eventName) {
+            this._handlers[eventName] = bind(handler, this);
+        }, this);
+    }
 
     /**
-     * If set to null/undefined/false, select disabled.
-     * @param {Object} brushOption
-     * @param {string|boolean} brushOption.brushType 'line', 'rect', 'polygon' or false
-     *                          If passing false/null/undefined, disable brush.
-     *                          If passing 'auto', determined by panel.defaultBrushType.
-     *                              ('auto' can not be used in global panel)
-     * @param {number} [brushOption.brushMode='single'] 'single' or 'multiple'
-     * @param {boolean} [brushOption.transformable=true]
-     * @param {boolean} [brushOption.removeOnClick=false]
-     * @param {Object} [brushOption.brushStyle]
-     * @param {number} [brushOption.brushStyle.width]
-     * @param {number} [brushOption.brushStyle.lineWidth]
-     * @param {string} [brushOption.brushStyle.stroke]
-     * @param {string} [brushOption.brushStyle.fill]
-     * @param {number} [brushOption.z]
+     * If set to `false`, select disabled.
      */
-    enableBrush: function (brushOption) {
+    enableBrush(brushOption: Partial<BrushCoverCreatorConfig> | false): BrushController {
         if (__DEV__) {
-            zrUtil.assert(this._mounted);
+            assert(this._mounted);
         }
 
-        this._brushType && doDisableBrush(this);
-        brushOption.brushType && doEnableBrush(this, brushOption);
+        this._brushType && this._doDisableBrush();
+        (brushOption as Partial<BrushCoverCreatorConfig>).brushType && this._doEnableBrush(
+            brushOption as Partial<BrushCoverCreatorConfig>
+        );
 
         return this;
-    },
+    }
+
+    private _doEnableBrush(brushOption: Partial<BrushCoverCreatorConfig>): void {
+        var zr = this._zr;
+
+        // Consider roam, which takes globalPan too.
+        if (!this._enableGlobalPan) {
+            interactionMutex.take(zr, MUTEX_RESOURCE_KEY, this._uid);
+        }
+
+        each(this._handlers, function (handler, eventName) {
+            zr.on(eventName, handler);
+        });
+
+        this._brushType = brushOption.brushType;
+        this._brushOption = merge(
+            clone(DEFAULT_BRUSH_OPT), brushOption, true
+        ) as BrushCoverCreatorConfig;
+    }
+
+    private _doDisableBrush(): void {
+        var zr = this._zr;
+
+        interactionMutex.release(zr, MUTEX_RESOURCE_KEY, this._uid);
+
+        each(this._handlers, function (handler, eventName) {
+            zr.off(eventName, handler);
+        });
+
+        this._brushType = this._brushOption = null;
+    }
 
     /**
-     * @param {Array.<Object>} panelOpts If not pass, it is global brush.
-     *        Each items: {
-     *            panelId, // mandatory.
-     *            clipPath, // mandatory. function.
-     *            isTargetByCursor, // mandatory. function.
-     *            defaultBrushType, // optional, only used when brushType is 'auto'.
-     *            getLinearBrushOtherExtent, // optional. function.
-     *        }
+     * @param panelOpts If not pass, it is global brush.
      */
-    setPanels: function (panelOpts) {
+    setPanels(panelOpts?: BrushPanelConfig[]): BrushController {
         if (panelOpts && panelOpts.length) {
-            var panels = this._panels = {};
-            zrUtil.each(panelOpts, function (panelOpts) {
-                panels[panelOpts.panelId] = zrUtil.clone(panelOpts);
+            var panels = this._panels = {} as Dictionary<BrushPanelConfig>;
+            each(panelOpts, function (panelOpts) {
+                panels[panelOpts.panelId] = clone(panelOpts);
             });
         }
         else {
             this._panels = null;
         }
         return this;
-    },
+    }
 
-    /**
-     * @param {Object} [opt]
-     * @return {boolean} [opt.enableGlobalPan=false]
-     */
-    mount: function (opt) {
+    mount(opt?: {
+        enableGlobalPan?: boolean;
+        position?: number[];
+        rotation?: number;
+        scale?: number[];
+    }): BrushController {
         opt = opt || {};
 
         if (__DEV__) {
@@ -267,41 +361,33 @@ BrushController.prototype = {
         this._transform = thisGroup.getLocalTransform();
 
         return this;
-    },
+    }
 
-    eachCover: function (cb, context) {
-        each(this._covers, cb, context);
-    },
+    // eachCover(cb, context): void {
+    //     each(this._covers, cb, context);
+    // }
 
     /**
      * Update covers.
-     * @param {Array.<Object>} brushOptionList Like:
-     *        [
-     *            {id: 'xx', brushType: 'line', range: [23, 44], brushStyle, transformable},
-     *            {id: 'yy', brushType: 'rect', range: [[23, 44], [23, 54]]},
-     *            ...
-     *        ]
-     *        `brushType` is required in each cover info. (can not be 'auto')
-     *        `id` is not mandatory.
-     *        `brushStyle`, `transformable` is not mandatory, use DEFAULT_BRUSH_OPT by default.
-     *        If brushOptionList is null/undefined, all covers removed.
+     * @param coverConfigList
+     *        If coverConfigList is null/undefined, all covers removed.
      */
-    updateCovers: function (brushOptionList) {
+    updateCovers(coverConfigList: BrushCoverConfig[]) {
         if (__DEV__) {
-            zrUtil.assert(this._mounted);
+            assert(this._mounted);
         }
 
-        brushOptionList = zrUtil.map(brushOptionList, function (brushOption) {
-            return zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true);
-        });
+        coverConfigList = map(coverConfigList, function (coverConfig) {
+            return merge(clone(DEFAULT_BRUSH_OPT), coverConfig, true);
+        }) as BrushCoverConfig[];
 
         var tmpIdPrefix = '\0-brush-index-';
         var oldCovers = this._covers;
-        var newCovers = this._covers = [];
+        var newCovers = this._covers = [] as BrushCover[];
         var controller = this;
         var creatingCover = this._creatingCover;
 
-        (new DataDiffer(oldCovers, brushOptionList, oldGetKey, getKey))
+        (new DataDiffer(oldCovers, coverConfigList, oldGetKey, getKey))
             .add(addOrUpdate)
             .update(addOrUpdate)
             .remove(remove)
@@ -309,17 +395,17 @@ BrushController.prototype = {
 
         return this;
 
-        function getKey(brushOption, index) {
+        function getKey(brushOption: BrushCoverConfig, index: number): string {
             return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index)
                 + '-' + brushOption.brushType;
         }
 
-        function oldGetKey(cover, index) {
+        function oldGetKey(cover: BrushCover, index: number): string {
             return getKey(cover.__brushOption, index);
         }
 
-        function addOrUpdate(newIndex, oldIndex) {
-            var newBrushOption = brushOptionList[newIndex];
+        function addOrUpdate(newIndex: number, oldIndex?: number): void {
+            var newBrushInternal = coverConfigList[newIndex];
             // Consider setOption in event listener of brushSelect,
             // where updating cover when creating should be forbiden.
             if (oldIndex != null && oldCovers[oldIndex] === creatingCover) {
@@ -328,22 +414,22 @@ BrushController.prototype = {
             else {
                 var cover = newCovers[newIndex] = oldIndex != null
                     ? (
-                        oldCovers[oldIndex].__brushOption = newBrushOption,
+                        oldCovers[oldIndex].__brushOption = newBrushInternal,
                         oldCovers[oldIndex]
                     )
-                    : endCreating(controller, createCover(controller, newBrushOption));
+                    : endCreating(controller, createCover(controller, newBrushInternal));
                 updateCoverAfterCreation(controller, cover);
             }
         }
 
-        function remove(oldIndex) {
+        function remove(oldIndex: number) {
             if (oldCovers[oldIndex] !== creatingCover) {
                 controller.group.remove(oldCovers[oldIndex]);
             }
         }
-    },
+    }
 
-    unmount: function () {
+    unmount() {
         if (__DEV__) {
             if (!this._mounted) {
                 return;
@@ -361,53 +447,16 @@ BrushController.prototype = {
         }
 
         return this;
-    },
+    }
 
-    dispose: function () {
+    dispose() {
         this.unmount();
         this.off();
     }
-};
-
-zrUtil.mixin(BrushController, Eventful);
-
-function doEnableBrush(controller, brushOption) {
-    var zr = controller._zr;
-
-    // Consider roam, which takes globalPan too.
-    if (!controller._enableGlobalPan) {
-        interactionMutex.take(zr, MUTEX_RESOURCE_KEY, controller._uid);
-    }
-
-    mountHandlers(zr, controller._handlers);
-
-    controller._brushType = brushOption.brushType;
-    controller._brushOption = zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true);
 }
 
-function doDisableBrush(controller) {
-    var zr = controller._zr;
-
-    interactionMutex.release(zr, MUTEX_RESOURCE_KEY, controller._uid);
-
-    unmountHandlers(zr, controller._handlers);
 
-    controller._brushType = controller._brushOption = null;
-}
-
-function mountHandlers(zr, handlers) {
-    each(handlers, function (handler, eventName) {
-        zr.on(eventName, handler);
-    });
-}
-
-function unmountHandlers(zr, handlers) {
-    each(handlers, function (handler, eventName) {
-        zr.off(eventName, handler);
-    });
-}
-
-function createCover(controller, brushOption) {
+function createCover(controller: BrushController, brushOption: BrushCoverConfig): BrushCover {
     var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption);
     cover.__brushOption = brushOption;
     updateZ(cover, brushOption);
@@ -415,7 +464,7 @@ function createCover(controller, brushOption) {
     return cover;
 }
 
-function endCreating(controller, creatingCover) {
+function endCreating(controller: BrushController, creatingCover: BrushCover): BrushCover {
     var coverRenderer = getCoverRenderer(creatingCover);
     if (coverRenderer.endCreating) {
         coverRenderer.endCreating(controller, creatingCover);
@@ -424,36 +473,40 @@ function endCreating(controller, creatingCover) {
     return creatingCover;
 }
 
-function updateCoverShape(controller, cover) {
+function updateCoverShape(controller: BrushController, cover: BrushCover): void {
     var brushOption = cover.__brushOption;
     getCoverRenderer(cover).updateCoverShape(
         controller, cover, brushOption.range, brushOption
     );
 }
 
-function updateZ(cover, brushOption) {
+function updateZ(cover: BrushCover, brushOption: BrushCoverConfig): void {
     var z = brushOption.z;
     z == null && (z = COVER_Z);
-    cover.traverse(function (el) {
+    cover.traverse(function (el: Displayable) {
         el.z = z;
         el.z2 = z; // Consider in given container.
     });
 }
 
-function updateCoverAfterCreation(controller, cover) {
+function updateCoverAfterCreation(controller: BrushController, cover: BrushCover): void {
     getCoverRenderer(cover).updateCommon(controller, cover);
     updateCoverShape(controller, cover);
 }
 
-function getCoverRenderer(cover) {
+function getCoverRenderer(cover: BrushCover): CoverRenderer {
     return coverRenderers[cover.__brushOption.brushType];
 }
 
 // return target panel or `true` (means global panel)
-function getPanelByPoint(controller, e, localCursorPoint) {
+function getPanelByPoint(
+    controller: BrushController,
+    e: ElementEvent,
+    localCursorPoint: Point
+): BrushPanelConfigOrGlobal {
     var panels = controller._panels;
     if (!panels) {
-        return true; // Global panel
+        return BRUSH_PANEL_GLOBAL; // Global panel
     }
     var panel;
     var transform = controller._transform;
@@ -464,18 +517,18 @@ function getPanelByPoint(controller, e, localCursorPoint) {
 }
 
 // Return a panel or true
-function getPanelByCover(controller, cover) {
+function getPanelByCover(controller: BrushController, cover: BrushCover): BrushPanelConfigOrGlobal {
     var panels = controller._panels;
     if (!panels) {
-        return true; // Global panel
+        return BRUSH_PANEL_GLOBAL; // Global panel
     }
     var panelId = cover.__brushOption.panelId;
     // User may give cover without coord sys info,
     // which is then treated as global panel.
-    return panelId != null ? panels[panelId] : true;
+    return panelId != null ? panels[panelId] : BRUSH_PANEL_GLOBAL;
 }
 
-function clearCovers(controller) {
+function clearCovers(controller: BrushController): boolean {
     var covers = controller._covers;
     var originalLength = covers.length;
     each(covers, function (cover) {
@@ -486,10 +539,13 @@ function clearCovers(controller) {
     return !!originalLength;
 }
 
-function trigger(controller, opt) {
+function trigger(
+    controller: BrushController,
+    opt: {isEnd?: boolean, removeOnClick?: boolean}
+): void {
     var areas = map(controller._covers, function (cover) {
         var brushOption = cover.__brushOption;
-        var range = zrUtil.clone(brushOption.range);
+        var range = clone(brushOption.range);
         return {
             brushType: brushOption.brushType,
             panelId: brushOption.panelId,
@@ -497,13 +553,14 @@ function trigger(controller, opt) {
         };
     });
 
-    controller.trigger('brush', areas, {
+    controller.trigger('brush', {
+        areas: areas,
         isEnd: !!opt.isEnd,
         removeOnClick: !!opt.removeOnClick
     });
 }
 
-function shouldShowCover(controller) {
+function shouldShowCover(controller: BrushController): boolean {
     var track = controller._track;
 
     if (!track.length) {
@@ -519,14 +576,23 @@ function shouldShowCover(controller) {
     return dist > UNSELECT_THRESHOLD;
 }
 
-function getTrackEnds(track) {
+function getTrackEnds(track: Point[]): Point[] {
     var tail = track.length - 1;
     tail < 0 && (tail = 0);
     return [track[0], track[tail]];
 }
 
-function createBaseRectCover(doDrift, controller, brushOption, edgeNames) {
-    var cover = new graphic.Group();
+interface RectRangeConverter {
+    toRectRange: (range: BrushAreaRange) => BrushDimensionMinMax[];
+    fromRectRange: (areaRange: BrushDimensionMinMax[]) => BrushAreaRange;
+};
+function createBaseRectCover(
+    rectRangeConverter: RectRangeConverter,
+    controller: BrushController,
+    brushOption: BrushCoverConfig,
+    edgeNameSequences: DirectionNameSequence[]
+): BrushCover {
+    var cover = new graphic.Group() as BrushCover;
 
     cover.add(new graphic.Rect({
         name: 'main',
@@ -534,20 +600,20 @@ function createBaseRectCover(doDrift, controller, brushOption, edgeNames) {
         silent: true,
         draggable: true,
         cursor: 'move',
-        drift: curry(doDrift, controller, cover, 'nswe'),
+        drift: curry(driftRect, rectRangeConverter, controller, cover, ['n', 's', 'w', 'e']),
         ondragend: curry(trigger, controller, {isEnd: true})
     }));
 
     each(
-        edgeNames,
-        function (name) {
+        edgeNameSequences,
+        function (nameSequence) {
             cover.add(new graphic.Rect({
-                name: name,
+                name: nameSequence.join(''),
                 style: {opacity: 0},
                 draggable: true,
                 silent: true,
                 invisible: true,
-                drift: curry(doDrift, controller, cover, name),
+                drift: curry(driftRect, rectRangeConverter, controller, cover, nameSequence),
                 ondragend: curry(trigger, controller, {isEnd: true})
             }));
         }
@@ -556,7 +622,12 @@ function createBaseRectCover(doDrift, controller, brushOption, edgeNames) {
     return cover;
 }
 
-function updateBaseRect(controller, cover, localRange, brushOption) {
+function updateBaseRect(
+    controller: BrushController,
+    cover: BrushCover,
+    localRange: BrushDimensionMinMax[],
+    brushOption: BrushCoverConfig
+): void {
     var lineWidth = brushOption.brushStyle.lineWidth || 0;
     var handleSize = mathMax(lineWidth, MIN_RESIZE_LINE_WIDTH);
     var x = localRange[0][0];
@@ -587,11 +658,11 @@ function updateBaseRect(controller, cover, localRange, brushOption) {
     }
 }
 
-function updateCommon(controller, cover) {
+function updateCommon(controller: BrushController, cover: BrushCover): void {
     var brushOption = cover.__brushOption;
     var transformable = brushOption.transformable;
 
-    var mainEl = cover.childAt(0);
+    var mainEl = cover.childAt(0) as Displayable;
     mainEl.useStyle(makeStyle(brushOption));
     mainEl.attr({
         silent: !transformable,
@@ -599,10 +670,12 @@ function updateCommon(controller, cover) {
     });
 
     each(
-        ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'],
-        function (name) {
-            var el = cover.childOfName(name);
-            var globalDir = getGlobalDirection(controller, name);
+        [['w'], ['e'], ['n'], ['s'], ['s', 'e'], ['s', 'w'], ['n', 'e'], ['n', 'w']],
+        function (nameSequence: DirectionNameSequence) {
+            var el = cover.childOfName(nameSequence.join('')) as Displayable;
+            var globalDir = nameSequence.length === 1
+                ? getGlobalDirection1(controller, nameSequence[0])
+                : getGlobalDirection2(controller, nameSequence);
 
             el && el.attr({
                 silent: !transformable,
@@ -613,18 +686,23 @@ function updateCommon(controller, cover) {
     );
 }
 
-function updateRectShape(controller, cover, name, x, y, w, h) {
-    var el = cover.childOfName(name);
+function updateRectShape(
+    controller: BrushController,
+    cover: BrushCover,
+    name: string,
+    x: number, y: number, w: number, h: number
+): void {
+    var el = cover.childOfName(name) as graphic.Rect;
     el && el.setShape(pointsToRect(
         clipByPanel(controller, cover, [[x, y], [x + w, y + h]])
     ));
 }
 
-function makeStyle(brushOption) {
-    return zrUtil.defaults({strokeNoScale: true}, brushOption.brushStyle);
+function makeStyle(brushOption: BrushCoverConfig) {
+    return defaults({strokeNoScale: true}, brushOption.brushStyle);
 }
 
-function formatRectRange(x, y, x2, y2) {
+function formatRectRange(x: number, y: number, x2: number, y2: number): BrushDimensionMinMax[] {
     var min = [mathMin(x, x2), mathMin(y, y2)];
     var max = [mathMax(x, x2), mathMax(y, y2)];
 
@@ -634,41 +712,49 @@ function formatRectRange(x, y, x2, y2) {
     ];
 }
 
-function getTransform(controller) {
+function getTransform(controller: BrushController): matrix.MatrixArray {
     return graphic.getTransform(controller.group);
 }
 
-function getGlobalDirection(controller, localDirection) {
-    if (localDirection.length > 1) {
-        localDirection = localDirection.split('');
-        var globalDir = [
-            getGlobalDirection(controller, localDirection[0]),
-            getGlobalDirection(controller, localDirection[1])
-        ];
-        (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse();
-        return globalDir.join('');
-    }
-    else {
-        var map = {w: 'left', e: 'right', n: 'top', s: 'bottom'};
-        var inverseMap = {left: 'w', right: 'e', top: 'n', bottom: 's'};
-        var globalDir = graphic.transformDirection(
-            map[localDirection], getTransform(controller)
-        );
-        return inverseMap[globalDir];
-    }
+function getGlobalDirection1(
+    controller: BrushController, localDirName: DirectionName
+): keyof typeof CURSOR_MAP {
+    var map = {w: 'left', e: 'right', n: 'top', s: 'bottom'} as const;
+    var inverseMap = {left: 'w', right: 'e', top: 'n', bottom: 's'} as const;
+    var dir = graphic.transformDirection(
+        map[localDirName], getTransform(controller)
+    );
+    return inverseMap[dir];
+}
+function getGlobalDirection2(
+    controller: BrushController, localDirNameSeq: DirectionNameSequence
+): keyof typeof CURSOR_MAP {
+    var globalDir = [
+        getGlobalDirection1(controller, localDirNameSeq[0]),
+        getGlobalDirection1(controller, localDirNameSeq[1])
+    ];
+    (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse();
+    return globalDir.join('') as keyof typeof CURSOR_MAP;
 }
 
-function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy, e) {
+function driftRect(
+    rectRangeConverter: RectRangeConverter,
+    controller: BrushController,
+    cover: BrushCover,
+    dirNameSequence: DirectionNameSequence,
+    dx: number,
+    dy: number
+): void {
     var brushOption = cover.__brushOption;
-    var rectRange = toRectRange(brushOption.range);
+    var rectRange = rectRangeConverter.toRectRange(brushOption.range);
     var localDelta = toLocalDelta(controller, dx, dy);
 
-    each(name.split(''), function (namePart) {
-        var ind = DIRECTION_MAP[namePart];
+    each(dirNameSequence, function (dirName) {
+        var ind = DIRECTION_MAP[dirName];
         rectRange[ind[0]][ind[1]] += localDelta[ind[0]];
     });
 
-    brushOption.range = fromRectRange(formatRectRange(
+    brushOption.range = rectRangeConverter.fromRectRange(formatRectRange(
         rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1]
     ));
 
@@ -676,8 +762,13 @@ function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy,
     trigger(controller, {isEnd: false});
 }
 
-function driftPolygon(controller, cover, dx, dy, e) {
-    var range = cover.__brushOption.range;
+function driftPolygon(
+    controller: BrushController,
+    cover: BrushCover,
+    dx: number,
+    dy: number
+): void {
+    var range = cover.__brushOption.range as BrushDimensionMinMax[];
     var localDelta = toLocalDelta(controller, dx, dy);
 
     each(range, function (point) {
@@ -689,7 +780,9 @@ function driftPolygon(controller, cover, dx, dy, e) {
     trigger(controller, {isEnd: false});
 }
 
-function toLocalDelta(controller, dx, dy) {
+function toLocalDelta(
+    controller: BrushController, dx: number, dy: number
+): BrushDimensionMinMax {
     var thisGroup = controller.group;
     var localD = thisGroup.transformCoordToLocal(dx, dy);
     var localZero = thisGroup.transformCoordToLocal(0, 0);
@@ -697,15 +790,15 @@ function toLocalDelta(controller, dx, dy) {
     return [localD[0] - localZero[0], localD[1] - localZero[1]];
 }
 
-function clipByPanel(controller, cover, data) {
+function clipByPanel(controller: BrushController, cover: BrushCover, data: Point[]): Point[] {
     var panel = getPanelByCover(controller, cover);
 
-    return (panel && panel !== true)
+    return (panel && panel !== BRUSH_PANEL_GLOBAL)
         ? panel.clipPath(data, controller._transform)
-        : zrUtil.clone(data);
+        : clone(data);
 }
 
-function pointsToRect(points) {
+function pointsToRect(points: Point[]): graphic.Rect['shape'] {
     var xmin = mathMin(points[0][0], points[1][0]);
     var ymin = mathMin(points[0][1], points[1][1]);
     var xmax = mathMax(points[0][0], points[1][0]);
@@ -719,14 +812,16 @@ function pointsToRect(points) {
     };
 }
 
-function resetCursor(controller, e, localCursorPoint) {
+function resetCursor(
+    controller: BrushController, e: ElementEvent, localCursorPoint: Point
+): void {
     if (
         // Check active
         !controller._brushType
         // resetCursor should be always called when mouse is in zr area,
         // but not called when mouse is out of zr area to avoid bad influence
         // if `mousemove`, `mouseup` are triggered from `document` event.
-        || isOutsideZrArea(controller, e)
+        || isOutsideZrArea(controller, e.offsetX, e.offsetY)
     ) {
         return;
     }
@@ -740,7 +835,7 @@ function resetCursor(controller, e, localCursorPoint) {
         for (var i = 0; i < covers.length; i++) {
             var brushOption = covers[i].__brushOption;
             if (currPanel
-                && (currPanel === true || brushOption.panelId === currPanel.panelId)
+                && (currPanel === BRUSH_PANEL_GLOBAL || brushOption.panelId === currPanel.panelId)
                 && coverRenderers[brushOption.brushType].contain(
                     covers[i], localCursorPoint[0], localCursorPoint[1]
                 )
@@ -754,16 +849,24 @@ function resetCursor(controller, e, localCursorPoint) {
     currPanel && zr.setCursorStyle('crosshair');
 }
 
-function preventDefault(e) {
+function preventDefault(e: ElementEvent): void {
     var rawE = e.event;
     rawE.preventDefault && rawE.preventDefault();
 }
 
-function mainShapeContain(cover, x, y) {
-    return cover.childOfName('main').contain(x, y);
+function mainShapeContain(cover: BrushCover, x: number, y: number): boolean {
+    return (cover.childOfName('main') as Displayable).contain(x, y);
 }
 
-function updateCoverByMouse(controller, e, localCursorPoint, isEnd) {
+function updateCoverByMouse(
+    controller: BrushController,
+    e: ElementEvent,
+    localCursorPoint: Point,
+    isEnd: boolean
+): {
+    isEnd: boolean,
+    removeOnClick?: boolean
+} {
     var creatingCover = controller._creatingCover;
     var panel = controller._creatingPanel;
     var thisBrushOption = controller._brushOption;
@@ -775,15 +878,17 @@ function updateCoverByMouse(controller, e, localCursorPoint, isEnd) {
 
         if (panel && !creatingCover) {
             thisBrushOption.brushMode === 'single' && clearCovers(controller);
-            var brushOption = zrUtil.clone(thisBrushOption);
-            brushOption.brushType = determineBrushType(brushOption.brushType, panel);
-            brushOption.panelId = panel === true ? null : panel.panelId;
+            var brushOption = clone(thisBrushOption) as BrushCoverConfig;
+            brushOption.brushType = determineBrushType(brushOption.brushType, panel as BrushPanelConfig);
+            brushOption.panelId = panel === BRUSH_PANEL_GLOBAL ? null : panel.panelId;
             creatingCover = controller._creatingCover = createCover(controller, brushOption);
             controller._covers.push(creatingCover);
         }
 
         if (creatingCover) {
-            var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)];
+            var coverRenderer = coverRenderers[
+                determineBrushType(controller._brushType, panel as BrushPanelConfig)
+            ];
             var coverBrushOption = creatingCover.__brushOption;
 
             coverBrushOption.range = coverRenderer.getCreatingRange(
@@ -818,20 +923,20 @@ function updateCoverByMouse(controller, e, localCursorPoint, isEnd) {
     return eventParams;
 }
 
-function determineBrushType(brushType, panel) {
+function determineBrushType(brushType: BrushTypeUncertain, panel: BrushPanelConfig): BrushType {
     if (brushType === 'auto') {
         if (__DEV__) {
-            zrUtil.assert(
+            assert(
                 panel && panel.defaultBrushType,
                 'MUST have defaultBrushType when brushType is "atuo"'
             );
         }
         return panel.defaultBrushType;
     }
-    return brushType;
+    return brushType as BrushType;
 }
 
-var pointerHandlers = {
+var pointerHandlers: Dictionary<(this: BrushController, e: ElementEvent) => void> = {
 
     mousedown: function (e) {
         if (this._dragging) {
@@ -876,7 +981,7 @@ var pointerHandlers = {
 };
 
 
-function handleDragEnd(controller, e) {
+function handleDragEnd(controller: BrushController, e: ElementEvent) {
     if (controller._dragging) {
         preventDefault(e);
 
@@ -895,17 +1000,27 @@ function handleDragEnd(controller, e) {
     }
 }
 
-function isOutsideZrArea(controller, x, y) {
+function isOutsideZrArea(controller: BrushController, x: number, y: number): boolean {
     var zr = controller._zr;
     return x < 0 || x > zr.getWidth() || y < 0 || y > zr.getHeight();
 }
 
 
+interface CoverRenderer {
+    createCover(controller: BrushController, brushOption: BrushCoverConfig): BrushCover;
+    getCreatingRange(localTrack: Point[]): BrushAreaRange;
+    updateCoverShape(
+        controller: BrushController, cover: BrushCover, localRange: BrushAreaRange, brushOption: BrushCoverConfig
+    ): void;
+    updateCommon(controller: BrushController, cover: BrushCover): void;
+    contain(cover: BrushCover, x: number, y: number): boolean;
+    endCreating?(controller: BrushController, creatingCover: BrushCover): void;
+}
+
 /**
  * key: brushType
- * @type {Object}
  */
-var coverRenderers = {
+var coverRenderers: Record<BrushType, CoverRenderer> = {
 
     lineX: getLineRenderer(0),
 
@@ -913,26 +1028,24 @@ var coverRenderers = {
 
     rect: {
         createCover: function (controller, brushOption) {
+            function returnInput(range: BrushDimensionMinMax[]): BrushDimensionMinMax[] {
+                return range;
+            }
             return createBaseRectCover(
-                curry(
-                    driftRect,
-                    function (range) {
-                        return range;
-                    },
-                    function (range) {
-                        return range;
-                    }
-                ),
+                {
+                    toRectRange: returnInput,
+                    fromRectRange: returnInput
+                },
                 controller,
                 brushOption,
-                ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw']
+                [['w'], ['e'], ['n'], ['s'], ['s', 'e'], ['s', 'w'], ['n', 'e'], ['n', 'w']]
             );
         },
         getCreatingRange: function (localTrack) {
             var ends = getTrackEnds(localTrack);
             return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]);
         },
-        updateCoverShape: function (controller, cover, localRange, brushOption) {
+        updateCoverShape: function (controller, cover, localRange: BrushDimensionMinMax[], brushOption) {
             updateBaseRect(controller, cover, localRange, brushOption);
         },
         updateCommon: updateCommon,
@@ -951,7 +1064,7 @@ var coverRenderers = {
                 silent: true
             }));
 
-            return cover;
+            return cover as BrushCover;
         },
         getCreatingRange: function (localTrack) {
             return localTrack;
@@ -966,8 +1079,8 @@ var coverRenderers = {
                 ondragend: curry(trigger, controller, {isEnd: true})
             }));
         },
-        updateCoverShape: function (controller, cover, localRange, brushOption) {
-            cover.childAt(0).setShape({
+        updateCoverShape: function (controller, cover, localRange: BrushDimensionMinMax[], brushOption) {
+            (cover.childAt(0) as graphic.Polygon).setShape({
                 points: clipByPanel(controller, cover, localRange)
             });
         },
@@ -976,41 +1089,43 @@ var coverRenderers = {
     }
 };
 
-function getLineRenderer(xyIndex) {
+function getLineRenderer(xyIndex: 0 | 1) {
     return {
-        createCover: function (controller, brushOption) {
+        createCover: function (controller: BrushController, brushOption: BrushCoverConfig): BrushCover {
             return createBaseRectCover(
-                curry(
-                    driftRect,
-                    function (range) {
+                {
+                    toRectRange: function (range: BrushDimensionMinMax): BrushDimensionMinMax[] {
                         var rectRange = [range, [0, 100]];
                         xyIndex && rectRange.reverse();
                         return rectRange;
                     },
-                    function (rectRange) {
+                    fromRectRange: function (rectRange: BrushDimensionMinMax[]): BrushDimensionMinMax {
                         return rectRange[xyIndex];
                     }
-                ),
+                },
                 controller,
                 brushOption,
-                [['w', 'e'], ['n', 's']][xyIndex]
+                ([[['w'], ['e']], [['n'], ['s']]] as DirectionNameSequence[][])[xyIndex]
             );
         },
-        getCreatingRange: function (localTrack) {
+        getCreatingRange: function (localTrack: Point[]): BrushDimensionMinMax {
             var ends = getTrackEnds(localTrack);
             var min = mathMin(ends[0][xyIndex], ends[1][xyIndex]);
             var max = mathMax(ends[0][xyIndex], ends[1][xyIndex]);
 
             return [min, max];
         },
-        updateCoverShape: function (controller, cover, localRange, brushOption) {
+        updateCoverShape: function (
+            controller: BrushController,
+            cover: BrushCover,
+            localRange: BrushDimensionMinMax,
+            brushOption: BrushCoverConfig
+        ): void {
             var otherExtent;
             // If brushWidth not specified, fit the panel.
             var panel = getPanelByCover(controller, cover);
-            if (panel !== true && panel.getLinearBrushOtherExtent) {
-                otherExtent = panel.getLinearBrushOtherExtent(
-                    xyIndex, controller._transform
-                );
+            if (panel !== BRUSH_PANEL_GLOBAL && panel.getLinearBrushOtherExtent) {
+                otherExtent = panel.getLinearBrushOtherExtent(xyIndex);
             }
             else {
                 var zr = controller._zr;
@@ -1026,4 +1141,4 @@ function getLineRenderer(xyIndex) {
     };
 }
 
-export default BrushController;
\ No newline at end of file
+export default BrushController;
diff --git a/src/component/helper/BrushTargetManager.ts b/src/component/helper/BrushTargetManager.ts
index 115d1e8..dac5171 100644
--- a/src/component/helper/BrushTargetManager.ts
+++ b/src/component/helper/BrushTargetManager.ts
@@ -17,19 +17,30 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import {__DEV__} from '../../config';
-import * as zrUtil from 'zrender/src/core/util';
+import { each, indexOf, curry, assert, map, createHashMap } from 'zrender/src/core/util';
 import * as graphic from '../../util/graphic';
-import * as modelUtil from '../../util/model';
 import * as brushHelper from './brushHelper';
-
-var each = zrUtil.each;
-var indexOf = zrUtil.indexOf;
-var curry = zrUtil.curry;
-
-var COORD_CONVERTS = ['dataToPoint', 'pointToData'];
+import { BrushPanelConfig, BrushControllerEvents, BrushType, BrushAreaRange, BrushDimensionMinMax } from './BrushController';
+import ExtensionAPI from '../../ExtensionAPI';
+import GridModel from '../../coord/cartesian/GridModel';
+import GeoModel from '../../coord/geo/GeoModel';
+import { CoordinateSystemMaster } from '../../coord/CoordinateSystem';
+import Cartesian2D from '../../coord/cartesian/Cartesian2D';
+import Geo from '../../coord/geo/Geo';
+import GlobalModel from '../../model/Global';
+import { BrushAreaParam, BrushAreaParamInternal } from '../brush/BrushModel';
+import SeriesModel from '../../model/Series';
+import { Dictionary } from '../../util/types';
+import {
+    ModelFinderObject, ParsedModelFinder, ModelFinder,
+    parseFinder as modelUtilParseFinder
+} from '../../util/model';
+
+
+var COORD_CONVERTS = ['dataToPoint', 'pointToData'] as const;
+type COORD_CONVERTS_INDEX = 0 | 1;
 
 // FIXME
 // how to genarialize to more coordinate systems.
@@ -38,235 +49,260 @@ var INCLUDE_FINDER_MAIN_TYPES = [
     'polar', 'radiusAxis', 'angleAxis', 'bmap'
 ];
 
+type BrushableCoordinateSystem = Cartesian2D | Geo;
+type BrushTargetBuilderKey = 'grid' | 'geo';
+
 /**
- * [option in constructor]:
- * {
- *     Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder.
- * }
- *
- *
- * [targetInfo]:
- *
  * There can be multiple axes in a single targetInfo. Consider the case
  * of `grid` component, a targetInfo represents a grid which contains one or more
  * cartesian and one or more axes. And consider the case of parallel system,
  * which has multiple axes in a coordinate system.
- * Can be {
- *     panelId: ...,
- *     coordSys: <a representitive cartesian in grid (first cartesian by default)>,
- *     coordSyses: all cartesians.
- *     gridModel: <grid component>
- *     xAxes: correspond to coordSyses on index
- *     yAxes: correspond to coordSyses on index
- * }
- * or {
- *     panelId: ...,
- *     coordSys: <geo coord sys>
- *     coordSyses: [<geo coord sys>]
- *     geoModel: <geo component>
- * }
- *
- *
- * [panelOpt]:
- *
- * Make from targetInfo. Input to BrushController.
- * {
- *     panelId: ...,
- *     rect: ...
- * }
- *
- *
- * [area]:
- *
- * Generated by BrushController or user input.
- * {
- *     panelId: Used to locate coordInfo directly. If user inpput, no panelId.
- *     brushType: determine how to convert to/from coord('rect' or 'polygon' or 'lineX/Y').
- *     Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder.
- *     range: pixel range.
- *     coordRange: representitive coord range (the first one of coordRanges).
- *     coordRanges: <Array> coord ranges, used in multiple cartesian in one grid.
- * }
  */
-
-/**
- * @param {Object} option contains Index/Id/Name of xAxis/yAxis/geo/grid
- *        Each can be {number|Array.<number>}. like: {xAxisIndex: [3, 4]}
- * @param {module:echarts/model/Global} ecModel
- * @param {Object} [opt]
- * @param {Array.<string>} [opt.include] include coordinate system types.
- */
-function BrushTargetManager(option, ecModel, opt) {
-    /**
-     * @private
-     * @type {Array.<Object>}
-     */
-    var targetInfoList = this._targetInfoList = [];
-    var info = {};
-    var foundCpts = parseFinder(ecModel, option);
-
-    each(targetInfoBuilders, function (builder, type) {
-        if (!opt || !opt.include || indexOf(opt.include, type) >= 0) {
-            builder(foundCpts, targetInfoList, info);
-        }
-    });
+interface BrushTargetInfo {
+    panelId: string;
+    coordSysModel: CoordinateSystemMaster['model'];
+    // Use the first one as the representitive coordSys.
+    // A representitive cartesian in grid (first cartesian by default).
+    coordSys: BrushableCoordinateSystem;
+    // All cartesians.
+    coordSyses: BrushableCoordinateSystem[];
+    getPanelRect: GetPanelRect,
+}
+export interface BrushTargetInfoCartesian2D extends BrushTargetInfo {
+    gridModel: GridModel;
+    coordSys: Cartesian2D;
+    coordSyses: Cartesian2D[];
+    xAxisDeclared: boolean;
+    yAxisDeclared: boolean;
 }
+export interface BrushTargetInfoGeo extends BrushTargetInfo {
+    geoModel: GeoModel,
+    coordSysModel: GeoModel,
+    coordSys: Geo,
+    coordSyses: Geo[],
+}
+type GetPanelRect = () => graphic.BoundingRect;
 
-var proto = BrushTargetManager.prototype;
-
-proto.setOutputRanges = function (areas, ecModel) {
-    this.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {
-        (area.coordRanges || (area.coordRanges = [])).push(coordRange);
-        // area.coordRange is the first of area.coordRanges
-        if (!area.coordRange) {
-            area.coordRange = coordRange;
-            // In 'category' axis, coord to pixel is not reversible, so we can not
-            // rebuild range by coordRange accrately, which may bring trouble when
-            // brushing only one item. So we use __rangeOffset to rebuilding range
-            // by coordRange. And this it only used in brush component so it is no
-            // need to be adapted to coordRanges.
-            var result = coordConvert[area.brushType](0, coordSys, coordRange);
-            area.__rangeOffset = {
-                offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]),
-                xyMinMax: result.xyMinMax
-            };
-        }
-    });
-};
 
-proto.matchOutputRanges = function (areas, ecModel, cb) {
-    each(areas, function (area) {
-        var targetInfo = this.findTargetInfo(area, ecModel);
+class BrushTargetManager {
 
-        if (targetInfo && targetInfo !== true) {
-            zrUtil.each(
-                targetInfo.coordSyses,
-                function (coordSys) {
-                    var result = coordConvert[area.brushType](1, coordSys, area.range);
-                    cb(area, result.values, coordSys, ecModel);
-                }
-            );
-        }
-    }, this);
-};
+    private _targetInfoList: BrushTargetInfo[] = [];
 
-proto.setInputRanges = function (areas, ecModel) {
-    each(areas, function (area) {
-        var targetInfo = this.findTargetInfo(area, ecModel);
+    /**
+     * @param finder contains Index/Id/Name of xAxis/yAxis/geo/grid
+     *        Each can be {number|Array.<number>}. like: {xAxisIndex: [3, 4]}
+     * @param opt.include include coordinate system types.
+     */
+    constructor(
+        finder: ModelFinderObject,
+        ecModel: GlobalModel,
+        opt?: {include?: BrushTargetBuilderKey[]}
+    ) {
+        var foundCpts = parseFinder(ecModel, finder);
+
+        each(targetInfoBuilders, (builder, type) => {
+            if (!opt || !opt.include || indexOf(opt.include, type) >= 0) {
+                builder(foundCpts, this._targetInfoList);
+            }
+        });
+    }
 
-        if (__DEV__) {
-            zrUtil.assert(
-                !targetInfo || targetInfo === true || area.coordRange,
-                'coordRange must be specified when coord index specified.'
-            );
-            zrUtil.assert(
-                !targetInfo || targetInfo !== true || area.range,
-                'range must be specified in global brush.'
-            );
-        }
+    setOutputRanges(
+        areas: BrushControllerEvents['brush']['areas'],
+        ecModel: GlobalModel
+    ): BrushAreaParam[] {
+        this.matchOutputRanges(areas, ecModel, function (
+            area: BrushAreaParam,
+            coordRange: ReturnType<ConvertCoord>['values'],
+            coordSys: BrushableCoordinateSystem,
+        ) {
+            (area.coordRanges || (area.coordRanges = [])).push(coordRange);
+            // area.coordRange is the first of area.coordRanges
+            if (!area.coordRange) {
+                area.coordRange = coordRange;
+                // In 'category' axis, coord to pixel is not reversible, so we can not
+                // rebuild range by coordRange accrately, which may bring trouble when
+                // brushing only one item. So we use __rangeOffset to rebuilding range
+                // by coordRange. And this it only used in brush component so it is no
+                // need to be adapted to coordRanges.
+                var result = coordConvert[area.brushType](0, coordSys, coordRange);
+                area.__rangeOffset = {
+                    offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]),
+                    xyMinMax: result.xyMinMax
+                };
+            }
+        });
+        return areas;
+    }
 
-        area.range = area.range || [];
-
-        // convert coordRange to global range and set panelId.
-        if (targetInfo && targetInfo !== true) {
-            area.panelId = targetInfo.panelId;
-            // (1) area.range shoule always be calculate from coordRange but does
-            // not keep its original value, for the sake of the dataZoom scenario,
-            // where area.coordRange remains unchanged but area.range may be changed.
-            // (2) Only support converting one coordRange to pixel range in brush
-            // component. So do not consider `coordRanges`.
-            // (3) About __rangeOffset, see comment above.
-            var result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange);
-            var rangeOffset = area.__rangeOffset;
-            area.range = rangeOffset
-                ? diffProcessor[area.brushType](
-                    result.values,
-                    rangeOffset.offset,
-                    getScales(result.xyMinMax, rangeOffset.xyMinMax)
-                )
-                : result.values;
+    matchOutputRanges<T extends (
+        Parameters<BrushTargetManager['findTargetInfo']>[0] & {
+            brushType: BrushType;
+            range: BrushAreaRange;
         }
-    }, this);
-};
-
-proto.makePanelOpts = function (api, getDefaultBrushType) {
-    return zrUtil.map(this._targetInfoList, function (targetInfo) {
-        var rect = targetInfo.getPanelRect();
-        return {
-            panelId: targetInfo.panelId,
-            defaultBrushType: getDefaultBrushType && getDefaultBrushType(targetInfo),
-            clipPath: brushHelper.makeRectPanelClipPath(rect),
-            isTargetByCursor: brushHelper.makeRectIsTargetByCursor(
-                rect, api, targetInfo.coordSysModel
-            ),
-            getLinearBrushOtherExtent: brushHelper.makeLinearBrushOtherExtent(rect)
-        };
-    });
-};
+    )>(
+        areas: T[],
+        ecModel: GlobalModel,
+        cb: (
+            area: T,
+            coordRange: ReturnType<ConvertCoord>['values'],
+            coordSys: BrushableCoordinateSystem,
+            ecModel: GlobalModel
+        ) => void
+    ) {
+        each(areas, function (area) {
+            var targetInfo = this.findTargetInfo(area, ecModel);
+
+            if (targetInfo && targetInfo !== true) {
+                each(
+                    targetInfo.coordSyses,
+                    function (coordSys) {
+                        var result = coordConvert[area.brushType](1, coordSys, area.range);
+                        cb(area, result.values, coordSys, ecModel);
+                    }
+                );
+            }
+        }, this);
+    }
 
-proto.controlSeries = function (area, seriesModel, ecModel) {
-    // Check whether area is bound in coord, and series do not belong to that coord.
-    // If do not do this check, some brush (like lineX) will controll all axes.
-    var targetInfo = this.findTargetInfo(area, ecModel);
-    return targetInfo === true || (
-        targetInfo && indexOf(targetInfo.coordSyses, seriesModel.coordinateSystem) >= 0
-    );
-};
+    /**
+     * the `areas` is `BrushModel.areas`.
+     * Called in layout stage.
+     * convert `area.coordRange` to global range and set panelId to `area.range`.
+     */
+    setInputRanges(
+        areas: BrushAreaParamInternal[],
+        ecModel: GlobalModel
+    ): void {
+        each(areas, function (area) {
+            var targetInfo = this.findTargetInfo(area, ecModel);
+
+            if (__DEV__) {
+                assert(
+                    !targetInfo || targetInfo === true || area.coordRange,
+                    'coordRange must be specified when coord index specified.'
+                );
+                assert(
+                    !targetInfo || targetInfo !== true || area.range,
+                    'range must be specified in global brush.'
+                );
+            }
 
-/**
- * If return Object, a coord found.
- * If reutrn true, global found.
- * Otherwise nothing found.
- *
- * @param {Object} area
- * @param {Array} targetInfoList
- * @return {Object|boolean}
- */
-proto.findTargetInfo = function (area, ecModel) {
-    var targetInfoList = this._targetInfoList;
-    var foundCpts = parseFinder(ecModel, area);
-
-    for (var i = 0; i < targetInfoList.length; i++) {
-        var targetInfo = targetInfoList[i];
-        var areaPanelId = area.panelId;
-        if (areaPanelId) {
-            if (targetInfo.panelId === areaPanelId) {
-                return targetInfo;
+            area.range = area.range || [];
+
+            // convert coordRange to global range and set panelId.
+            if (targetInfo && targetInfo !== true) {
+                area.panelId = targetInfo.panelId;
+                // (1) area.range shoule always be calculate from coordRange but does
+                // not keep its original value, for the sake of the dataZoom scenario,
+                // where area.coordRange remains unchanged but area.range may be changed.
+                // (2) Only support converting one coordRange to pixel range in brush
+                // component. So do not consider `coordRanges`.
+                // (3) About __rangeOffset, see comment above.
+                var result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange);
+                var rangeOffset = area.__rangeOffset;
+                area.range = rangeOffset
+                    ? diffProcessor[area.brushType](
+                        result.values,
+                        rangeOffset.offset,
+                        getScales(result.xyMinMax, rangeOffset.xyMinMax)
+                    )
+                    : result.values;
             }
-        }
-        else {
-            for (var i = 0; i < targetInfoMatchers.length; i++) {
-                if (targetInfoMatchers[i](foundCpts, targetInfo)) {
+        }, this);
+    }
+
+    makePanelOpts(
+        api: ExtensionAPI,
+        getDefaultBrushType?: (targetInfo: BrushTargetInfo) => BrushType
+    ): BrushPanelConfig[] {
+        return map(this._targetInfoList, function (targetInfo) {
+            var rect = targetInfo.getPanelRect();
+            return {
+                panelId: targetInfo.panelId,
+                defaultBrushType: getDefaultBrushType ? getDefaultBrushType(targetInfo) : null,
+                clipPath: brushHelper.makeRectPanelClipPath(rect),
+                isTargetByCursor: brushHelper.makeRectIsTargetByCursor(
+                    rect, api, targetInfo.coordSysModel
+                ),
+                getLinearBrushOtherExtent: brushHelper.makeLinearBrushOtherExtent(rect)
+            };
+        });
+    }
+
+    controlSeries(area: BrushAreaParamInternal, seriesModel: SeriesModel, ecModel: GlobalModel): boolean {
+        // Check whether area is bound in coord, and series do not belong to that coord.
+        // If do not do this check, some brush (like lineX) will controll all axes.
+        var targetInfo = this.findTargetInfo(area, ecModel);
+        return targetInfo === true || (
+            targetInfo && indexOf(
+                targetInfo.coordSyses, seriesModel.coordinateSystem as BrushableCoordinateSystem
+            ) >= 0
+        );
+    }
+
+    /**
+     * If return Object, a coord found.
+     * If reutrn true, global found.
+     * Otherwise nothing found.
+     */
+    findTargetInfo(
+        area: ModelFinderObject & {
+            panelId?: string
+        },
+        ecModel: GlobalModel
+    ): BrushTargetInfo | true {
+        var targetInfoList = this._targetInfoList;
+        var foundCpts = parseFinder(ecModel, area);
+
+        for (var i = 0; i < targetInfoList.length; i++) {
+            var targetInfo = targetInfoList[i];
+            var areaPanelId = area.panelId;
+            if (areaPanelId) {
+                if (targetInfo.panelId === areaPanelId) {
                     return targetInfo;
                 }
             }
+            else {
+                for (var i = 0; i < targetInfoMatchers.length; i++) {
+                    if (targetInfoMatchers[i](foundCpts, targetInfo)) {
+                        return targetInfo;
+                    }
+                }
+            }
         }
+
+        return true;
     }
 
-    return true;
-};
+}
 
-function formatMinMax(minMax) {
+function formatMinMax(minMax: BrushDimensionMinMax): BrushDimensionMinMax {
     minMax[0] > minMax[1] && minMax.reverse();
     return minMax;
 }
 
-function parseFinder(ecModel, option) {
-    return modelUtil.parseFinder(
+function parseFinder(
+    ecModel: GlobalModel, option: ModelFinder
+): ParsedModelFinder {
+    return modelUtilParseFinder(
         ecModel, option, {includeMainTypes: INCLUDE_FINDER_MAIN_TYPES}
     );
 }
 
-var targetInfoBuilders = {
+type TargetInfoBuilder = (
+    foundCpts: ParsedModelFinder, targetInfoList: BrushTargetInfo[]
+) => void;
+var targetInfoBuilders: Record<BrushTargetBuilderKey, TargetInfoBuilder> = {
 
     grid: function (foundCpts, targetInfoList) {
         var xAxisModels = foundCpts.xAxisModels;
         var yAxisModels = foundCpts.yAxisModels;
         var gridModels = foundCpts.gridModels;
         // Remove duplicated.
-        var gridModelMap = zrUtil.createHashMap();
-        var xAxesHas = {};
-        var yAxesHas = {};
+        var gridModelMap = createHashMap<GridModel>();
+        var xAxesHas = {} as Dictionary<boolean>;
+        var yAxesHas = {} as Dictionary<boolean>;
 
         if (!xAxisModels && !yAxisModels && !gridModels) {
             return;
@@ -290,7 +326,7 @@ var targetInfoBuilders = {
 
         gridModelMap.each(function (gridModel) {
             var grid = gridModel.coordinateSystem;
-            var cartesians = [];
+            var cartesians = [] as Cartesian2D[];
 
             each(grid.getCartesians(), function (cartesian, index) {
                 if (indexOf(xAxisModels, cartesian.getAxis('x').model) >= 0
@@ -306,15 +342,15 @@ var targetInfoBuilders = {
                 // Use the first one as the representitive coordSys.
                 coordSys: cartesians[0],
                 coordSyses: cartesians,
-                getPanelRect: panelRectBuilder.grid,
+                getPanelRect: panelRectBuilders.grid,
                 xAxisDeclared: xAxesHas[gridModel.id],
                 yAxisDeclared: yAxesHas[gridModel.id]
-            });
+            } as BrushTargetInfoCartesian2D);
         });
     },
 
     geo: function (foundCpts, targetInfoList) {
-        each(foundCpts.geoModels, function (geoModel) {
+        each(foundCpts.geoModels, function (geoModel: GeoModel) {
             var coordSys = geoModel.coordinateSystem;
             targetInfoList.push({
                 panelId: 'geo--' + geoModel.id,
@@ -322,13 +358,16 @@ var targetInfoBuilders = {
                 coordSysModel: geoModel,
                 coordSys: coordSys,
                 coordSyses: [coordSys],
-                getPanelRect: panelRectBuilder.geo
-            });
+                getPanelRect: panelRectBuilders.geo
+            } as BrushTargetInfoGeo);
         });
     }
 };
 
-var targetInfoMatchers = [
+type TargetInfoMatcher = (
+    foundCpts: ParsedModelFinder, targetInfo: BrushTargetInfo
+) => boolean;
+var targetInfoMatchers: TargetInfoMatcher[] = [
 
     // grid
     function (foundCpts, targetInfo) {
@@ -339,24 +378,25 @@ var targetInfoMatchers = [
         !gridModel && xAxisModel && (gridModel = xAxisModel.axis.grid.model);
         !gridModel && yAxisModel && (gridModel = yAxisModel.axis.grid.model);
 
-        return gridModel && gridModel === targetInfo.gridModel;
+        return gridModel && gridModel === (targetInfo as BrushTargetInfoCartesian2D).gridModel;
     },
 
     // geo
     function (foundCpts, targetInfo) {
         var geoModel = foundCpts.geoModel;
-        return geoModel && geoModel === targetInfo.geoModel;
+        return geoModel && geoModel === (targetInfo as BrushTargetInfoGeo).geoModel;
     }
 ];
 
-var panelRectBuilder = {
+type PanelRectBuilder = (this: BrushTargetInfo) => graphic.BoundingRect;
+var panelRectBuilders: Record<BrushTargetBuilderKey, PanelRectBuilder> = {
 
-    grid: function () {
+    grid: function (this: BrushTargetInfoCartesian2D) {
         // grid is not Transformable.
         return this.coordSys.grid.getRect().clone();
     },
 
-    geo: function () {
+    geo: function (this: BrushTargetInfoGeo) {
         var coordSys = this.coordSys;
         var rect = coordSys.getBoundingRect().clone();
         // geo roam and zoom transform
@@ -365,13 +405,24 @@ var panelRectBuilder = {
     }
 };
 
-var coordConvert = {
+type ConvertCoord = (
+    to: COORD_CONVERTS_INDEX,
+    coordSys: BrushableCoordinateSystem,
+    rangeOrCoordRange: BrushAreaRange
+) => {
+    values: BrushAreaRange,
+    xyMinMax: BrushDimensionMinMax[]
+};
+var coordConvert: Record<BrushType, ConvertCoord> = {
 
     lineX: curry(axisConvert, 0),
 
     lineY: curry(axisConvert, 1),
 
-    rect: function (to, coordSys, rangeOrCoordRange) {
+    rect: function (to, coordSys, rangeOrCoordRange: BrushDimensionMinMax[]): {
+        values: BrushDimensionMinMax[],
+        xyMinMax: BrushDimensionMinMax[]
+    } {
         var xminymin = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]]);
         var xmaxymax = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]]);
         var values = [
@@ -381,9 +432,12 @@ var coordConvert = {
         return {values: values, xyMinMax: values};
     },
 
-    polygon: function (to, coordSys, rangeOrCoordRange) {
+    polygon: function (to, coordSys, rangeOrCoordRange: BrushDimensionMinMax[]): {
+        values: BrushDimensionMinMax[],
+        xyMinMax: BrushDimensionMinMax[]
+    } {
         var xyMinMax = [[Infinity, -Infinity], [Infinity, -Infinity]];
-        var values = zrUtil.map(rangeOrCoordRange, function (item) {
+        var values = map(rangeOrCoordRange, function (item) {
             var p = coordSys[COORD_CONVERTS[to]](item);
             xyMinMax[0][0] = Math.min(xyMinMax[0][0], p[0]);
             xyMinMax[1][0] = Math.min(xyMinMax[1][0], p[1]);
@@ -395,16 +449,24 @@ var coordConvert = {
     }
 };
 
-function axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) {
+function axisConvert(
+    axisNameIndex: 0 | 1,
+    to: COORD_CONVERTS_INDEX,
+    coordSys: Cartesian2D,
+    rangeOrCoordRange: BrushDimensionMinMax
+): {
+    values: BrushDimensionMinMax,
+    xyMinMax: BrushDimensionMinMax[]
+} {
     if (__DEV__) {
-        zrUtil.assert(
+        assert(
             coordSys.type === 'cartesian2d',
             'lineX/lineY brush is available only in cartesian2d.'
         );
     }
 
     var axis = coordSys.getAxis(['x', 'y'][axisNameIndex]);
-    var values = formatMinMax(zrUtil.map([0, 1], function (i) {
+    var values = formatMinMax(map([0, 1], function (i) {
         return to
             ? axis.coordToData(axis.toLocalCoord(rangeOrCoordRange[i]))
             : axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i]));
@@ -416,26 +478,43 @@ function axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) {
     return {values: values, xyMinMax: xyMinMax};
 }
 
-var diffProcessor = {
+
+type DiffProcess = (
+    values: BrushDimensionMinMax | BrushDimensionMinMax[],
+    refer: BrushDimensionMinMax | BrushDimensionMinMax[],
+    scales: ReturnType<typeof getScales>
+) => BrushDimensionMinMax | BrushDimensionMinMax[];
+
+var diffProcessor: Record<BrushType, DiffProcess> = {
+
     lineX: curry(axisDiffProcessor, 0),
 
     lineY: curry(axisDiffProcessor, 1),
 
-    rect: function (values, refer, scales) {
+    rect: function (
+        values: BrushDimensionMinMax[], refer: BrushDimensionMinMax[], scales: ReturnType<typeof getScales>
+    ): BrushDimensionMinMax[] {
         return [
             [values[0][0] - scales[0] * refer[0][0], values[0][1] - scales[0] * refer[0][1]],
             [values[1][0] - scales[1] * refer[1][0], values[1][1] - scales[1] * refer[1][1]]
         ];
     },
 
-    polygon: function (values, refer, scales) {
-        return zrUtil.map(values, function (item, idx) {
+    polygon: function (
+        values: BrushDimensionMinMax[], refer: BrushDimensionMinMax[], scales: ReturnType<typeof getScales>
+    ): BrushDimensionMinMax[] {
+        return map(values, function (item, idx) {
             return [item[0] - scales[0] * refer[idx][0], item[1] - scales[1] * refer[idx][1]];
         });
     }
 };
 
-function axisDiffProcessor(axisNameIndex, values, refer, scales) {
+function axisDiffProcessor(
+    axisNameIndex: 0 | 1,
+    values: BrushDimensionMinMax,
+    refer: BrushDimensionMinMax,
+    scales: ReturnType<typeof getScales>
+): BrushDimensionMinMax {
     return [
         values[0] - scales[axisNameIndex] * refer[0],
         values[1] - scales[axisNameIndex] * refer[1]
@@ -444,7 +523,8 @@ function axisDiffProcessor(axisNameIndex, values, refer, scales) {
 
 // We have to process scale caused by dataZoom manually,
 // although it might be not accurate.
-function getScales(xyMinMaxCurr, xyMinMaxOrigin) {
+// Return [0~1, 0~1]
+function getScales(xyMinMaxCurr: BrushDimensionMinMax[], xyMinMaxOrigin: BrushDimensionMinMax[]): number[] {
     var sizeCurr = getSize(xyMinMaxCurr);
     var sizeOrigin = getSize(xyMinMaxOrigin);
     var scales = [sizeCurr[0] / sizeOrigin[0], sizeCurr[1] / sizeOrigin[1]];
@@ -453,10 +533,10 @@ function getScales(xyMinMaxCurr, xyMinMaxOrigin) {
     return scales;
 }
 
-function getSize(xyMinMax) {
+function getSize(xyMinMax: BrushDimensionMinMax[]): number[] {
     return xyMinMax
         ? [xyMinMax[0][1] - xyMinMax[0][0], xyMinMax[1][1] - xyMinMax[1][0]]
         : [NaN, NaN];
 }
 
-export default BrushTargetManager;
\ No newline at end of file
+export default BrushTargetManager;
diff --git a/src/component/helper/brushHelper.ts b/src/component/helper/brushHelper.ts
index 8166855..6819251 100644
--- a/src/component/helper/brushHelper.ts
+++ b/src/component/helper/brushHelper.ts
@@ -17,22 +17,24 @@
 * under the License.
 */
 
-// @ts-nocheck
 
-import BoundingRect from 'zrender/src/core/BoundingRect';
+import BoundingRect, { RectLike } from 'zrender/src/core/BoundingRect';
 import {onIrrelevantElement} from './cursorHelper';
 import * as graphicUtil from '../../util/graphic';
+import ExtensionAPI from '../../ExtensionAPI';
+import { ElementEvent } from 'zrender/src/Element';
+import ComponentModel from '../../model/Component';
 
-export function makeRectPanelClipPath(rect) {
+export function makeRectPanelClipPath(rect: RectLike) {
     rect = normalizeRect(rect);
-    return function (localPoints, transform) {
+    return function (localPoints: number[][]) {
         return graphicUtil.clipPointsByRect(localPoints, rect);
     };
 }
 
-export function makeLinearBrushOtherExtent(rect, specifiedXYIndex) {
+export function makeLinearBrushOtherExtent(rect: RectLike, specifiedXYIndex?: 0 | 1) {
     rect = normalizeRect(rect);
-    return function (xyIndex) {
+    return function (xyIndex: 0 | 1) {
         var idx = specifiedXYIndex != null ? specifiedXYIndex : xyIndex;
         var brushWidth = idx ? rect.width : rect.height;
         var base = idx ? rect.x : rect.y;
@@ -40,16 +42,16 @@ export function makeLinearBrushOtherExtent(rect, specifiedXYIndex) {
     };
 }
 
-export function makeRectIsTargetByCursor(rect, api, targetModel) {
-    rect = normalizeRect(rect);
-    return function (e, localCursorPoint, transform) {
-        return rect.contain(localCursorPoint[0], localCursorPoint[1])
+export function makeRectIsTargetByCursor(rect: RectLike, api: ExtensionAPI, targetModel: ComponentModel) {
+    var boundingRect = normalizeRect(rect);
+    return function (e: ElementEvent, localCursorPoint: number[]) {
+        return boundingRect.contain(localCursorPoint[0], localCursorPoint[1])
             && !onIrrelevantElement(e, api, targetModel);
     };
 }
 
 // Consider width/height is negative.
-function normalizeRect(rect) {
+function normalizeRect(rect: RectLike): BoundingRect {
     return BoundingRect.create(rect);
 }
 
diff --git a/src/component/helper/cursorHelper.ts b/src/component/helper/cursorHelper.ts
index 4bde807..99c087d 100644
--- a/src/component/helper/cursorHelper.ts
+++ b/src/component/helper/cursorHelper.ts
@@ -17,7 +17,11 @@
 * under the License.
 */
 
-// @ts-nocheck
+
+import { ElementEvent } from 'zrender/src/Element';
+import ExtensionAPI from '../../ExtensionAPI';
+import SeriesModel from '../../model/Series';
+import { CoordinateSystem } from '../../coord/CoordinateSystem';
 
 var IRRELEVANT_EXCLUDES = {'axisPointer': 1, 'tooltip': 1, 'brush': 1};
 
@@ -25,12 +29,14 @@ var IRRELEVANT_EXCLUDES = {'axisPointer': 1, 'tooltip': 1, 'brush': 1};
  * Avoid that: mouse click on a elements that is over geo or graph,
  * but roam is triggered.
  */
-export function onIrrelevantElement(e, api, targetCoordSysModel) {
+export function onIrrelevantElement(
+    e: ElementEvent, api: ExtensionAPI, targetCoordSysModel: CoordinateSystem['model']
+): boolean {
     var model = api.getComponentByElement(e.topTarget);
     // If model is axisModel, it works only if it is injected with coordinateSystem.
-    var coordSys = model && model.coordinateSystem;
+    var coordSys = model && (model as SeriesModel).coordinateSystem;
     return model
         && model !== targetCoordSysModel
-        && !IRRELEVANT_EXCLUDES[model.mainType]
+        && !IRRELEVANT_EXCLUDES.hasOwnProperty(model.mainType)
         && (coordSys && coordSys.model !== targetCoordSysModel);
 }
diff --git a/src/component/parallel.ts b/src/component/parallel.ts
index cea1a7d..2835985 100644
--- a/src/component/parallel.ts
+++ b/src/component/parallel.ts
@@ -17,31 +17,52 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import * as echarts from '../echarts';
 import * as zrUtil from 'zrender/src/core/util';
 import * as throttleUtil from '../util/throttle';
 import parallelPreprocessor from '../coord/parallel/parallelPreprocessor';
-
 import '../coord/parallel/parallelCreator';
 import '../coord/parallel/ParallelModel';
 import './parallelAxis';
+import GlobalModel from '../model/Global';
+import ParallelModel, { ParallelCoordinateSystemOption } from '../coord/parallel/ParallelModel';
+import ExtensionAPI from '../ExtensionAPI';
+import ComponentView from '../view/Component';
+import { ElementEventName } from 'zrender/src/core/types';
+import { ElementEvent } from 'zrender/src/Element';
+import { ParallelAxisExpandPayload } from './axis/parallelAxisAction';
+
 
 var CLICK_THRESHOLD = 5; // > 4
 
-// Parallel view
-echarts.extendComponentView({
-    type: 'parallel',
 
-    render: function (parallelModel, ecModel, api) {
+class ParallelView extends ComponentView {
+
+    static type = 'parallel';
+    readonly type = ParallelView.type;
+
+    // @internal
+    _model: ParallelModel;
+    private _api: ExtensionAPI;
+
+    // @internal
+    _mouseDownPoint: number[];
+
+    private _handlers: Partial<Record<ElementEventName, ElementEventHandler>>;
+
+
+    render(parallelModel: ParallelModel, ecModel: GlobalModel, api: ExtensionAPI): void {
         this._model = parallelModel;
         this._api = api;
 
         if (!this._handlers) {
             this._handlers = {};
-            zrUtil.each(handlers, function (handler, eventName) {
-                api.getZr().on(eventName, this._handlers[eventName] = zrUtil.bind(handler, this));
+            zrUtil.each(handlers, function (handler: ElementEventHandler, eventName) {
+                api.getZr().on(
+                    eventName,
+                    this._handlers[eventName] = zrUtil.bind(handler, this) as ElementEventHandler
+                );
             }, this);
         }
 
@@ -51,31 +72,38 @@ echarts.extendComponentView({
             parallelModel.get('axisExpandRate'),
             'fixRate'
         );
-    },
+    }
 
-    dispose: function (ecModel, api) {
-        zrUtil.each(this._handlers, function (handler, eventName) {
+    dispose(ecModel: GlobalModel, api: ExtensionAPI): void {
+        zrUtil.each(this._handlers, function (handler: ElementEventHandler, eventName) {
             api.getZr().off(eventName, handler);
         });
         this._handlers = null;
-    },
+    }
 
     /**
+     * @internal
      * @param {Object} [opt] If null, cancle the last action triggering for debounce.
      */
-    _throttledDispatchExpand: function (opt) {
+    _throttledDispatchExpand(this: ParallelView, opt: Omit<ParallelAxisExpandPayload, 'type'>): void {
         this._dispatchExpand(opt);
-    },
+    }
 
-    _dispatchExpand: function (opt) {
+    /**
+     * @internal
+     */
+    _dispatchExpand(opt: Omit<ParallelAxisExpandPayload, 'type'>) {
         opt && this._api.dispatchAction(
             zrUtil.extend({type: 'parallelAxisExpand'}, opt)
         );
     }
 
-});
+}
+
+ComponentView.registerClass(ParallelView);
 
-var handlers = {
+type ElementEventHandler = (this: ParallelView, e: ElementEvent) => void;
+var handlers: Partial<Record<ElementEventName, ElementEventHandler>> = {
 
     mousedown: function (e) {
         if (checkTrigger(this, 'click')) {
@@ -118,7 +146,9 @@ var handlers = {
         );
 
         var behavior = result.behavior;
-        behavior === 'jump' && this._throttledDispatchExpand.debounceNextCall(model.get('axisExpandDebounce'));
+        behavior === 'jump' && (
+            this._throttledDispatchExpand as ParallelView['_throttledDispatchExpand'] & throttleUtil.ThrottleController
+        ).debounceNextCall(model.get('axisExpandDebounce'));
         this._throttledDispatchExpand(
             behavior === 'none'
                 ? null // Cancle the last trigger, in case that mouse slide out of the area quickly.
@@ -131,7 +161,10 @@ var handlers = {
     }
 };
 
-function checkTrigger(view, triggerOn) {
+function checkTrigger(
+    view: ParallelView,
+    triggerOn: ParallelCoordinateSystemOption['axisExpandTriggerOn']
+): boolean {
     var model = view._model;
     return model.get('axisExpandable') && model.get('axisExpandTriggerOn') === triggerOn;
 }
diff --git a/src/component/toolbox/feature/Brush.ts b/src/component/toolbox/feature/Brush.ts
index a4d1589..88016c1 100644
--- a/src/component/toolbox/feature/Brush.ts
+++ b/src/component/toolbox/feature/Brush.ts
@@ -27,6 +27,8 @@ import {
 } from '../featureManager';
 import GlobalModel from '../../../model/Global';
 import ExtensionAPI from '../../../ExtensionAPI';
+import BrushModel from '../../brush/BrushModel';
+import { BrushTypeUncertain } from '../../helper/BrushController';
 
 var brushLang = lang.toolbox.brush;
 
@@ -42,7 +44,7 @@ interface ToolboxBrushFeatureOption extends ToolboxFeatureOption {
 
 class BrushFeature extends ToolboxFeature<ToolboxBrushFeatureOption> {
 
-    private _brushType: string
+    private _brushType: BrushTypeUncertain
     private _brushMode: string
 
     render(
@@ -50,17 +52,14 @@ class BrushFeature extends ToolboxFeature<ToolboxBrushFeatureOption> {
         ecModel: GlobalModel,
         api: ExtensionAPI
     ) {
-        var brushType: string;
+        var brushType: BrushTypeUncertain;
         var brushMode: string;
         var isBrushed: boolean;
 
-        ecModel.eachComponent({mainType: 'brush'}, function (brushModel) {
-            // @ts-ignore BrushModel
+        ecModel.eachComponent({mainType: 'brush'}, function (brushModel: BrushModel) {
             brushType = brushModel.brushType;
-            // @ts-ignore BrushModel
             brushMode = brushModel.brushOption.brushMode || 'single';
-            // @ts-ignore BrushModel
-            isBrushed = isBrushed || brushModel.areas.length;
+            isBrushed = isBrushed || !!brushModel.areas.length;
         });
         this._brushType = brushType;
         this._brushMode = brushMode;
diff --git a/src/component/toolbox/feature/DataZoom.ts b/src/component/toolbox/feature/DataZoom.ts
index e68e9f7..8e4a318 100644
--- a/src/component/toolbox/feature/DataZoom.ts
+++ b/src/component/toolbox/feature/DataZoom.ts
@@ -17,13 +17,12 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 // TODO depends on DataZoom and Brush
 import * as echarts from '../../../echarts';
 import * as zrUtil from 'zrender/src/core/util';
-import BrushController from '../../helper/BrushController';
-import BrushTargetManager from '../../helper/BrushTargetManager';
+import BrushController, { BrushControllerEvents, BrushDimensionMinMax } from '../../helper/BrushController';
+import BrushTargetManager, { BrushTargetInfoCartesian2D } from '../../helper/BrushTargetManager';
 import * as history from '../../dataZoom/history';
 import sliderMove from '../../helper/sliderMove';
 import lang from '../../../lang';
@@ -37,6 +36,13 @@ import {
 } from '../featureManager';
 import GlobalModel from '../../../model/Global';
 import ExtensionAPI from '../../../ExtensionAPI';
+import { Payload, ECUnitOption, Dictionary } from '../../../util/types';
+import Cartesian2D from '../../../coord/cartesian/Cartesian2D';
+import CartesianAxisModel from '../../../coord/cartesian/AxisModel';
+import DataZoomModel from '../../dataZoom/DataZoomModel';
+import { DataZoomPayloadBatchItem } from '../../dataZoom/helper';
+import { ModelFinderObject, ModelFinderIndexQuery } from '../../../util/model';
+import { ToolboxOption } from '../ToolboxModel';
 
 var dataZoomLang = lang.toolbox.dataZoom;
 var each = zrUtil.each;
@@ -53,6 +59,9 @@ interface ToolboxDataZoomFeatureOption extends ToolboxFeatureOption {
     title?: {[key in IconType]?: string}
     // TODO: TYPE Use type in dataZoom
     filterMode?: 'filter' | 'weakFilter' | 'empty' | 'none'
+    // Backward compat: false means 'none'
+    xAxisIndex?: ModelFinderIndexQuery | false
+    yAxisIndex?: ModelFinderIndexQuery | false
 }
 
 type ToolboxDataZoomFeatureModel = ToolboxFeatureModel<ToolboxDataZoomFeatureOption>
@@ -67,7 +76,7 @@ class DataZoomFeature extends ToolboxFeature<ToolboxDataZoomFeatureOption> {
         featureModel: ToolboxDataZoomFeatureModel,
         ecModel: GlobalModel,
         api: ExtensionAPI,
-        payload
+        payload: Payload
     ) {
         if (!this.brushController) {
             this.brushController = new BrushController(api.getZr());
@@ -100,11 +109,12 @@ class DataZoomFeature extends ToolboxFeature<ToolboxDataZoomFeatureOption> {
         this.brushController.dispose();
     }
 
-    private _onBrush(areas, opt) {
-        if (!opt.isEnd || !areas.length) {
+    private _onBrush(eventParam: BrushControllerEvents['brush']): void {
+        var areas = eventParam.areas;
+        if (!eventParam.isEnd || !areas.length) {
             return;
         }
-        var snapshot = {};
+        var snapshot: history.DataZoomStoreSnapshot = {};
         var ecModel = this.ecModel;
 
         this.brushController.updateCovers([]); // remove cover
@@ -112,20 +122,22 @@ class DataZoomFeature extends ToolboxFeature<ToolboxDataZoomFeatureOption> {
         var brushTargetManager = new BrushTargetManager(
             retrieveAxisSetting(this.model.option), ecModel, {include: ['grid']}
         );
-        brushTargetManager.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {
+        brushTargetManager.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys: Cartesian2D) {
             if (coordSys.type !== 'cartesian2d') {
                 return;
             }
 
             var brushType = area.brushType;
             if (brushType === 'rect') {
-                setBatch('x', coordSys, coordRange[0]);
-                setBatch('y', coordSys, coordRange[1]);
+                setBatch('x', coordSys, (coordRange as BrushDimensionMinMax[])[0]);
+                setBatch('y', coordSys, (coordRange as BrushDimensionMinMax[])[1]);
             }
             else {
-                setBatch(({
-                    lineX: 'x', lineY: 'y'
-                })[brushType], coordSys, coordRange);
+                setBatch(
+                    ({lineX: 'x', lineY: 'y'})[brushType as 'lineX' | 'lineY'],
+                    coordSys,
+                    coordRange as BrushDimensionMinMax
+                );
             }
         });
 
@@ -133,7 +145,7 @@ class DataZoomFeature extends ToolboxFeature<ToolboxDataZoomFeatureOption> {
 
         this._dispatchZoomAction(snapshot);
 
-        function setBatch(dimName: string, coordSys, minMax: number[]) {
+        function setBatch(dimName: string, coordSys: Cartesian2D, minMax: number[]) {
             var axis = coordSys.getAxis(dimName);
             var axisModel = axis.model;
             var dataZoomModel = findDataZoom(dimName, axisModel, ecModel);
@@ -154,9 +166,9 @@ class DataZoomFeature extends ToolboxFeature<ToolboxDataZoomFeatureOption> {
             });
         }
 
-        function findDataZoom(dimName: string, axisModel, ecModel: GlobalModel) {
+        function findDataZoom(dimName: string, axisModel: CartesianAxisModel, ecModel: GlobalModel): DataZoomModel {
             var found;
-            ecModel.eachComponent({mainType: 'dataZoom', subType: 'select'}, function (dzModel) {
+            ecModel.eachComponent({mainType: 'dataZoom', subType: 'select'}, function (dzModel: DataZoomModel) {
                 var has = dzModel.getAxisModel(dimName, axisModel.componentIndex);
                 has && (found = dzModel);
             });
@@ -164,8 +176,11 @@ class DataZoomFeature extends ToolboxFeature<ToolboxDataZoomFeatureOption> {
         }
     };
 
-    dispatchZoomAction(snapshot) {
-        var batch = [];
+    /**
+     * @internal
+     */
+    _dispatchZoomAction(snapshot: history.DataZoomStoreSnapshot): void {
+        var batch: DataZoomPayloadBatchItem[] = [];
 
         // Convert from hash map to array.
         each(snapshot, function (batchItem, dataZoomId) {
@@ -204,18 +219,19 @@ const handlers: { [key in IconType]: (this: DataZoomFeature) => void } = {
     },
 
     back: function () {
-        this.dispatchZoomAction(history.pop(this.ecModel));
+        this._dispatchZoomAction(history.pop(this.ecModel));
     }
 };
 
 
-function retrieveAxisSetting(option) {
-    var setting = {};
+function retrieveAxisSetting(option: ToolboxDataZoomFeatureOption): ModelFinderObject {
+    var setting = {} as ModelFinderObject;
     // Compatible with previous setting: null => all axis, false => no axis.
-    zrUtil.each(['xAxisIndex', 'yAxisIndex'], function (name) {
-        setting[name] = option[name];
-        setting[name] == null && (setting[name] = 'all');
-        (setting[name] === false || setting[name] === 'none') && (setting[name] = []);
+    zrUtil.each(['xAxisIndex', 'yAxisIndex'] as const, function (name) {
+        var val = option[name];
+        val == null && (val = 'all');
+        (val === false || val === 'none') && (val = []);
+        setting[name] = val;
     });
     return setting;
 }
@@ -234,7 +250,7 @@ function updateZoomBtnStatus(
     featureModel: ToolboxDataZoomFeatureModel,
     ecModel: GlobalModel,
     view: DataZoomFeature,
-    payload,
+    payload: Payload,
     api: ExtensionAPI
 ) {
     var zoomActive = view.isZoomActive;
@@ -253,7 +269,7 @@ function updateZoomBtnStatus(
     );
 
     view.brushController
-        .setPanels(brushTargetManager.makePanelOpts(api, function (targetInfo) {
+        .setPanels(brushTargetManager.makePanelOpts(api, function (targetInfo: BrushTargetInfoCartesian2D) {
             return (targetInfo.xAxisDeclared && !targetInfo.yAxisDeclared)
                 ? 'lineX'
                 : (!targetInfo.xAxisDeclared && targetInfo.yAxisDeclared)
@@ -280,7 +296,7 @@ registerFeature('dataZoom', DataZoomFeature);
 
 // Create special dataZoom option for select
 // FIXME consider the case of merge option, where axes options are not exists.
-echarts.registerPreprocessor(function (option) {
+echarts.registerPreprocessor(function (option: ECUnitOption) {
     if (!option) {
         return;
     }
@@ -290,7 +306,7 @@ echarts.registerPreprocessor(function (option) {
         option.dataZoom = dataZoomOpts = [dataZoomOpts];
     }
 
-    var toolboxOpt = option.toolbox;
+    var toolboxOpt = option.toolbox as ToolboxOption;
     if (toolboxOpt) {
         // Assume there is only one toolbox
         if (zrUtil.isArray(toolboxOpt)) {
@@ -298,7 +314,7 @@ echarts.registerPreprocessor(function (option) {
         }
 
         if (toolboxOpt && toolboxOpt.feature) {
-            var dataZoomOpt = toolboxOpt.feature.dataZoom;
+            var dataZoomOpt = toolboxOpt.feature.dataZoom as ToolboxDataZoomFeatureOption;
             // FIXME: If add dataZoom when setOption in merge mode,
             // no axis info to be added. See `test/dataZoom-extreme.html`
             addForAxis('xAxis', dataZoomOpt);
@@ -306,13 +322,13 @@ echarts.registerPreprocessor(function (option) {
         }
     }
 
-    function addForAxis(axisName: string, dataZoomOpt) {
+    function addForAxis(axisName: 'xAxis' | 'yAxis', dataZoomOpt: ToolboxDataZoomFeatureOption): void {
         if (!dataZoomOpt) {
             return;
         }
 
         // Try not to modify model, because it is not merged yet.
-        var axisIndicesName = axisName + 'Index';
+        var axisIndicesName = axisName + 'Index' as 'xAxisIndex' | 'yAxisIndex';
         var givenAxisIndices = dataZoomOpt[axisIndicesName];
         if (givenAxisIndices != null
             && givenAxisIndices !== 'all'
@@ -321,10 +337,10 @@ echarts.registerPreprocessor(function (option) {
             givenAxisIndices = (givenAxisIndices === false || givenAxisIndices === 'none') ? [] : [givenAxisIndices];
         }
 
-        forEachComponent(axisName, function (axisOpt, axisIndex) {
+        forEachComponent(axisName, function (axisOpt: unknown, axisIndex: number) {
             if (givenAxisIndices != null
                 && givenAxisIndices !== 'all'
-                && zrUtil.indexOf(givenAxisIndices, axisIndex) === -1
+                && zrUtil.indexOf(givenAxisIndices as number[], axisIndex) === -1
             ) {
                 return;
             }
@@ -335,7 +351,7 @@ echarts.registerPreprocessor(function (option) {
                 filterMode: dataZoomOpt.filterMode || 'filter',
                 // Id for merge mapping.
                 id: DATA_ZOOM_ID_BASE + axisName + axisIndex
-            };
+            } as Dictionary<unknown>;
             // FIXME
             // Only support one axis now.
             newOpt[axisIndicesName] = axisIndex;
@@ -343,7 +359,7 @@ echarts.registerPreprocessor(function (option) {
         });
     }
 
-    function forEachComponent(mainType: string, cb) {
+    function forEachComponent(mainType: string, cb: (axisOpt: unknown, axisIndex: number) => void) {
         var opts = option[mainType];
         if (!zrUtil.isArray(opts)) {
             opts = opts ? [opts] : [];
@@ -352,4 +368,4 @@ echarts.registerPreprocessor(function (option) {
     }
 });
 
-export default DataZoomFeature;
\ No newline at end of file
+export default DataZoomFeature;
diff --git a/src/coord/CoordinateSystem.ts b/src/coord/CoordinateSystem.ts
index 96f1373..678cf21 100644
--- a/src/coord/CoordinateSystem.ts
+++ b/src/coord/CoordinateSystem.ts
@@ -35,7 +35,8 @@ export interface CoordinateSystemCreator {
     // FIXME current dimensions must be string[].
     // check and unify the definition.
     // FIXME:TS check where used (seams only HeatmapSeries used?)
-    dimensions: DimensionName[];
+    // Some coordinate system do not have static dimensions (like parallel)
+    dimensions?: DimensionName[];
 
     // dimensionsInfo like [{name: ..., type: ...}, 'xxx', ...]
     getDimensionsInfo?: () => DimensionDefinitionLoose[];
@@ -59,7 +60,7 @@ export interface CoordinateSystemMaster {
     // coodinate system is applicable to the given `finder`.
     // Each coordinate system will be tried, util one returns none
     // null/undefined value.
-    convertToPixel(
+    convertToPixel?(
         ecModel: GlobalModel, finder: ParsedModelFinder, value: ScaleDataValue | ScaleDataValue[]
     ): number | number[];
 
@@ -67,7 +68,7 @@ export interface CoordinateSystemMaster {
     // coodinate system is applicable to the given `finder`.
     // Each coordinate system will be tried, util one returns none
     // null/undefined value.
-    convertFromPixel(
+    convertFromPixel?(
         ecModel: GlobalModel, finder: ParsedModelFinder, pixelValue: number | number[]
     ): ScaleDataValue | ScaleDataValue[];
 
@@ -102,21 +103,27 @@ export interface CoordinateSystem {
 
     model?: ComponentModel;
 
-    // @param data
-    // @param reserved Defined by the coordinate system itself
-    // @param out
-    // @return {Array.<number>} point Point in global pixel coordinate system.
+    /**
+     * @param data
+     * @param reserved Defined by the coordinate system itself
+     * @param out
+     * @return {Array.<number>} point Point in global pixel coordinate system.
+     */
     dataToPoint(
         data: ScaleDataValue | ScaleDataValue[],
         reserved?: any,
         out?: number[]
     ): number[];
 
-    // @param point point Point in global pixel coordinate system.
-    // @param reserved Defined by the coordinate system itself
-    // @param out
-    // @return data
-    pointToData(
+    /**
+     * Some coord sys (like Parallel) might do not have `pointToData`,
+     * or the meaning of this kind of features is not clear yet.
+     * @param point point Point in global pixel coordinate system.
+     * @param reserved Defined by the coordinate system itself
+     * @param out
+     * @return data
+     */
+    pointToData?(
         point: number[],
         reserved?: any,
         out?: number[]
diff --git a/src/coord/cartesian/Grid.ts b/src/coord/cartesian/Grid.ts
index 288af99..5884470 100644
--- a/src/coord/cartesian/Grid.ts
+++ b/src/coord/cartesian/Grid.ts
@@ -224,7 +224,7 @@ class Grid implements CoordinateSystemMaster {
     convertToPixel(
         ecModel: GlobalModel, finder: ParsedModelFinder, value: ScaleDataValue | ScaleDataValue[]
     ): number | number[] {
-        var target = this._findConvertTarget(ecModel, finder);
+        var target = this._findConvertTarget(finder);
 
         return target.cartesian
             ? target.cartesian.dataToPoint(value as ScaleDataValue[])
@@ -239,7 +239,7 @@ class Grid implements CoordinateSystemMaster {
     convertFromPixel(
         ecModel: GlobalModel, finder: ParsedModelFinder, value: number | number[]
     ): number | number[] {
-        var target = this._findConvertTarget(ecModel, finder);
+        var target = this._findConvertTarget(finder);
 
         return target.cartesian
             ? target.cartesian.pointToData(value as number[])
@@ -248,9 +248,10 @@ class Grid implements CoordinateSystemMaster {
             : null;
     }
 
-    private _findConvertTarget(
-        ecModel: GlobalModel, finder: ParsedModelFinder
-    ): {cartesian: Cartesian2D, axis: Axis2D} {
+    private _findConvertTarget(finder: ParsedModelFinder): {
+        cartesian: Cartesian2D,
+        axis: Axis2D
+    } {
         var seriesModel = finder.seriesModel;
         var xAxisModel = finder.xAxisModel
             || (seriesModel && seriesModel.getReferringComponents('xAxis')[0]);
diff --git a/src/coord/cartesian/cartesianAxisHelper.ts b/src/coord/cartesian/cartesianAxisHelper.ts
index 5d11aa7..5fa4237 100644
--- a/src/coord/cartesian/cartesianAxisHelper.ts
+++ b/src/coord/cartesian/cartesianAxisHelper.ts
@@ -26,9 +26,9 @@ interface CartesianAxisLayout {
     position: [number, number];
     rotation: number;
     labelOffset: number;
-    labelDirection: number; // 1 | -1;
-    tickDirection: number; // 1 | -1;
-    nameDirection: number; // 1 | -1;
+    labelDirection: -1 | 1;
+    tickDirection: -1 | 1;
+    nameDirection: -1 | 1;
     labelRotate: number;
     z2: number;
 }
@@ -74,16 +74,16 @@ export function layout(
     layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1);
 
     // Tick and label direction, x y is axisDim
-    var dirMap = {top: -1, bottom: 1, left: -1, right: 1};
+    var dirMap = {top: -1, bottom: 1, left: -1, right: 1} as const;
 
     layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition];
     layout.labelOffset = otherAxisOnZeroOf ? posBound[idx[rawAxisPosition]] - posBound[idx.onZero] : 0;
 
     if (axisModel.get(['axisTick', 'inside'])) {
-        layout.tickDirection = -layout.tickDirection;
+        layout.tickDirection = -layout.tickDirection as 1 | -1;
     }
     if (zrUtil.retrieve(opt.labelInside, axisModel.get(['axisLabel', 'inside']))) {
-        layout.labelDirection = -layout.labelDirection;
+        layout.labelDirection = -layout.labelDirection as 1 | -1;
     }
 
     // Special label rotation
diff --git a/src/coord/geo/GeoModel.ts b/src/coord/geo/GeoModel.ts
index 00d795f..e11981a 100644
--- a/src/coord/geo/GeoModel.ts
+++ b/src/coord/geo/GeoModel.ts
@@ -78,11 +78,6 @@ export interface GeoCommonOptionMixin extends RoamOptionMixin {
     // higher priority than center and zoom
     boundingCoords?: number[][];
 
-    scaleLimit?: {
-        min?: number;
-        max?: number;
-    };
-
     nameMap?: NameMap;
 }
 
@@ -245,6 +240,8 @@ class GeoModel extends ComponentModel<GeoOption> {
 
 }
 
+ComponentModel.registerClass(GeoModel);
+
 interface GeoModel extends DataSelectableMixin<GeoOption> {};
 zrUtil.mixin(GeoModel, DataSelectableMixin);
 
diff --git a/src/coord/parallel/AxisModel.ts b/src/coord/parallel/AxisModel.ts
index da707a9..c949253 100644
--- a/src/coord/parallel/AxisModel.ts
+++ b/src/coord/parallel/AxisModel.ts
@@ -17,34 +17,62 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import * as zrUtil from 'zrender/src/core/util';
 import ComponentModel from '../../model/Component';
 import makeStyleMapper from '../../model/mixin/makeStyleMapper';
-import axisModelCreator from '../axisModelCreator';
+import axisModelCreator, { AxisModelExtendedInCreator } from '../axisModelCreator';
 import * as numberUtil from '../../util/number';
 import {AxisModelCommonMixin} from '../axisModelCommonMixin';
+import ParallelAxis from './ParallelAxis';
+import { ComponentOption, ZRColor, ParsedValue } from '../../util/types';
+import { AxisBaseOption } from '../axisCommonTypes';
+import { StyleProps } from 'zrender/src/graphic/Style';
+import Parallel from './Parallel';
+
+
+// 'normal' means there is no "active intervals" existing.
+export type ParallelActiveState = 'normal' | 'active' | 'inactive';
+export type ParallelAxisInterval = number[];
+type ParallelAreaSelectStyleKey = 'fill' | 'lineWidth' | 'stroke' | 'opacity';
+export type ParallelAreaSelectStyleProps = Pick<StyleProps, ParallelAreaSelectStyleKey> & {
+    // Selected area width.
+    width: number;
+}
+
+export interface ParallelAxisOption extends AxisBaseOption {
+    /**
+     * 0, 1, 2, ...
+     */
+    dim?: number[];
+    parallelIndex?: number;
+    areaSelectStyle?: {
+        width?: number;
+        borderWidth?: number;
+        borderColor?: ZRColor;
+        color?: ZRColor;
+        opacity?: number;
+    };
+    // Whether realtime update view when select.
+    realtime?: boolean;
+}
 
-var AxisModel = ComponentModel.extend({
+class ParallelAxisModel extends ComponentModel<ParallelAxisOption> {
 
-    type: 'baseParallelAxis',
+    static type: 'baseParallelAxis';
+    readonly type = ParallelAxisModel.type;
 
-    /**
-     * @type {module:echarts/coord/parallel/Axis}
-     */
-    axis: null,
+    axis: ParallelAxis;
+
+    // Inject
+    coordinateSystem: Parallel;
 
     /**
-     * @type {Array.<Array.<number>}
      * @readOnly
      */
-    activeIntervals: [],
+    activeIntervals: ParallelAxisInterval[];
 
-    /**
-     * @return {Object}
-     */
-    getAreaSelectStyle: function () {
+    getAreaSelectStyle(): ParallelAreaSelectStyleProps {
         return makeStyleMapper(
             [
                 ['fill', 'color'],
@@ -53,8 +81,8 @@ var AxisModel = ComponentModel.extend({
                 ['width', 'width'],
                 ['opacity', 'opacity']
             ]
-        )(this.getModel('areaSelectStyle'));
-    },
+        )(this.getModel('areaSelectStyle')) as ParallelAreaSelectStyleProps;
+    }
 
     /**
      * The code of this feature is put on AxisModel but not ParallelAxis,
@@ -62,11 +90,9 @@ var AxisModel = ComponentModel.extend({
      * ParallelAxis having been disposed. this._activeInterval should be kept
      * when action dispatched (i.e. legend click).
      *
-     * @param {Array.<Array<number>>} intervals interval.length === 0
-     *                                          means set all active.
-     * @public
+     * @param intervals `interval.length === 0` means set all active.
      */
-    setActiveIntervals: function (intervals) {
+    setActiveIntervals(intervals: ParallelAxisInterval[]): void {
         var activeIntervals = this.activeIntervals = zrUtil.clone(intervals);
 
         // Normalize
@@ -75,24 +101,20 @@ var AxisModel = ComponentModel.extend({
                 numberUtil.asc(activeIntervals[i]);
             }
         }
-    },
+    }
 
     /**
-     * @param {number|string} [value] When attempting to detect 'no activeIntervals set',
-     *                         value can not be input.
-     * @return {string} 'normal': no activeIntervals set,
-     *                  'active',
-     *                  'inactive'.
-     * @public
+     * @param value When only attempting detect whether 'no activeIntervals set',
+     *        `value` is not needed to be input.
      */
-    getActiveState: function (value) {
+    getActiveState(value?: ParsedValue): ParallelActiveState {
         var activeIntervals = this.activeIntervals;
 
         if (!activeIntervals.length) {
             return 'normal';
         }
 
-        if (value == null || isNaN(value)) {
+        if (value == null || isNaN(+value)) {
             return 'inactive';
         }
 
@@ -114,19 +136,10 @@ var AxisModel = ComponentModel.extend({
         return 'inactive';
     }
 
-});
-
-var defaultOption = {
+}
 
+var defaultOption: ParallelAxisOption = {
     type: 'value',
-
-    /**
-     * @type {Array.<number>}
-     */
-    dim: null, // 0, 1, 2, ...
-
-    // parallelIndex: null,
-
     areaSelectStyle: {
         width: 20,
         borderWidth: 1,
@@ -134,14 +147,19 @@ var defaultOption = {
         color: 'rgba(160,197,232)',
         opacity: 0.3
     },
-
-    realtime: true, // Whether realtime update view when select.
-
+    realtime: true,
     z: 10
 };
 
-zrUtil.mixin(AxisModel, AxisModelCommonMixin);
+ComponentModel.registerClass(ParallelAxisModel);
+
+interface ParallelAxisModel extends AxisModelCommonMixin<ParallelAxisOption>,
+    AxisModelExtendedInCreator<ParallelAxisOption> {}
+
+zrUtil.mixin(ParallelAxisModel, AxisModelCommonMixin);
 
-axisModelCreator('parallel', AxisModel, defaultOption);
+axisModelCreator<ParallelAxisOption, typeof ParallelAxisModel>(
+    'parallel', ParallelAxisModel, defaultOption
+);
 
-export default AxisModel;
\ No newline at end of file
+export default ParallelAxisModel;
diff --git a/src/coord/parallel/Parallel.ts b/src/coord/parallel/Parallel.ts
index 0959806..e9014c4 100644
--- a/src/coord/parallel/Parallel.ts
+++ b/src/coord/parallel/Parallel.ts
@@ -17,7 +17,6 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 /**
  * Parallel Coordinates
@@ -32,6 +31,15 @@ import ParallelAxis from './ParallelAxis';
 import * as graphic from '../../util/graphic';
 import * as numberUtil from '../../util/number';
 import sliderMove from '../../component/helper/sliderMove';
+import ParallelModel, { ParallelLayoutDirection } from './ParallelModel';
+import GlobalModel from '../../model/Global';
+import ExtensionAPI from '../../ExtensionAPI';
+import { Dictionary, DimensionName, ScaleDataValue } from '../../util/types';
+import { CoordinateSystem, CoordinateSystemMaster } from '../CoordinateSystem';
+import ParallelAxisModel, { ParallelActiveState } from './AxisModel';
+import ParallelSeries from '../../chart/parallel/ParallelSeries';
+import List from '../../data/List';
+import { ParsedModelFinder } from '../../util/model';
 
 var each = zrUtil.each;
 var mathMin = Math.min;
@@ -42,54 +50,72 @@ var round = numberUtil.round;
 
 var PI = Math.PI;
 
-function Parallel(parallelModel, ecModel, api) {
+interface ParallelCoordinateSystemLayoutInfo {
+    layout: ParallelLayoutDirection;
+    pixelDimIndex: number;
+    layoutBase: number;
+    layoutLength: number;
+    axisBase: number;
+    axisLength: number;
+    axisExpandable: boolean;
+    axisExpandWidth: number;
+    axisCollapseWidth: number;
+    axisExpandWindow: number[];
+    axisCount: number;
+    winInnerIndices: number[];
+    axisExpandWindow0Pos: number;
+}
+
+export interface ParallelAxisLayoutInfo {
+    position: number[];
+    rotation: number;
+    transform: matrix.MatrixArray;
+    axisNameAvailableWidth: number;
+    axisLabelShow: boolean;
+    nameTruncateMaxWidth: number;
+    tickDirection: -1 | 1;
+    labelDirection: -1 | 1;
+}
+
+type SlidedAxisExpandBehavior = 'none' | 'slide' | 'jump';
+
+class Parallel implements CoordinateSystemMaster, CoordinateSystem {
+
+    readonly type = 'parallel';
 
     /**
      * key: dimension
-     * @type {Object.<string, module:echarts/coord/parallel/Axis>}
-     * @private
      */
-    this._axesMap = zrUtil.createHashMap();
+    private _axesMap = zrUtil.createHashMap<ParallelAxis>();
 
     /**
      * key: dimension
      * value: {position: [], rotation, }
-     * @type {Object.<string, Object>}
-     * @private
      */
-    this._axesLayout = {};
+    private _axesLayout: Dictionary<ParallelAxisLayoutInfo> = {};
 
     /**
      * Always follow axis order.
-     * @type {Array.<string>}
-     * @readOnly
      */
-    this.dimensions = parallelModel.dimensions;
+    readonly dimensions: ParallelModel['dimensions'];
 
-    /**
-     * @type {module:zrender/core/BoundingRect}
-     */
-    this._rect;
+    private _rect: graphic.BoundingRect;
 
-    /**
-     * @type {module:echarts/coord/parallel/ParallelModel}
-     */
-    this._model = parallelModel;
+    private _model: ParallelModel;
 
-    this._init(parallelModel, ecModel, api);
-}
+    // Inject
+    name: string;
+    model: ParallelModel;
 
-Parallel.prototype = {
 
-    type: 'parallel',
+    constructor(parallelModel: ParallelModel, ecModel: GlobalModel, api: ExtensionAPI) {
+        this.dimensions = parallelModel.dimensions;
+        this._model = parallelModel;
 
-    constructor: Parallel,
+        this._init(parallelModel, ecModel, api);
+    }
 
-    /**
-     * Initialize cartesian coordinate systems
-     * @private
-     */
-    _init: function (parallelModel, ecModel, api) {
+    private _init(parallelModel: ParallelModel, ecModel: GlobalModel, api: ExtensionAPI): void {
 
         var dimensions = parallelModel.dimensions;
         var parallelAxisIndex = parallelModel.parallelAxisIndex;
@@ -97,7 +123,7 @@ Parallel.prototype = {
         each(dimensions, function (dim, idx) {
 
             var axisIndex = parallelAxisIndex[idx];
-            var axisModel = ecModel.getComponent('parallelAxis', axisIndex);
+            var axisModel = ecModel.getComponent('parallelAxis', axisIndex) as ParallelAxisModel;
 
             var axis = this._axesMap.set(dim, new ParallelAxis(
                 dim,
@@ -117,21 +143,16 @@ Parallel.prototype = {
             axis.coordinateSystem = axisModel.coordinateSystem = this;
 
         }, this);
-    },
+    }
 
     /**
      * Update axis scale after data processed
-     * @param  {module:echarts/model/Global} ecModel
-     * @param  {module:echarts/ExtensionAPI} api
      */
-    update: function (ecModel, api) {
+    update(ecModel: GlobalModel, api: ExtensionAPI): void {
         this._updateAxesFromSeries(this._model, ecModel);
-    },
+    }
 
-    /**
-     * @override
-     */
-    containPoint: function (point) {
+    containPoint(point: number[]): boolean {
         var layoutInfo = this._makeLayoutInfo();
         var axisBase = layoutInfo.axisBase;
         var layoutBase = layoutInfo.layoutBase;
@@ -143,17 +164,16 @@ Parallel.prototype = {
             && pAxis <= axisBase + layoutInfo.axisLength
             && pLayout >= layoutBase
             && pLayout <= layoutBase + layoutInfo.layoutLength;
-    },
+    }
 
-    getModel: function () {
+    getModel(): ParallelModel {
         return this._model;
-    },
+    }
 
     /**
      * Update properties from series
-     * @private
      */
-    _updateAxesFromSeries: function (parallelModel, ecModel) {
+    private _updateAxesFromSeries(parallelModel: ParallelModel, ecModel: GlobalModel): void {
         ecModel.eachSeries(function (seriesModel) {
 
             if (!parallelModel.contains(seriesModel, ecModel)) {
@@ -168,14 +188,12 @@ Parallel.prototype = {
                 axisHelper.niceScaleExtent(axis.scale, axis.model);
             }, this);
         }, this);
-    },
+    }
 
     /**
      * Resize the parallel coordinate system.
-     * @param {module:echarts/coord/parallel/ParallelModel} parallelModel
-     * @param {module:echarts/ExtensionAPI} api
      */
-    resize: function (parallelModel, api) {
+    resize(parallelModel: ParallelModel, api: ExtensionAPI): void {
         this._rect = layoutUtil.getLayoutRect(
             parallelModel.getBoxLayoutParams(),
             {
@@ -185,23 +203,17 @@ Parallel.prototype = {
         );
 
         this._layoutAxes();
-    },
+    }
 
-    /**
-     * @return {module:zrender/core/BoundingRect}
-     */
-    getRect: function () {
+    getRect(): graphic.BoundingRect {
         return this._rect;
-    },
+    }
 
-    /**
-     * @private
-     */
-    _makeLayoutInfo: function () {
+    private _makeLayoutInfo(): ParallelCoordinateSystemLayoutInfo {
         var parallelModel = this._model;
         var rect = this._rect;
-        var xy = ['x', 'y'];
-        var wh = ['width', 'height'];
+        var xy = ['x', 'y'] as const;
+        var wh = ['width', 'height'] as const;
         var layout = parallelModel.get('layout');
         var pixelDimIndex = layout === 'horizontal' ? 0 : 1;
         var layoutLength = rect[wh[pixelDimIndex]];
@@ -229,8 +241,8 @@ Parallel.prototype = {
             axisExpandWindow[1] = axisExpandWindow[0] + winSize;
         }
         else {
-                winSize = restrict(axisExpandWindow[1] - axisExpandWindow[0], layoutExtent);
-                axisExpandWindow[1] = axisExpandWindow[0] + winSize;
+            winSize = restrict(axisExpandWindow[1] - axisExpandWindow[0], layoutExtent);
+            axisExpandWindow[1] = axisExpandWindow[0] + winSize;
         }
 
         var axisCollapseWidth = (layoutLength - winSize) / (axisCount - axisExpandCount);
@@ -261,12 +273,9 @@ Parallel.prototype = {
             winInnerIndices: winInnerIndices,
             axisExpandWindow0Pos: axisExpandWindow0Pos
         };
-    },
+    }
 
-    /**
-     * @private
-     */
-    _layoutAxes: function () {
+    private _layoutAxes(): void {
         var rect = this._rect;
         var axes = this._axesMap;
         var dimensions = this.dimensions;
@@ -310,10 +319,10 @@ Parallel.prototype = {
             matrix.translate(transform, transform, position);
 
             // TODO
-            // tick等排布信息。
+            // tick layout info
 
             // TODO
-            // 根据axis order 更新 dimensions顺序。
+            // update dimensions info based on axis order.
 
             this._axesLayout[dim] = {
                 position: position,
@@ -326,46 +335,43 @@ Parallel.prototype = {
                 labelDirection: 1
             };
         }, this);
-    },
+    }
 
     /**
      * Get axis by dim.
-     * @param {string} dim
-     * @return {module:echarts/coord/parallel/ParallelAxis} [description]
      */
-    getAxis: function (dim) {
+    getAxis(dim: DimensionName): ParallelAxis {
         return this._axesMap.get(dim);
-    },
+    }
 
     /**
      * Convert a dim value of a single item of series data to Point.
-     * @param {*} value
-     * @param {string} dim
-     * @return {Array}
      */
-    dataToPoint: function (value, dim) {
+    dataToPoint(value: ScaleDataValue, dim: DimensionName): number[] {
         return this.axisCoordToPoint(
             this._axesMap.get(dim).dataToCoord(value),
             dim
         );
-    },
+    }
 
     /**
      * Travel data for one time, get activeState of each data item.
-     * @param {module:echarts/data/List} data
-     * @param {Functio} cb param: {string} activeState 'active' or 'inactive' or 'normal'
-     *                            {number} dataIndex
-     * @param {number} [start=0] the start dataIndex that travel from.
-     * @param {number} [end=data.count()] the next dataIndex of the last dataIndex will be travel.
+     * @param start the start dataIndex that travel from.
+     * @param end the next dataIndex of the last dataIndex will be travel.
      */
-    eachActiveState: function (data, callback, start, end) {
+    eachActiveState(
+        data: List,
+        callback: (activeState: ParallelActiveState, dataIndex: number) => void,
+        start?: number,
+        end?: number
+    ): void {
         start == null && (start = 0);
         end == null && (end = data.count());
 
         var axesMap = this._axesMap;
         var dimensions = this.dimensions;
-        var dataDimensions = [];
-        var axisModels = [];
+        var dataDimensions = [] as DimensionName[];
+        var axisModels = [] as ParallelAxisModel[];
 
         zrUtil.each(dimensions, function (axisDim) {
             dataDimensions.push(data.mapDimension(axisDim));
@@ -375,7 +381,7 @@ Parallel.prototype = {
         var hasActiveSet = this.hasAxisBrushed();
 
         for (var dataIndex = start; dataIndex < end; dataIndex++) {
-            var activeState;
+            var activeState: ParallelActiveState;
 
             if (!hasActiveSet) {
                 activeState = 'normal';
@@ -395,13 +401,12 @@ Parallel.prototype = {
 
             callback(activeState, dataIndex);
         }
-    },
+    }
 
     /**
      * Whether has any activeSet.
-     * @return {boolean}
      */
-    hasAxisBrushed: function () {
+    hasAxisBrushed(): boolean {
         var dimensions = this.dimensions;
         var axesMap = this._axesMap;
         var hasActiveSet = false;
@@ -413,32 +418,31 @@ Parallel.prototype = {
         }
 
         return hasActiveSet;
-    },
+    }
 
     /**
      * Convert coords of each axis to Point.
      *  Return point. For example: [10, 20]
-     * @param {Array.<number>} coords
-     * @param {string} dim
-     * @return {Array.<number>}
      */
-    axisCoordToPoint: function (coord, dim) {
+    axisCoordToPoint(coord: number, dim: DimensionName): number[] {
         var axisLayout = this._axesLayout[dim];
         return graphic.applyTransform([coord, 0], axisLayout.transform);
-    },
+    }
 
     /**
      * Get axis layout.
      */
-    getAxisLayout: function (dim) {
+    getAxisLayout(dim: DimensionName): ParallelAxisLayoutInfo {
         return zrUtil.clone(this._axesLayout[dim]);
-    },
+    }
 
     /**
-     * @param {Array.<number>} point
      * @return {Object} {axisExpandWindow, delta, behavior: 'jump' | 'slide' | 'none'}.
      */
-    getSlidedAxisExpandWindow: function (point) {
+    getSlidedAxisExpandWindow(point: number[]): {
+        axisExpandWindow: number[],
+        behavior: SlidedAxisExpandBehavior
+    } {
         var layoutInfo = this._makeLayoutInfo();
         var pixelDimIndex = layoutInfo.pixelDimIndex;
         var axisExpandWindow = layoutInfo.axisExpandWindow.slice();
@@ -456,7 +460,7 @@ Parallel.prototype = {
         // For dragging operation convenience, the window should not be
         // slided when mouse is the center area of the window.
         var delta;
-        var behavior = 'slide';
+        var behavior: SlidedAxisExpandBehavior = 'slide';
         var axisCollapseWidth = layoutInfo.axisCollapseWidth;
         var triggerArea = this._model.get('axisExpandSlideTriggerArea');
         // But consider touch device, jump is necessary.
@@ -496,13 +500,32 @@ Parallel.prototype = {
             behavior: behavior
         };
     }
-};
 
-function restrict(len, extent) {
+    // TODO
+    // convertToPixel
+    // convertFromPixel
+    // Note:
+    // (1) Consider Parallel, the return type of `convertToPixel` could be number[][] (Point[]).
+    // (2) In parallel coord sys, how to make `convertFromPixel` make sense?
+    // Perhaps convert a point based on "a rensent most axis" is more meaningful rather than based on all axes?
+}
+
+
+function restrict(len: number, extent: number[]): number {
     return mathMin(mathMax(len, extent[0]), extent[1]);
 }
 
-function layoutAxisWithoutExpand(axisIndex, layoutInfo) {
+interface ParallelAxisLayoutPositionInfo {
+    position: number;
+    axisNameAvailableWidth: number;
+    axisLabelShow: boolean;
+    nameTruncateMaxWidth?: number;
+}
+
+function layoutAxisWithoutExpand(
+    axisIndex: number,
+    layoutInfo: ParallelCoordinateSystemLayoutInfo
+): ParallelAxisLayoutPositionInfo {
     var step = layoutInfo.layoutLength / (layoutInfo.axisCount - 1);
     return {
         position: step * axisIndex,
@@ -511,7 +534,10 @@ function layoutAxisWithoutExpand(axisIndex, layoutInfo) {
     };
 }
 
-function layoutAxisWithExpand(axisIndex, layoutInfo) {
+function layoutAxisWithExpand(
+    axisIndex: number,
+    layoutInfo: ParallelCoordinateSystemLayoutInfo
+): ParallelAxisLayoutPositionInfo {
     var layoutLength = layoutInfo.layoutLength;
     var axisExpandWidth = layoutInfo.axisExpandWidth;
     var axisCount = layoutInfo.axisCount;
@@ -546,4 +572,4 @@ function layoutAxisWithExpand(axisIndex, layoutInfo) {
     };
 }
 
-export default Parallel;
\ No newline at end of file
+export default Parallel;
diff --git a/src/coord/parallel/ParallelAxis.ts b/src/coord/parallel/ParallelAxis.ts
index b159cac..4161bd4 100644
--- a/src/coord/parallel/ParallelAxis.ts
+++ b/src/coord/parallel/ParallelAxis.ts
@@ -17,59 +17,40 @@
 * under the License.
 */
 
-// @ts-nocheck
 
-import * as zrUtil from 'zrender/src/core/util';
 import Axis from '../Axis';
+import Scale from '../../scale/Scale';
+import { DimensionName } from '../../util/types';
+import { OptionAxisType } from '../axisCommonTypes';
+import AxisModel from './AxisModel';
+import Parallel from './Parallel';
 
-/**
- * @constructor module:echarts/coord/parallel/ParallelAxis
- * @extends {module:echarts/coord/Axis}
- * @param {string} dim
- * @param {*} scale
- * @param {Array.<number>} coordExtent
- * @param {string} axisType
- */
-var ParallelAxis = function (dim, scale, coordExtent, axisType, axisIndex) {
 
-    Axis.call(this, dim, scale, coordExtent);
+class ParallelAxis extends Axis {
 
-    /**
-     * Axis type
-     *  - 'category'
-     *  - 'value'
-     *  - 'time'
-     *  - 'log'
-     * @type {string}
-     */
-    this.type = axisType || 'value';
+    readonly axisIndex: number;
 
-    /**
-     * @type {number}
-     * @readOnly
-     */
-    this.axisIndex = axisIndex;
-};
+    // Inject
+    model: AxisModel;
+    coordinateSystem: Parallel;
 
-ParallelAxis.prototype = {
+    constructor (
+        dim: DimensionName,
+        scale: Scale,
+        coordExtent: [number, number],
+        axisType: OptionAxisType,
+        axisIndex: number
+    ) {
+        super(dim, scale, coordExtent);
 
-    constructor: ParallelAxis,
-
-    /**
-     * Axis model
-     * @param {module:echarts/coord/parallel/AxisModel}
-     */
-    model: null,
+        this.type = axisType || 'value';
+        this.axisIndex = axisIndex;
+    }
 
-    /**
-     * @override
-     */
-    isHorizontal: function () {
+    isHorizontal(): boolean {
         return this.coordinateSystem.getModel().get('layout') !== 'horizontal';
     }
 
-};
-
-zrUtil.inherits(ParallelAxis, Axis);
+}
 
-export default ParallelAxis;
\ No newline at end of file
+export default ParallelAxis;
diff --git a/src/coord/parallel/ParallelModel.ts b/src/coord/parallel/ParallelModel.ts
index 63ef628..f0105c4 100644
--- a/src/coord/parallel/ParallelModel.ts
+++ b/src/coord/parallel/ParallelModel.ts
@@ -17,41 +17,65 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import * as zrUtil from 'zrender/src/core/util';
-import Component from '../../model/Component';
-
+import ComponentModel from '../../model/Component';
 import './AxisModel';
+import Parallel from './Parallel';
+import { DimensionName, ComponentOption, BoxLayoutOptionMixin, Dictionary } from '../../util/types';
+import ParallelAxisModel, { ParallelAxisOption } from './AxisModel';
+import GlobalModel from '../../model/Global';
+import ParallelSeries from '../../chart/parallel/ParallelSeries';
+import SeriesModel from '../../model/Series';
 
-export default Component.extend({
 
-    type: 'parallel',
+export type ParallelLayoutDirection = 'horizontal' | 'vertical';
 
-    dependencies: ['parallelAxis'],
+export interface ParallelCoordinateSystemOption extends ComponentOption, BoxLayoutOptionMixin {
+    layout?: ParallelLayoutDirection;
 
-    /**
-     * @type {module:echarts/coord/parallel/Parallel}
-     */
-    coordinateSystem: null,
+    axisExpandable?: boolean;
+    axisExpandCenter?: number;
+    axisExpandCount?: number;
+    axisExpandWidth?: number; // TODO '10%' ?
+    axisExpandTriggerOn?: 'click' | 'mousemove';
+
+    // Not ready to expose to users yet.
+    axisExpandRate?: number;
+    // Not ready to expose to users yet.
+    axisExpandDebounce?: number;
+    // Not ready to expose to users yet.
+    // [out, in, jumpTarget]. In percentage. If use [null, 0.05], null means full.
+    // Do not doc to user until necessary.
+    axisExpandSlideTriggerArea?: [number, number, number];
+    // Not ready to expose to users yet.
+    axisExpandWindow?: number[];
+
+    parallelAxisDefault?: ParallelAxisOption
+}
+
+class ParallelModel extends ComponentModel<ParallelCoordinateSystemOption> {
+
+    static type = 'parallel';
+    readonly type = ParallelModel.type;
+
+    static dependencies = ['parallelAxis'];
+
+    coordinateSystem: Parallel;
 
     /**
      * Each item like: 'dim0', 'dim1', 'dim2', ...
-     * @type {Array.<string>}
-     * @readOnly
      */
-    dimensions: null,
+    dimensions: DimensionName[];
 
     /**
      * Coresponding to dimensions.
-     * @type {Array.<number>}
-     * @readOnly
      */
-    parallelAxisIndex: null,
+    parallelAxisIndex: number[];
 
-    layoutMode: 'box',
+    static layoutMode = 'box' as const;
 
-    defaultOption: {
+    static defaultOption: ParallelCoordinateSystemOption = {
         zlevel: 0,
         z: 0,
         left: 80,
@@ -77,68 +101,73 @@ export default Component.extend({
         axisExpandTriggerOn: 'click', // 'mousemove' or 'click'
 
         parallelAxisDefault: null
-    },
-
-    /**
-     * @override
-     */
-    init: function () {
-        Component.prototype.init.apply(this, arguments);
+    };
 
+    init() {
+        super.init.apply(this, arguments as any);
         this.mergeOption({});
-    },
+    }
 
-    /**
-     * @override
-     */
-    mergeOption: function (newOption) {
+    mergeOption(newOption: ParallelCoordinateSystemOption) {
         var thisOption = this.option;
 
         newOption && zrUtil.merge(thisOption, newOption, true);
 
         this._initDimensions();
-    },
+    }
 
     /**
      * Whether series or axis is in this coordinate system.
-     * @param {module:echarts/model/Series|module:echarts/coord/parallel/AxisModel} model
-     * @param {module:echarts/model/Global} ecModel
      */
-    contains: function (model, ecModel) {
-        var parallelIndex = model.get('parallelIndex');
+    contains(model: SeriesModel | ParallelAxisModel, ecModel: GlobalModel): boolean {
+        var parallelIndex = (model as ParallelSeries).get('parallelIndex');
         return parallelIndex != null
             && ecModel.getComponent('parallel', parallelIndex) === this;
-    },
+    }
 
-    setAxisExpand: function (opt) {
+    setAxisExpand(opt: {
+        axisExpandable?: boolean,
+        axisExpandCenter?: number,
+        axisExpandCount?: number,
+        axisExpandWidth?: number,
+        axisExpandWindow?: number[]
+    }): void {
         zrUtil.each(
-            ['axisExpandable', 'axisExpandCenter', 'axisExpandCount', 'axisExpandWidth', 'axisExpandWindow'],
+            [
+                'axisExpandable',
+                'axisExpandCenter',
+                'axisExpandCount',
+                'axisExpandWidth',
+                'axisExpandWindow'
+            ] as const,
             function (name) {
                 if (opt.hasOwnProperty(name)) {
+                    // @ts-ignore FIXME: why "never" inferred in this.option[name]?
                     this.option[name] = opt[name];
                 }
             },
             this
         );
-    },
+    }
 
-    /**
-     * @private
-     */
-    _initDimensions: function () {
-        var dimensions = this.dimensions = [];
-        var parallelAxisIndex = this.parallelAxisIndex = [];
+    private _initDimensions(): void {
+        var dimensions = this.dimensions = [] as DimensionName[];
+        var parallelAxisIndex = this.parallelAxisIndex = [] as number[];
 
-        var axisModels = zrUtil.filter(this.dependentModels.parallelAxis, function (axisModel) {
+        var axisModels = zrUtil.filter(this.dependentModels.parallelAxis, function (axisModel: ParallelAxisModel) {
             // Can not use this.contains here, because
             // initialization has not been completed yet.
             return (axisModel.get('parallelIndex') || 0) === this.componentIndex;
         }, this);
 
-        zrUtil.each(axisModels, function (axisModel) {
+        zrUtil.each(axisModels, function (axisModel: ParallelAxisModel) {
             dimensions.push('dim' + axisModel.get('dim'));
             parallelAxisIndex.push(axisModel.componentIndex);
         });
     }
 
-});
+}
+
+ComponentModel.registerClass(ParallelModel);
+
+export default ParallelModel;
diff --git a/src/coord/parallel/parallelCreator.ts b/src/coord/parallel/parallelCreator.ts
index 3d853e5..1369c67 100644
--- a/src/coord/parallel/parallelCreator.ts
+++ b/src/coord/parallel/parallelCreator.ts
@@ -17,19 +17,23 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 /**
  * Parallel coordinate system creater.
  */
 
 import Parallel from './Parallel';
-import CoordinateSystem from '../../CoordinateSystem';
+import GlobalModel from '../../model/Global';
+import ExtensionAPI from '../../ExtensionAPI';
+import ParallelModel from './ParallelModel';
+import { CoordinateSystemMaster } from '../CoordinateSystem';
+import ParallelSeries from '../../chart/parallel/ParallelSeries';
+import CoordinateSystemManager from '../../CoordinateSystem';
 
-function create(ecModel, api) {
-    var coordSysList = [];
+function create(ecModel: GlobalModel, api: ExtensionAPI): CoordinateSystemMaster[] {
+    var coordSysList: CoordinateSystemMaster[] = [];
 
-    ecModel.eachComponent('parallel', function (parallelModel, idx) {
+    ecModel.eachComponent('parallel', function (parallelModel: ParallelModel, idx: number) {
         var coordSys = new Parallel(parallelModel, ecModel, api);
 
         coordSys.name = 'parallel_' + idx;
@@ -43,12 +47,12 @@ function create(ecModel, api) {
 
     // Inject the coordinateSystems into seriesModel
     ecModel.eachSeries(function (seriesModel) {
-        if (seriesModel.get('coordinateSystem') === 'parallel') {
+        if ((seriesModel as ParallelSeries).get('coordinateSystem') === 'parallel') {
             var parallelModel = ecModel.queryComponents({
                 mainType: 'parallel',
-                index: seriesModel.get('parallelIndex'),
-                id: seriesModel.get('parallelId')
-            })[0];
+                index: (seriesModel as ParallelSeries).get('parallelIndex'),
+                id: (seriesModel as ParallelSeries).get('parallelId')
+            })[0] as ParallelModel;
             seriesModel.coordinateSystem = parallelModel.coordinateSystem;
         }
     });
@@ -56,4 +60,4 @@ function create(ecModel, api) {
     return coordSysList;
 }
 
-CoordinateSystem.register('parallel', {create: create});
+CoordinateSystemManager.register('parallel', {create: create});
diff --git a/src/coord/parallel/parallelPreprocessor.ts b/src/coord/parallel/parallelPreprocessor.ts
index 50cc870..7947601 100644
--- a/src/coord/parallel/parallelPreprocessor.ts
+++ b/src/coord/parallel/parallelPreprocessor.ts
@@ -17,12 +17,12 @@
 * under the License.
 */
 
-// @ts-nocheck
 
 import * as zrUtil from 'zrender/src/core/util';
 import * as modelUtil from '../../util/model';
+import { ECUnitOption, SeriesOption } from '../../util/types';
 
-export default function (option) {
+export default function (option: ECUnitOption): void {
     createParallelIfNeeded(option);
     mergeAxisOptionFromParallel(option);
 }
@@ -31,14 +31,14 @@ export default function (option) {
  * Create a parallel coordinate if not exists.
  * @inner
  */
-function createParallelIfNeeded(option) {
+function createParallelIfNeeded(option: ECUnitOption): void {
     if (option.parallel) {
         return;
     }
 
     var hasParallelSeries = false;
 
-    zrUtil.each(option.series, function (seriesOpt) {
+    zrUtil.each(option.series, function (seriesOpt: SeriesOption) {
         if (seriesOpt && seriesOpt.type === 'parallel') {
             hasParallelSeries = true;
         }
@@ -53,7 +53,7 @@ function createParallelIfNeeded(option) {
  * Merge aixs definition from parallel option (if exists) to axis option.
  * @inner
  */
-function mergeAxisOptionFromParallel(option) {
+function mergeAxisOptionFromParallel(option: ECUnitOption): void {
     var axes = modelUtil.normalizeToArray(option.parallelAxis);
 
     zrUtil.each(axes, function (axisOption) {
diff --git a/src/data/Source.ts b/src/data/Source.ts
index 2f06452..0bc561c 100644
--- a/src/data/Source.ts
+++ b/src/data/Source.ts
@@ -95,8 +95,9 @@ class Source {
     /**
      * encode definition in option.
      * can be null/undefined.
+     * Might be specified outside.
      */
-    readonly encodeDefine: HashMap<OptionEncodeValue>;
+    encodeDefine: HashMap<OptionEncodeValue>;
 
     /**
      * Not null/undefined, uint.
diff --git a/src/echarts.ts b/src/echarts.ts
index 3eefe16..62bb930 100644
--- a/src/echarts.ts
+++ b/src/echarts.ts
@@ -721,11 +721,11 @@ class ECharts extends Eventful {
     getVisual(finder: ModelFinder, visualType: string) {
         var ecModel = this._model;
 
-        finder = modelUtil.parseFinder(ecModel, finder, {
+        var parsedFinder = modelUtil.parseFinder(ecModel, finder, {
             defaultMainType: 'series'
         });
 
-        var seriesModel = finder.seriesModel;
+        var seriesModel = parsedFinder.seriesModel;
 
         if (__DEV__) {
             if (!seriesModel) {
@@ -735,10 +735,10 @@ class ECharts extends Eventful {
 
         var data = seriesModel.getData();
 
-        var dataIndexInside = finder.hasOwnProperty('dataIndexInside')
-            ? finder.dataIndexInside
-            : finder.hasOwnProperty('dataIndex')
-            ? data.indexOfRawIndex(finder.dataIndex)
+        var dataIndexInside = parsedFinder.hasOwnProperty('dataIndexInside')
+            ? parsedFinder.dataIndexInside
+            : parsedFinder.hasOwnProperty('dataIndex')
+            ? data.indexOfRawIndex(parsedFinder.dataIndex)
             : null;
 
         return dataIndexInside != null
diff --git a/src/model/Series.ts b/src/model/Series.ts
index 7b6c569..a398209 100644
--- a/src/model/Series.ts
+++ b/src/model/Series.ts
@@ -29,7 +29,8 @@ import {
 import * as modelUtil from '../util/model';
 import {
     DataHost, DimensionName, StageHandlerProgressParams,
-    SeriesOption, TooltipRenderMode, ZRColor, BoxLayoutOptionMixin, ScaleDataValue, Dictionary, ColorString
+    SeriesOption, TooltipRenderMode, ZRColor, BoxLayoutOptionMixin,
+    ScaleDataValue, Dictionary, ColorString
 } from '../util/types';
 import ComponentModel, { ComponentModelConstructor } from './Component';
 import {ColorPaletteMixin} from './mixin/colorPalette';
@@ -55,6 +56,7 @@ import List from '../data/List';
 import Source from '../data/Source';
 import Axis from '../coord/Axis';
 import { GradientObject } from 'zrender/src/graphic/Gradient';
+import type { BrushCommonSelectorsForSeries, BrushSelectableArea } from '../component/brush/selector';
 
 var inner = modelUtil.makeInner<{
     data: List
@@ -90,7 +92,18 @@ interface SeriesModel {
     /**
      * Get position for marker
      */
-    getMarkerPosition(value: ScaleDataValue[]): number[]
+    getMarkerPosition(value: ScaleDataValue[]): number[];
+
+    /**
+     * See `component/brush/selector.js`
+     * Defined the brush selector for this series.
+     */
+    brushSelector(
+        dataIndex: number,
+        data: List,
+        selectors: BrushCommonSelectorsForSeries,
+        area: BrushSelectableArea
+    ): boolean;
 }
 
 class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentModel<Opt> {
diff --git a/src/util/graphic.ts b/src/util/graphic.ts
index e936fd2..c522804 100644
--- a/src/util/graphic.ts
+++ b/src/util/graphic.ts
@@ -1278,7 +1278,7 @@ export function initProps<Props>(
  * @param target
  * @param [ancestor]
  */
-export function getTransform(target: Transformable, ancestor: Transformable): matrix.MatrixArray {
+export function getTransform(target: Transformable, ancestor?: Transformable): matrix.MatrixArray {
     var mat = matrix.identity([]);
 
     while (target && target !== ancestor) {
@@ -1395,7 +1395,7 @@ export function groupTransition(
     });
 }
 
-export function clipPointsByRect(points: vector.VectorArray[], rect: ZRRectLike): vector.VectorArray[] {
+export function clipPointsByRect(points: vector.VectorArray[], rect: ZRRectLike): number[][] {
     // FIXME: this way migth be incorrect when grpahic clipped by a corner.
     // and when element have border.
     return zrUtil.map(points, function (point) {
diff --git a/src/util/model.ts b/src/util/model.ts
index e9f3f71..874d8cc 100644
--- a/src/util/model.ts
+++ b/src/util/model.ts
@@ -496,16 +496,17 @@ var innerUniqueIndex = Math.round(Math.random() * 5);
  * xxxIndex can be set as 'all' (means all xxx) or 'none' (means not specify)
  * If nothing or null/undefined specified, return nothing.
  */
+export type ModelFinderIndexQuery = number | number[] | 'all' | 'none';
 export type ModelFinder = string | ModelFinderObject;
 export type ModelFinderObject = {
-    seriesIndex?: number, seriesId?: string, seriesName?: string,
-    geoIndex?: number, geoId?: string, geoName?: string,
-    bmapIndex?: number, bmapId?: string, bmapName?: string,
-    xAxisIndex?: number, xAxisId?: string, xAxisName?: string,
-    yAxisIndex?: number, yAxisId?: string, yAxisName?: string,
-    gridIndex?: number, gridId?: string, gridName?: string,
+    seriesIndex?: ModelFinderIndexQuery, seriesId?: string, seriesName?: string,
+    geoIndex?: ModelFinderIndexQuery, geoId?: string, geoName?: string,
+    bmapIndex?: ModelFinderIndexQuery, bmapId?: string, bmapName?: string,
+    xAxisIndex?: ModelFinderIndexQuery, xAxisId?: string, xAxisName?: string,
+    yAxisIndex?: ModelFinderIndexQuery, yAxisId?: string, yAxisName?: string,
+    gridIndex?: ModelFinderIndexQuery, gridId?: string, gridName?: string,
     // ... (can be extended)
-    [key: string]: any
+    [key: string]: unknown
 };
 /**
  * {
@@ -516,46 +517,23 @@ export type ModelFinderObject = {
  *     ...
  * }
  */
-export type ParsedModelFinder = {
-    seriesModels?: SeriesModel[],
-    seriesModel?: SeriesModel,
-    xAxisModels?: CartesianAxisModel[],
-    xAxisModel?: CartesianAxisModel,
-    yAxisModels?: CartesianAxisModel[],
-    yAxisModel?: CartesianAxisModel,
-    gridModels?: GridModel[],
-    gridModel?: GridModel,
-    // others:
-    [key: string]: ComponentModel | ComponentModel[]
+type ParsedModelFinderKnown = {
+    seriesModels?: SeriesModel[];
+    seriesModel?: SeriesModel;
+    xAxisModels?: CartesianAxisModel[];
+    xAxisModel?: CartesianAxisModel;
+    yAxisModels?: CartesianAxisModel[];
+    yAxisModel?: CartesianAxisModel;
+    gridModels?: GridModel[];
+    gridModel?: GridModel;
+    dataIndex?: number;
+    dataIndexInside?: number;
 };
+export type ParsedModelFinder = ParsedModelFinderKnown & {
+    // other components
+    [key: string]: ComponentModel | ComponentModel[];
+}
 
-/**
- * @param {module:echarts/model/Global} ecModel
- * @param {string|Object} finder
- *        If string, e.g., 'geo', means {geoIndex: 0}.
- *        If Object, could contain some of these properties below:
- *        {
- *            seriesIndex, seriesId, seriesName,
- *            geoIndex, geoId, geoName,
- *            bmapIndex, bmapId, bmapName,
- *            xAxisIndex, xAxisId, xAxisName,
- *            yAxisIndex, yAxisId, yAxisName,
- *            gridIndex, gridId, gridName,
- *            ... (can be extended)
- *        }
- *        Each properties can be number|string|Array.<number>|Array.<string>
- *        For example, a finder could be
- *        {
- *            seriesIndex: 3,
- *            geoId: ['aa', 'cc'],
- *            gridName: ['xx', 'rr']
- *        }
- *        xxxIndex can be set as 'all' (means all xxx) or 'none' (means not specify)
- *        If nothing or null/undefined specified, return nothing.
- * @param [opt]
- * @param [opt.defaultMainType]
- * @param [opt.includeMainTypes]
- */
 export function parseFinder(
     ecModel: GlobalModel,
     finderInput: ModelFinder,
@@ -587,7 +565,7 @@ export function parseFinder(
 
         // Exclude 'dataIndex' and other illgal keys.
         if (key === 'dataIndex' || key === 'dataIndexInside') {
-            result[key] = value;
+            result[key] = value as number;
             return;
         }
 
@@ -606,7 +584,7 @@ export function parseFinder(
 
         var queryParam = {mainType: mainType} as QueryConditionKindB;
         if (queryType !== 'index' || value !== 'all') {
-            queryParam[queryType] = value;
+            queryParam[queryType] = value as any;
         }
 
         var models = ecModel.queryComponents(queryParam);
diff --git a/src/util/throttle.ts b/src/util/throttle.ts
index 461c91a..830fd4f 100755
--- a/src/util/throttle.ts
+++ b/src/util/throttle.ts
@@ -17,14 +17,19 @@
 * under the License.
 */
 
-// @ts-nocheck
 
+var ORIGIN_METHOD = '\0__throttleOriginMethod' as const;
+var RATE = '\0__throttleRate' as const;
+var THROTTLE_TYPE = '\0__throttleType' as const;
 
-var ORIGIN_METHOD = '\0__throttleOriginMethod';
-var RATE = '\0__throttleRate';
-var THROTTLE_TYPE = '\0__throttleType';
+type ThrottleFunction = (this: unknown, ...args: unknown[]) => void;
+export type ThrottleType = 'fixRate' | 'debounce';
+
+export interface ThrottleController {
+    clear(): void;
+    debounceNextCall(debounceDelay: number): void;
+};
 
-type ThrottleFunction = (...args: any[]) => void;
 /**
  * @public
  * @param {(Function)} fn
@@ -34,29 +39,33 @@ type ThrottleFunction = (...args: any[]) => void;
  *        false: If call interval less than `delay, call works on fixed rate.
  * @return {(Function)} throttled fn.
  */
-export function throttle<T extends ThrottleFunction>(fn:T, delay?: number, debounce?: boolean): T {
+export function throttle<T extends ThrottleFunction>(
+    fn: T,
+    delay?: number,
+    debounce?: boolean
+): T & ThrottleController {
 
     var currCall;
     var lastCall = 0;
     var lastExec = 0;
-    var timer = null;
+    var timer: ReturnType<typeof setTimeout> = null;
     var diff;
-    var scope;
-    var args;
-    var debounceNextCall;
+    var scope: unknown;
+    var args: unknown[];
+    var debounceNextCall: number;
 
     delay = delay || 0;
 
-    function exec() {
+    function exec(): void {
         lastExec = (new Date()).getTime();
         timer = null;
         fn.apply(scope, args || []);
     }
 
-    var cb = function () {
+    var cb = function (this: unknown, ...cbArgs: unknown[]): void {
         currCall = (new Date()).getTime();
         scope = this;
-        args = arguments;
+        args = cbArgs;
         var thisDelay = debounceNextCall || delay;
         var thisDebounce = debounceNextCall || debounce;
         debounceNextCall = null;
@@ -85,13 +94,13 @@ export function throttle<T extends ThrottleFunction>(fn:T, delay?: number, debou
         }
 
         lastCall = currCall;
-    };
+    } as T & ThrottleController;
 
     /**
      * Clear throttle.
      * @public
      */
-    cb.clear = function () {
+    cb.clear = function (): void {
         if (timer) {
             clearTimeout(timer);
             timer = null;
@@ -101,7 +110,7 @@ export function throttle<T extends ThrottleFunction>(fn:T, delay?: number, debou
     /**
      * Enable debounce once.
      */
-    cb.debounceNextCall = function (debounceDelay) {
+    cb.debounceNextCall = function (debounceDelay: number): void {
         debounceNextCall = debounceDelay;
     };
 
@@ -133,17 +142,17 @@ export function createOrUpdate<T, S extends keyof T, P = T[S]>(
     obj: T,
     fnAttr: S,
     rate: number,
-    throttleType: 'fixRate' | 'debounce'
-): P extends ThrottleFunction ? P : never {
+    throttleType: ThrottleType
+): P extends ThrottleFunction ? P & ThrottleController : never {
     var fn = obj[fnAttr];
 
     if (!fn) {
         return;
     }
 
-    var originFn = fn[ORIGIN_METHOD] || fn;
-    var lastThrottleType = fn[THROTTLE_TYPE];
-    var lastRate = fn[RATE];
+    var originFn = (fn as any)[ORIGIN_METHOD] || fn;
+    var lastThrottleType = (fn as any)[THROTTLE_TYPE];
+    var lastRate = (fn as any)[RATE];
 
     if (lastRate !== rate || lastThrottleType !== throttleType) {
         if (rate == null || !throttleType) {
@@ -153,12 +162,12 @@ export function createOrUpdate<T, S extends keyof T, P = T[S]>(
         fn = obj[fnAttr] = throttle(
             originFn, rate, throttleType === 'debounce'
         );
-        fn[ORIGIN_METHOD] = originFn;
-        fn[THROTTLE_TYPE] = throttleType;
-        fn[RATE] = rate;
+        (fn as any)[ORIGIN_METHOD] = originFn;
+        (fn as any)[THROTTLE_TYPE] = throttleType;
+        (fn as any)[RATE] = rate;
     }
 
-    return fn;
+    return fn as ReturnType<typeof createOrUpdate>;
 }
 
 /**
@@ -166,7 +175,7 @@ export function createOrUpdate<T, S extends keyof T, P = T[S]>(
  */
 export function clear<T, S extends keyof T>(obj: T, fnAttr: S): void {
     var fn = obj[fnAttr];
-    if (fn && fn[ORIGIN_METHOD]) {
-        obj[fnAttr] = fn[ORIGIN_METHOD];
+    if (fn && (fn as any)[ORIGIN_METHOD]) {
+        obj[fnAttr] = (fn as any)[ORIGIN_METHOD];
     }
 }
diff --git a/src/view/Component.ts b/src/view/Component.ts
index 69febfe..2f3f5c9 100644
--- a/src/view/Component.ts
+++ b/src/view/Component.ts
@@ -30,7 +30,7 @@ interface ComponentView {
     /**
      * Implement it if needed.
      */
-    updateTransform(
+    updateTransform?(
         seriesModel: ComponentModel, ecModel: GlobalModel, api: ExtensionAPI, payload: Payload
     ): void | {update: true};
 
@@ -43,7 +43,6 @@ interface ComponentView {
     ): boolean;
 }
 
-
 class ComponentView {
 
     // [Caution]: for compat the previous "class extend"
@@ -89,6 +88,7 @@ class ComponentView {
     static registerClass: clazzUtil.ClassManager['registerClass'];
 };
 
+
 export type ComponentViewConstructor = typeof ComponentView
     & clazzUtil.ExtendableConstructor
     & clazzUtil.ClassManager;


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