提交 99ab1d7a 编写于 作者: 1 100pah

ts: (1) add more strict type check for id, name from option. (2) add more...

ts: (1) add more strict type check for id, name from option. (2) add more strict conversion for id, name from option.
上级 85a47ef6
......@@ -30,6 +30,7 @@ import {
OptionDataItemObject
} from '../../util/types';
import SeriesModel from '../../model/Series';
import { convertOptionIdName } from '../../util/model';
export default function (
nodes: OptionSourceDataOriginal<OptionDataValue, OptionDataItemObject<OptionDataValue>>,
......@@ -59,7 +60,7 @@ export default function (
if (graph.addEdge(source, target, linkCount)) {
validEdges.push(link);
linkNameList.push(zrUtil.retrieve(
link.id != null ? link.id + '' : null,
convertOptionIdName(link.id, null),
source + ' > ' + target
));
linkCount++;
......
......@@ -25,7 +25,6 @@ import type { SeriesOption, SeriesOnCartesianOptionMixin, LayoutOrient } from '.
import type GlobalModel from '../../model/Global';
import type SeriesModel from '../../model/Series';
import type CartesianAxisModel from '../../coord/cartesian/AxisModel';
import type DataDimensionInfo from '../../data/DataDimensionInfo';
import type List from '../../data/List';
import type Axis2D from '../../coord/cartesian/Axis2D';
import { CoordDimensionDefinition } from '../../data/helper/createDimensions';
......
......@@ -38,7 +38,7 @@ import GlobalModel from '../../model/Global';
import List from '../../data/List';
import { LayoutRect } from '../../util/layout';
import { createTooltipMarkup } from '../../component/tooltip/tooltipMarkup';
import { defaultSeriesFormatTooltip } from '../../component/tooltip/seriesFormatTooltip';
type FocusNodeAdjacency = boolean | 'inEdges' | 'outEdges' | 'allEdges';
......
......@@ -19,7 +19,6 @@
import ComponentModel from '../../model/Component';
import List from '../../data/List';
import * as modelUtil from '../../util/model';
import {
ComponentOption,
BoxLayoutOptionMixin,
......@@ -38,6 +37,7 @@ import {
import Model from '../../model/Model';
import GlobalModel, { GlobalModelSetOptionOpts } from '../../model/Global';
import { each, isObject, clone, isString } from 'zrender/src/core/util';
import { convertOptionIdName, getDataItemValue } from '../../util/model';
export interface TimelineControlStyle extends ItemStyleOption {
......@@ -247,7 +247,7 @@ class TimelineModel extends ComponentModel<TimelineOption> {
if (axisType === 'category') {
processedDataArr = [];
each(dataArr, function (item, index) {
let value = modelUtil.getDataItemValue(item);
const value = convertOptionIdName(getDataItemValue(item), '');
let newItem;
if (isObject(item)) {
......@@ -260,11 +260,7 @@ class TimelineModel extends ComponentModel<TimelineOption> {
processedDataArr.push(newItem);
if (!isString(value) && (value == null || isNaN(value as number))) {
value = '';
}
names.push(value + '');
names.push(value);
});
}
else {
......
......@@ -33,9 +33,10 @@ import {ArrayLike, Dictionary, FunctionPropertyNames} from 'zrender/src/core/typ
import Element from 'zrender/src/Element';
import {
DimensionIndex, DimensionName, DimensionLoose, OptionDataItem,
ParsedValue, ParsedValueNumeric, OrdinalNumber, DimensionUserOuput, ModelOption, SeriesDataType
ParsedValue, ParsedValueNumeric, OrdinalNumber, DimensionUserOuput,
ModelOption, SeriesDataType, OrdinalRawValue
} from '../util/types';
import {isDataItemOption} from '../util/model';
import {isDataItemOption, convertOptionIdName} from '../util/model';
import { getECData } from '../util/ecData';
import { PathStyleProps } from 'zrender/src/graphic/Path';
import type Graph from './Graph';
......@@ -144,12 +145,21 @@ export interface DefaultDataVisual {
colorFromPalette?: boolean
}
export interface DataCalculationInfo<SERIES_MODEL> {
stackedDimension: string;
stackedByDimension: string;
isStackedByIndex: boolean;
stackedOverDimension: string;
stackResultDimension: string;
stackedOnSeries?: SERIES_MODEL;
}
// -----------------------------
// Internal method declarations:
// -----------------------------
let defaultDimValueGetters: {[sourceFormat: string]: DimValueGetter};
let prepareInvertedIndex: (list: List) => void;
let getRawValueFromStore: (list: List, dimIndex: number, rawIndex: number) => any;
let getRawValueFromStore: (list: List, dimIndex: number, rawIndex: number) => ParsedValue | OrdinalRawValue;
let getIndicesCtor: (list: List) => DataArrayLikeConstructor;
let prepareChunks: (
storage: DataStorage, dimInfo: DataDimensionInfo, chunkSize: number, chunkCount: number, end: number
......@@ -245,7 +255,7 @@ class List<
private _invertedIndicesMap: {[dimName: string]: ArrayLike<number>};
private _calculationInfo: {[key: string]: any} = {};
private _calculationInfo: DataCalculationInfo<HostModel> = {} as DataCalculationInfo<HostModel>;
// User output info of this data.
// DO NOT use it in other places!
......@@ -628,7 +638,7 @@ class List<
// ??? FIXME not check by pure but sourceFormat?
// TODO refactor these logic.
if (!rawData.pure) {
let name: any = nameList[idx];
let name: string = nameList[idx];
if (dataItem && name == null) {
// If dataItem is {name: ...}, it has highest priority.
......@@ -636,24 +646,26 @@ class List<
if ((dataItem as any).name != null) {
// There is no other place to persistent dataItem.name,
// so save it to nameList.
nameList[idx] = name = (dataItem as any).name;
nameList[idx] = name = convertOptionIdName((dataItem as any).name, null);
}
else if (nameDimIdx != null) {
const nameDim = dimensions[nameDimIdx];
const nameDimChunk = storage[nameDim][chunkIndex];
if (nameDimChunk) {
name = nameDimChunk[chunkOffset];
const ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta;
if (ordinalMeta && ordinalMeta.categories.length) {
name = ordinalMeta.categories[name];
}
name = convertOptionIdName(
(ordinalMeta && ordinalMeta.categories.length)
? ordinalMeta.categories[nameDimChunk[chunkOffset] as number]
: nameDimChunk[chunkOffset],
null
);
}
}
}
// Try using the id in option
// id or name is used on dynamical data, mapping old and new items.
let id = dataItem == null ? null : (dataItem as any).id;
let id: string = dataItem == null ? null : convertOptionIdName((dataItem as any).id, null);
if (id == null && name != null) {
// Use name as id and add counter to avoid same name
......@@ -908,17 +920,29 @@ class List<
this._approximateExtent[dim] = extent.slice() as [number, number];
}
getCalculationInfo(key: string): any {
getCalculationInfo<CALC_INFO_KEY extends keyof DataCalculationInfo<HostModel>>(
key: CALC_INFO_KEY
): DataCalculationInfo<HostModel>[CALC_INFO_KEY] {
return this._calculationInfo[key];
}
/**
* @param key or k-v object
*/
setCalculationInfo(key: string | object, value?: any) {
setCalculationInfo(
key: DataCalculationInfo<HostModel>
): void;
setCalculationInfo<CALC_INFO_KEY extends keyof DataCalculationInfo<HostModel>>(
key: CALC_INFO_KEY,
value: DataCalculationInfo<HostModel>[CALC_INFO_KEY]
): void;
setCalculationInfo(
key: (keyof DataCalculationInfo<HostModel>) | DataCalculationInfo<HostModel>,
value?: DataCalculationInfo<HostModel>[keyof DataCalculationInfo<HostModel>]
): void {
isObject(key)
? zrUtil.extend(this._calculationInfo, key as object)
: (this._calculationInfo[key] = value);
: ((this._calculationInfo as any)[key] = value);
}
/**
......@@ -1140,13 +1164,25 @@ class List<
}
}
/**
* @return Never be null/undefined. `number` will be converted to string. Becuase:
* In most cases, name is used in display, where returning a string is more convenient.
* In other cases, name is used in query (see `indexOfName`), where we can keep the
* rule that name `2` equals to name `'2'`.
*/
getName(idx: number): string {
const rawIndex = this.getRawIndex(idx);
return this._nameList[rawIndex]
|| getRawValueFromStore(this, this._nameDimIdx, rawIndex)
|| convertOptionIdName(getRawValueFromStore(this, this._nameDimIdx, rawIndex), '')
|| '';
}
/**
* @return Never null/undefined. `number` will be converted to string. Becuase:
* In all cases having encountered at present, id is used in making diff comparison, which
* are usually based on hash map. We can keep the rule that the internal id are always string
* (treat `2` is the same as `'2'`) to make the related logic simple.
*/
getId(idx: number): string {
return getId(this, this.getRawIndex(idx));
}
......@@ -1981,7 +2017,9 @@ class List<
});
};
getRawValueFromStore = function (list: List, dimIndex: number, rawIndex: number): any {
getRawValueFromStore = function (
list: List, dimIndex: number, rawIndex: number
): ParsedValue | OrdinalRawValue {
let val;
if (dimIndex != null) {
const chunkSize = list._chunkSize;
......@@ -2043,10 +2081,13 @@ class List<
return -1;
};
/**
* @see the comment of `List['getId']`.
*/
getId = function (list: List, rawIndex: number): string {
let id = list._idList[rawIndex];
if (id == null) {
id = getRawValueFromStore(list, list._idDimIdx, rawIndex);
id = convertOptionIdName(getRawValueFromStore(list, list._idDimIdx, rawIndex), null);
}
if (id == null) {
// FIXME Check the usage in graph, should not use prefix.
......
......@@ -26,8 +26,12 @@ import Model from '../model/Model';
import linkList from './helper/linkList';
import List from './List';
import createDimensions from './helper/createDimensions';
import { DimensionLoose, ParsedValue } from '../util/types';
import {
DimensionLoose, ParsedValue, OptionId, OptionDataValue,
OptionDataItemObject
} from '../util/types';
import { Dictionary } from 'zrender/src/core/types';
import { convertOptionIdName } from '../util/model';
type TreeTraverseOrder = 'preorder' | 'postorder';
type TreeTraverseCallback<Ctx> = (this: Ctx, node: TreeNode) => boolean | void;
......@@ -36,10 +40,8 @@ type TreeTraverseOption = {
attr?: 'children' | 'viewChildren'
};
interface TreeNodeData {
name?: string
value?: any
children?: TreeNodeData[]
interface TreeNodeOption extends Pick<OptionDataItemObject<OptionDataValue>, 'name' | 'value'> {
children?: TreeNodeOption[];
}
export class TreeNode {
......@@ -405,7 +407,7 @@ class Tree<HostModel extends Model = Model, LevelOption = any, LeavesOption = an
* ]
* }
*/
static createTree<T extends TreeNodeData, HostModel extends Model, LevelOption>(
static createTree<T extends TreeNodeOption, HostModel extends Model, LevelOption>(
dataRoot: T,
hostModel: HostModel,
treeOptions?: {
......@@ -415,18 +417,18 @@ class Tree<HostModel extends Model = Model, LevelOption = any, LeavesOption = an
) {
const tree = new Tree(hostModel, treeOptions && treeOptions.levels);
const listData: TreeNodeData[] = [];
const listData: TreeNodeOption[] = [];
let dimMax = 1;
buildHierarchy(dataRoot);
function buildHierarchy(dataNode: TreeNodeData, parentNode?: TreeNode) {
function buildHierarchy(dataNode: TreeNodeOption, parentNode?: TreeNode) {
const value = dataNode.value;
dimMax = Math.max(dimMax, zrUtil.isArray(value) ? value.length : 1);
listData.push(dataNode);
const node = new TreeNode(dataNode.name, tree);
const node = new TreeNode(convertOptionIdName(dataNode.name, ''), tree);
parentNode
? addChild(node, parentNode)
: (tree.root = node);
......
......@@ -20,16 +20,9 @@
import {each, isString} from 'zrender/src/core/util';
import DataDimensionInfo from '../DataDimensionInfo';
import SeriesModel from '../../model/Series';
import List from '../List';
import List, { DataCalculationInfo } from '../List';
import type { SeriesOption, SeriesStackOptionMixin, DimensionName } from '../../util/types';
interface DataStackResult {
stackedDimension: string
stackedByDimension: string
isStackedByIndex: boolean
stackedOverDimension: string
stackResultDimension: string
}
/**
* Note that it is too complicated to support 3d stack by value
......@@ -58,7 +51,14 @@ export function enableDataStack(
stackedCoordDimension?: string
byIndex?: boolean
}
): DataStackResult {
): Pick<
DataCalculationInfo<unknown>,
'stackedDimension'
| 'stackedByDimension'
| 'isStackedByIndex'
| 'stackedOverDimension'
| 'stackResultDimension'
> {
opt = opt || {};
let byIndex = opt.byIndex;
const stackedCoordDimension = opt.stackedCoordDimension;
......
......@@ -41,7 +41,9 @@ import {
OptionDataItem,
OptionDataValue,
TooltipRenderMode,
Payload
Payload,
OptionId,
OptionName
} from './types';
import { Dictionary } from 'zrender/src/core/types';
import SeriesModel from '../model/Series';
......@@ -150,7 +152,7 @@ export function isDataItemOption(dataItem: OptionDataItem): boolean {
// number id will not be converted to string in option.
// number id will be converted to string in component instance id.
export interface MappingExistingItem {
id?: string | number;
id?: OptionId;
name?: string;
};
/**
......@@ -499,7 +501,11 @@ function makeIdAndName(
});
}
function keyExistAndEqual(attr: 'id' | 'name', obj1: MappingExistingItem, obj2: MappingExistingItem): boolean {
function keyExistAndEqual(
attr: 'id' | 'name',
obj1: { id?: OptionId, name?: OptionName },
obj2: { id?: OptionId, name?: OptionName }
): boolean {
const key1 = obj1[attr];
const key2 = obj2[attr];
// See `MappingExistingItem`. `id` and `name` trade string equals to number.
......@@ -509,13 +515,22 @@ function keyExistAndEqual(attr: 'id' | 'name', obj1: MappingExistingItem, obj2:
/**
* @return return null if not exist.
*/
function makeComparableKey(val: string | number): string {
function makeComparableKey(val: unknown): string {
if (__DEV__) {
if (val == null) {
throw new Error();
}
}
return val + '';
return convertOptionIdName(val, '');
}
export function convertOptionIdName(idOrName: unknown, defaultValue: string): string {
const type = typeof idOrName;
return type === 'string'
? idOrName as string
: (type === 'number' || isStringSafe(idOrName))
? idOrName + ''
: defaultValue;
}
export function validateIdOrName(idOrName: unknown) {
......@@ -542,7 +557,7 @@ export function isNameSpecified(componentModel: ComponentModel): boolean {
* @param {Object} cmptOption
* @return {boolean}
*/
export function isComponentIdInternal(cmptOption: MappingExistingItem): boolean {
export function isComponentIdInternal(cmptOption: { id?: MappingExistingItem['id'] }): boolean {
return cmptOption
&& cmptOption.id != null
&& makeComparableKey(cmptOption.id).indexOf(INTERNAL_COMPONENT_ID_PREFIX) === 0;
......@@ -733,8 +748,8 @@ let innerUniqueIndex = getRandomIdBase();
* The priority is: index > id > name, the same with `ecModel.queryComponents`.
*/
export type ModelFinderIndexQuery = number | number[] | 'all' | 'none' | false;
export type ModelFinderIdQuery = number | number[] | string | string[];
export type ModelFinderNameQuery = number | number[] | string | string[];
export type ModelFinderIdQuery = OptionId | OptionId[];
export type ModelFinderNameQuery = OptionId | OptionId[];
export type ModelFinder = string | ModelFinderObject;
export type ModelFinderObject = {
seriesIndex?: ModelFinderIndexQuery, seriesId?: ModelFinderIdQuery, seriesName?: ModelFinderNameQuery
......
......@@ -517,11 +517,15 @@ export type OptionDataItem =
| OptionDataItemObject<OptionDataValue>;
// Only for `SOURCE_FORMAT_KEYED_ORIGINAL`
export type OptionDataItemObject<T> = {
id?: string | number;
name?: string;
id?: OptionId;
name?: OptionName;
value?: T[] | T;
selected?: boolean;
};
// Compat number because it is usually used and not easy to
// restrict it in practise.
export type OptionId = string | number;
export type OptionName = string | number;
export interface GraphEdgeItemObject<
VAL extends OptionDataValue
> extends OptionDataItemObject<VAL> {
......@@ -1247,8 +1251,8 @@ export interface CommonAxisPointerOption {
export interface ComponentOption {
type?: string;
id?: string;
name?: string;
id?: OptionId;
name?: OptionName;
z?: number;
zlevel?: number;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册