From 7c715730dad5092c4299c4745f1fb402d3fee4e8 Mon Sep 17 00:00:00 2001 From: Andrei Kharitonov Date: Fri, 4 Sep 2026 09:34:47 +0200 Subject: [PATCH 1/2] Core(Data): remove m_ prefix from files --- .../js/__internal/data/{m_array_utils.ts => array_utils.ts} | 0 .../{m_operation_manager.ts => operation_manager.ts} | 0 .../js/__internal/data/data_source/{m_utils.ts => utils.ts} | 0 .../data/{m_endpoint_selector.ts => endpoint_selector.ts} | 0 .../devextreme/js/__internal/data/{m_errors.ts => errors.ts} | 0 .../js/__internal/data/{m_local_store.ts => local_store.ts} | 0 packages/devextreme/js/__internal/data/odata/context.ts | 2 +- .../data/odata/{m_query_adapter.ts => query_adapter.ts} | 4 ++-- .../odata/{m_request_dispatcher.ts => request_dispatcher.ts} | 0 .../devextreme/js/__internal/data/{m_query.ts => query.ts} | 0 .../devextreme/js/__internal/data/query_implementation.ts | 2 +- .../js/__internal/data/{m_remote_query.ts => remote_query.ts} | 0 .../js/__internal/data/{m_store_helper.ts => store_helper.ts} | 0 .../devextreme/js/__internal/data/{m_utils.ts => utils.ts} | 0 .../grids/new/grid_core/data_controller/public_methods.ts | 2 +- .../__internal/grids/new/grid_core/data_controller/utils.ts | 2 +- .../grid_core/filtering/header_filter/legacy_header_filter.ts | 2 +- .../devextreme/js/__internal/scheduler/utils/loader/utils.ts | 2 +- packages/devextreme/js/common/data/array_utils.js | 2 +- .../js/common/data/data_source/operation_manager.js | 2 +- packages/devextreme/js/common/data/data_source/utils.js | 2 +- packages/devextreme/js/common/data/endpoint_selector.js | 2 +- packages/devextreme/js/common/data/errors.js | 2 +- packages/devextreme/js/common/data/local_store.js | 2 +- packages/devextreme/js/common/data/odata/query_adapter.js | 2 +- .../devextreme/js/common/data/odata/request_dispatcher.js | 2 +- packages/devextreme/js/common/data/query.js | 2 +- packages/devextreme/js/common/data/remote_query.js | 2 +- packages/devextreme/js/common/data/store_helper.js | 2 +- packages/devextreme/js/common/data/utils.js | 2 +- 30 files changed, 20 insertions(+), 20 deletions(-) rename packages/devextreme/js/__internal/data/{m_array_utils.ts => array_utils.ts} (100%) rename packages/devextreme/js/__internal/data/data_source/{m_operation_manager.ts => operation_manager.ts} (100%) rename packages/devextreme/js/__internal/data/data_source/{m_utils.ts => utils.ts} (100%) rename packages/devextreme/js/__internal/data/{m_endpoint_selector.ts => endpoint_selector.ts} (100%) rename packages/devextreme/js/__internal/data/{m_errors.ts => errors.ts} (100%) rename packages/devextreme/js/__internal/data/{m_local_store.ts => local_store.ts} (100%) rename packages/devextreme/js/__internal/data/odata/{m_query_adapter.ts => query_adapter.ts} (99%) rename packages/devextreme/js/__internal/data/odata/{m_request_dispatcher.ts => request_dispatcher.ts} (100%) rename packages/devextreme/js/__internal/data/{m_query.ts => query.ts} (100%) rename packages/devextreme/js/__internal/data/{m_remote_query.ts => remote_query.ts} (100%) rename packages/devextreme/js/__internal/data/{m_store_helper.ts => store_helper.ts} (100%) rename packages/devextreme/js/__internal/data/{m_utils.ts => utils.ts} (100%) diff --git a/packages/devextreme/js/__internal/data/m_array_utils.ts b/packages/devextreme/js/__internal/data/array_utils.ts similarity index 100% rename from packages/devextreme/js/__internal/data/m_array_utils.ts rename to packages/devextreme/js/__internal/data/array_utils.ts diff --git a/packages/devextreme/js/__internal/data/data_source/m_operation_manager.ts b/packages/devextreme/js/__internal/data/data_source/operation_manager.ts similarity index 100% rename from packages/devextreme/js/__internal/data/data_source/m_operation_manager.ts rename to packages/devextreme/js/__internal/data/data_source/operation_manager.ts diff --git a/packages/devextreme/js/__internal/data/data_source/m_utils.ts b/packages/devextreme/js/__internal/data/data_source/utils.ts similarity index 100% rename from packages/devextreme/js/__internal/data/data_source/m_utils.ts rename to packages/devextreme/js/__internal/data/data_source/utils.ts diff --git a/packages/devextreme/js/__internal/data/m_endpoint_selector.ts b/packages/devextreme/js/__internal/data/endpoint_selector.ts similarity index 100% rename from packages/devextreme/js/__internal/data/m_endpoint_selector.ts rename to packages/devextreme/js/__internal/data/endpoint_selector.ts diff --git a/packages/devextreme/js/__internal/data/m_errors.ts b/packages/devextreme/js/__internal/data/errors.ts similarity index 100% rename from packages/devextreme/js/__internal/data/m_errors.ts rename to packages/devextreme/js/__internal/data/errors.ts diff --git a/packages/devextreme/js/__internal/data/m_local_store.ts b/packages/devextreme/js/__internal/data/local_store.ts similarity index 100% rename from packages/devextreme/js/__internal/data/m_local_store.ts rename to packages/devextreme/js/__internal/data/local_store.ts diff --git a/packages/devextreme/js/__internal/data/odata/context.ts b/packages/devextreme/js/__internal/data/odata/context.ts index 6e070b4c937c..dbd3dadbde42 100644 --- a/packages/devextreme/js/__internal/data/odata/context.ts +++ b/packages/devextreme/js/__internal/data/odata/context.ts @@ -9,7 +9,7 @@ import { each } from '@js/core/utils/iterator'; import { isDefined, isPlainObject } from '@js/core/utils/type'; import type { StoreErrorHandler } from '@ts/data/abstract_store'; -import { errors, handleError } from '../m_errors'; +import { errors, handleError } from '../errors'; import { escapeServiceOperationParams, formatFunctionInvocationUrl } from './utils'; export type ServiceOperationParams = Record; diff --git a/packages/devextreme/js/__internal/data/odata/m_query_adapter.ts b/packages/devextreme/js/__internal/data/odata/query_adapter.ts similarity index 99% rename from packages/devextreme/js/__internal/data/odata/m_query_adapter.ts rename to packages/devextreme/js/__internal/data/odata/query_adapter.ts index 6205484aa0a9..3adfa9edc6f9 100644 --- a/packages/devextreme/js/__internal/data/odata/m_query_adapter.ts +++ b/packages/devextreme/js/__internal/data/odata/query_adapter.ts @@ -4,12 +4,12 @@ import { extend } from '@js/core/utils/extend'; import { each } from '@js/core/utils/iterator'; import { isFunction } from '@js/core/utils/type'; -import { errors } from '../m_errors'; +import { errors } from '../errors'; import { isConjunctiveOperator, isUnaryOperation, normalizeBinaryCriterion, -} from '../m_utils'; +} from '../utils'; import { convertPrimitiveValue, generateExpand, diff --git a/packages/devextreme/js/__internal/data/odata/m_request_dispatcher.ts b/packages/devextreme/js/__internal/data/odata/request_dispatcher.ts similarity index 100% rename from packages/devextreme/js/__internal/data/odata/m_request_dispatcher.ts rename to packages/devextreme/js/__internal/data/odata/request_dispatcher.ts diff --git a/packages/devextreme/js/__internal/data/m_query.ts b/packages/devextreme/js/__internal/data/query.ts similarity index 100% rename from packages/devextreme/js/__internal/data/m_query.ts rename to packages/devextreme/js/__internal/data/query.ts diff --git a/packages/devextreme/js/__internal/data/query_implementation.ts b/packages/devextreme/js/__internal/data/query_implementation.ts index 2654903ad637..d7185d49d29e 100644 --- a/packages/devextreme/js/__internal/data/query_implementation.ts +++ b/packages/devextreme/js/__internal/data/query_implementation.ts @@ -1,5 +1,5 @@ import arrayQueryImpl from './array_query'; -import remoteQueryImpl from './m_remote_query'; +import remoteQueryImpl from './remote_query'; export const queryImpl = { array: arrayQueryImpl, diff --git a/packages/devextreme/js/__internal/data/m_remote_query.ts b/packages/devextreme/js/__internal/data/remote_query.ts similarity index 100% rename from packages/devextreme/js/__internal/data/m_remote_query.ts rename to packages/devextreme/js/__internal/data/remote_query.ts diff --git a/packages/devextreme/js/__internal/data/m_store_helper.ts b/packages/devextreme/js/__internal/data/store_helper.ts similarity index 100% rename from packages/devextreme/js/__internal/data/m_store_helper.ts rename to packages/devextreme/js/__internal/data/store_helper.ts diff --git a/packages/devextreme/js/__internal/data/m_utils.ts b/packages/devextreme/js/__internal/data/utils.ts similarity index 100% rename from packages/devextreme/js/__internal/data/m_utils.ts rename to packages/devextreme/js/__internal/data/utils.ts diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/public_methods.ts b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/public_methods.ts index 4d43b738af41..b1e2ccb921c4 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/public_methods.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/public_methods.ts @@ -5,7 +5,7 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import type { FilterDescriptor } from '@js/data'; import type DataSource from '@js/data/data_source'; -import { keysEqual } from '@ts/data/m_utils'; +import { keysEqual } from '@ts/data/utils'; import type { Constructor } from '../types'; import type { GridCoreNewBase } from '../widget'; diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/utils.ts b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/utils.ts index ac96d84c87ac..b03222a5303b 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/utils.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/utils.ts @@ -6,7 +6,7 @@ import type { Store } from '@js/data'; import type { DataSourceLike } from '@js/data/data_source'; import DataSource from '@js/data/data_source'; import { normalizeDataSourceOptions } from '@js/data/data_source/utils'; -import { applyBatch } from '@ts/data/m_array_utils'; +import { applyBatch } from '@ts/data/array_utils'; import type { DataObject, diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/filtering/header_filter/legacy_header_filter.ts b/packages/devextreme/js/__internal/grids/new/grid_core/filtering/header_filter/legacy_header_filter.ts index 76ee06aa32f8..761f198bce92 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/filtering/header_filter/legacy_header_filter.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/filtering/header_filter/legacy_header_filter.ts @@ -16,7 +16,7 @@ import { isDefined, isFunction, isObject } from '@js/core/utils/type'; import messageLocalization from '@js/localization/message'; import filteringUtils from '@js/ui/shared/filtering'; import { extend } from '@ts/core/utils/m_extend'; -import { normalizeDataSourceOptions as oldNormalizeDataSourceOptions } from '@ts/data/data_source/m_utils'; +import { normalizeDataSourceOptions as oldNormalizeDataSourceOptions } from '@ts/data/data_source/utils'; import { convertDataFromUTCToLocal, getFormatOptions, diff --git a/packages/devextreme/js/__internal/scheduler/utils/loader/utils.ts b/packages/devextreme/js/__internal/scheduler/utils/loader/utils.ts index ccf48a23701c..8a2f31585a13 100644 --- a/packages/devextreme/js/__internal/scheduler/utils/loader/utils.ts +++ b/packages/devextreme/js/__internal/scheduler/utils/loader/utils.ts @@ -1,6 +1,6 @@ import type { DataSourceLike } from '@js/data/data_source'; import DataSource from '@js/data/data_source'; -import { normalizeDataSourceOptions } from '@ts/data/data_source/m_utils'; +import { normalizeDataSourceOptions } from '@ts/data/data_source/utils'; export const normalizeDataSource = ( dataSourceOptions: DataSourceLike | null | undefined, diff --git a/packages/devextreme/js/common/data/array_utils.js b/packages/devextreme/js/common/data/array_utils.js index 74993adf03d4..d3f8e91646bc 100644 --- a/packages/devextreme/js/common/data/array_utils.js +++ b/packages/devextreme/js/common/data/array_utils.js @@ -1 +1 @@ -export * from '../../__internal/data/m_array_utils'; +export * from '../../__internal/data/array_utils'; diff --git a/packages/devextreme/js/common/data/data_source/operation_manager.js b/packages/devextreme/js/common/data/data_source/operation_manager.js index 059f55b8e65f..6de10df1d202 100644 --- a/packages/devextreme/js/common/data/data_source/operation_manager.js +++ b/packages/devextreme/js/common/data/data_source/operation_manager.js @@ -1 +1 @@ -export { default } from '../../../__internal/data/data_source/m_operation_manager'; +export { default } from '../../../__internal/data/data_source/operation_manager'; diff --git a/packages/devextreme/js/common/data/data_source/utils.js b/packages/devextreme/js/common/data/data_source/utils.js index 8b2686832ca6..52e070da838e 100644 --- a/packages/devextreme/js/common/data/data_source/utils.js +++ b/packages/devextreme/js/common/data/data_source/utils.js @@ -1 +1 @@ -export * from '../../../__internal/data/data_source/m_utils'; +export * from '../../../__internal/data/data_source/utils'; diff --git a/packages/devextreme/js/common/data/endpoint_selector.js b/packages/devextreme/js/common/data/endpoint_selector.js index e613c011c0ce..ea7b56fad2d1 100644 --- a/packages/devextreme/js/common/data/endpoint_selector.js +++ b/packages/devextreme/js/common/data/endpoint_selector.js @@ -4,4 +4,4 @@ * @param1 options:Object * @hidden */ -export { default } from '../../__internal/data/m_endpoint_selector'; +export { default } from '../../__internal/data/endpoint_selector'; diff --git a/packages/devextreme/js/common/data/errors.js b/packages/devextreme/js/common/data/errors.js index f2ad9f675151..255b551e3774 100644 --- a/packages/devextreme/js/common/data/errors.js +++ b/packages/devextreme/js/common/data/errors.js @@ -88,4 +88,4 @@ * @name ErrorsData.W4002 */ -export * from '../../__internal/data/m_errors'; +export * from '../../__internal/data/errors'; diff --git a/packages/devextreme/js/common/data/local_store.js b/packages/devextreme/js/common/data/local_store.js index 2b903d32d98b..48ec3d1bcc87 100644 --- a/packages/devextreme/js/common/data/local_store.js +++ b/packages/devextreme/js/common/data/local_store.js @@ -1 +1 @@ -export { default } from '../../__internal/data/m_local_store'; +export { default } from '../../__internal/data/local_store'; diff --git a/packages/devextreme/js/common/data/odata/query_adapter.js b/packages/devextreme/js/common/data/odata/query_adapter.js index a56104665e6c..dc61288953ee 100644 --- a/packages/devextreme/js/common/data/odata/query_adapter.js +++ b/packages/devextreme/js/common/data/odata/query_adapter.js @@ -1 +1 @@ -export * from '../../../__internal/data/odata/m_query_adapter'; +export * from '../../../__internal/data/odata/query_adapter'; diff --git a/packages/devextreme/js/common/data/odata/request_dispatcher.js b/packages/devextreme/js/common/data/odata/request_dispatcher.js index 13fc3dc1b4c6..434e13d5a8eb 100644 --- a/packages/devextreme/js/common/data/odata/request_dispatcher.js +++ b/packages/devextreme/js/common/data/odata/request_dispatcher.js @@ -1 +1 @@ -export { default } from '../../../__internal/data/odata/m_request_dispatcher'; +export { default } from '../../../__internal/data/odata/request_dispatcher'; diff --git a/packages/devextreme/js/common/data/query.js b/packages/devextreme/js/common/data/query.js index b1e2c444e038..bbb50289ac52 100644 --- a/packages/devextreme/js/common/data/query.js +++ b/packages/devextreme/js/common/data/query.js @@ -1 +1 @@ -export { default } from '../../__internal/data/m_query'; +export { default } from '../../__internal/data/query'; diff --git a/packages/devextreme/js/common/data/remote_query.js b/packages/devextreme/js/common/data/remote_query.js index 592ebfe65d2c..51f3df760e1e 100644 --- a/packages/devextreme/js/common/data/remote_query.js +++ b/packages/devextreme/js/common/data/remote_query.js @@ -1 +1 @@ -export { default } from '../../__internal/data/m_remote_query'; +export { default } from '../../__internal/data/remote_query'; diff --git a/packages/devextreme/js/common/data/store_helper.js b/packages/devextreme/js/common/data/store_helper.js index bb0fca23c7d1..5b66fa3425b1 100644 --- a/packages/devextreme/js/common/data/store_helper.js +++ b/packages/devextreme/js/common/data/store_helper.js @@ -1 +1 @@ -export { default } from '../../__internal/data/m_store_helper'; +export { default } from '../../__internal/data/store_helper'; diff --git a/packages/devextreme/js/common/data/utils.js b/packages/devextreme/js/common/data/utils.js index a59212919375..ba1bc28b4c72 100644 --- a/packages/devextreme/js/common/data/utils.js +++ b/packages/devextreme/js/common/data/utils.js @@ -3,7 +3,7 @@ import { compileGetter, compileSetter } from '../../core/utils/data'; /** * @name Utils */ -export * from '../../__internal/data/m_utils'; +export * from '../../__internal/data/utils'; export { compileGetter, From bda4be11fbca771e2fe522541c02f0edfa8effbe Mon Sep 17 00:00:00 2001 From: Andrei Kharitonov Date: Fri, 4 Sep 2026 11:50:14 +0200 Subject: [PATCH 2/2] Core(Data): fix typing and eslint issues --- .../js/__internal/data/abstract_store.ts | 6 + .../js/__internal/data/array_store.ts | 6 - .../js/__internal/data/array_utils.ts | 382 ++++++++++++------ .../js/__internal/data/custom_store.ts | 5 +- .../data/data_controller/data_controller.ts | 1 - .../data/data_source/data_source.ts | 6 +- .../data/data_source/operation_manager.ts | 31 +- .../js/__internal/data/data_source/utils.ts | 185 ++++++--- .../js/__internal/data/endpoint_selector.ts | 38 +- .../devextreme/js/__internal/data/errors.ts | 18 +- .../js/__internal/data/local_store.ts | 56 ++- .../js/__internal/data/odata/query_adapter.ts | 328 +++++++-------- .../data/odata/request_dispatcher.ts | 83 ++-- .../js/__internal/data/odata/store.ts | 9 +- .../devextreme/js/__internal/data/query.ts | 17 +- .../js/__internal/data/remote_query.ts | 241 +++++++---- .../js/__internal/data/store_helper.ts | 134 ++++-- .../devextreme/js/__internal/data/utils.ts | 282 +++++++------ .../grids/data_grid/grouping/m_grouping.ts | 3 + .../data_grid/grouping/m_grouping_expanded.ts | 1 - .../grids/data_grid/summary/m_summary.ts | 2 - .../m_columns_controller.ts | 1 - .../m_data_source_adapter.ts | 3 - .../header_filter/m_header_filter.ts | 5 +- .../js/__internal/grids/grid_core/m_utils.ts | 1 - .../grids/grid_core/search/m_search.ts | 2 - .../new/grid_core/data_controller/utils.ts | 3 +- .../header_filter/legacy_header_filter.ts | 1 - .../pivot_grid/data_source/m_data_source.ts | 1 - .../pivot_grid/local_store/m_local_store.ts | 1 - .../pivot_grid/remote_store/m_remote_store.ts | 1 - .../m_data_source_adapter.ts | 5 - .../scheduler/utils/loader/utils.ts | 2 + .../ui/collection/collection_widget.edit.ts | 1 - .../collection_widget.live_update.ts | 2 - .../hierarchical_collection/data_adapter.ts | 3 +- .../selection/selection.strategy.deferred.ts | 1 - .../selection/selection.strategy.standard.ts | 5 +- .../ui/selection/selection.strategy.ts | 1 - .../ui/shared/ui.editor_factory_mixin.ts | 1 - 40 files changed, 1130 insertions(+), 744 deletions(-) diff --git a/packages/devextreme/js/__internal/data/abstract_store.ts b/packages/devextreme/js/__internal/data/abstract_store.ts index 8987913c2ed7..57979b10ecc9 100644 --- a/packages/devextreme/js/__internal/data/abstract_store.ts +++ b/packages/devextreme/js/__internal/data/abstract_store.ts @@ -156,6 +156,9 @@ class Store { loadOptions._langParams = { ...this._langParams, ...loadOptions._langParams }; } + // @ts-expect-error `createQuery()` is declared with the public `Query` type, whose + // enumerate() promises a native Promise, while the query implementations resolve a + // Deferred (iteration 2: type createQuery() with the internal query interface) const result: DeferredObj = queryByOptions( this.createQuery(loadOptions), loadOptions, @@ -193,6 +196,9 @@ class Store { } _totalCountImpl(options?: StoreLoadOptions): DeferredObj { + // @ts-expect-error `createQuery()` is declared with the public `Query` type, whose + // count() promises a native Promise, while the query implementations resolve a + // Deferred (iteration 2: type createQuery() with the internal query interface) const result: DeferredObj = queryByOptions( this.createQuery(options), options, diff --git a/packages/devextreme/js/__internal/data/array_store.ts b/packages/devextreme/js/__internal/data/array_store.ts index 7ae9ef524ffc..814e5a272bb9 100644 --- a/packages/devextreme/js/__internal/data/array_store.ts +++ b/packages/devextreme/js/__internal/data/array_store.ts @@ -47,27 +47,23 @@ class ArrayStore extends Store { const index = indexByKey(this, this._array, key); if (index === -1) { - // @ts-expect-error data/utils is untyped: `rejectedPromise` reads its arguments object const rejected: DeferredObj = rejectedPromise(errors.Error('E4009')); return rejected; } - // @ts-expect-error data/utils is untyped: `trivialPromise` reads its arguments object const resolved: DeferredObj = trivialPromise(this._array[index]); return resolved; } _insertImpl(values: unknown): DeferredObj { - // @ts-expect-error array_utils is untyped: `insert` declares every argument as required const result: DeferredObj = insert(this, this._array, values); return result; } _pushImpl(changes: StoreChange[]): void { - // @ts-expect-error array_utils is untyped: `applyBatch` destructures every option as required applyBatch({ keyInfo: this, data: this._array, @@ -76,14 +72,12 @@ class ArrayStore extends Store { } _updateImpl(key: unknown, values: unknown): DeferredObj { - // @ts-expect-error array_utils is untyped: `update` declares every argument as required const result: DeferredObj = update(this, this._array, key, values); return result; } _removeImpl(key: unknown): DeferredObj { - // @ts-expect-error array_utils is untyped: `remove` declares every argument as required const result: DeferredObj = remove(this, this._array, key); return result; diff --git a/packages/devextreme/js/__internal/data/array_utils.ts b/packages/devextreme/js/__internal/data/array_utils.ts index 22d11b7c9c69..63f7a786a7f6 100644 --- a/packages/devextreme/js/__internal/data/array_utils.ts +++ b/packages/devextreme/js/__internal/data/array_utils.ts @@ -3,58 +3,75 @@ import { keysEqual, rejectedPromise, trivialPromise } from '@js/common/data/util import config from '@js/core/config'; import Guid from '@js/core/guid'; import { compileGetter } from '@js/core/utils/data'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { extend } from '@js/core/utils/extend'; import { deepExtendArraySafe } from '@js/core/utils/object'; import { - isDefined, isEmptyObject, isObject, isPlainObject, + isDefined, isEmptyObject, isObject, isPlainObject, isString, } from '@js/core/utils/type'; +import type { StoreChange } from '@js/data/store'; import { isCollectionLike } from '@ts/core/utils/m_object'; +import type { StoreKey } from '@ts/data/abstract_store'; -function hasKey(target, keyOrKeys) { - let key; - // @ts-expect-error - const keys = typeof keyOrKeys === 'string' ? keyOrKeys.split() : keyOrKeys.slice(); +export type KeyExpr = StoreKey | Function; - while (keys.length) { - key = keys.shift(); - if (key in target) { - return true; - } - } +export interface KeyInfo { + /* eslint-disable @typescript-eslint/method-signature-style */ + key(): KeyExpr | undefined; + keyOf(obj: unknown): unknown; + /* eslint-enable @typescript-eslint/method-signature-style */ +} - return false; +interface CachedArray extends Array { + _dataByKeyMap?: Record; + _dataByKeyMapLength?: number; } -function findItems(keyInfo, items, key, groupCount) { - let childItems; - let result; +interface GroupItem { + items?: unknown[]; + collapsedItems?: unknown[]; +} - if (groupCount) { - for (let i = 0; i < items.length; i++) { - childItems = items[i].items || items[i].collapsedItems || []; - result = findItems(keyInfo, childItems || [], key, groupCount - 1); - if (result) { - return result; - } - } - } else if (indexByKey(keyInfo, items, key) >= 0) { - return items; - } +export interface ApplyBatchOptions { + keyInfo: KeyInfo; + data: unknown[]; + changes: StoreChange[]; + groupCount?: number; + useInsertIndex?: boolean; + immutable?: boolean; + disableCache?: boolean; + logError?: boolean; + skipCopying?: boolean; } -function getItems(keyInfo, items, key, groupCount) { - if (groupCount) { - return findItems(keyInfo, items, key, groupCount) || []; +export interface ApplyChangesOptions { + keyExpr?: StoreKey; + immutable?: boolean; +} + +function hasKey(target: unknown, keyOrKeys: KeyExpr): boolean { + let keys: string[] = []; + if (isString(keyOrKeys)) { + keys = [keyOrKeys]; + } else if (Array.isArray(keyOrKeys)) { + keys = keyOrKeys.slice(); } - return items; + while (keys.length) { + const key = keys.shift(); + if (isDefined(key) && isObject(target) && key in target) { + return true; + } + } + + return false; } -function generateDataByKeyMap(keyInfo, array) { +function generateDataByKeyMap(keyInfo: KeyInfo, array: CachedArray): void { if (keyInfo.key() && (!array._dataByKeyMap || array._dataByKeyMapLength !== array.length)) { - const dataByKeyMap = {}; + const dataByKeyMap: Record = {}; const arrayLength = array.length; - for (let i = 0; i < arrayLength; i++) { + for (let i = 0; i < arrayLength; i += 1) { dataByKeyMap[JSON.stringify(keyInfo.keyOf(array[i]))] = array[i]; } @@ -63,13 +80,11 @@ function generateDataByKeyMap(keyInfo, array) { } } -function getCacheValue(array, key) { - if (array._dataByKeyMap) { - return array._dataByKeyMap[JSON.stringify(key)]; - } +function getCacheValue(array: CachedArray, key: unknown): unknown { + return array._dataByKeyMap?.[JSON.stringify(key)]; } -function getHasKeyCacheValue(array, key) { +function getHasKeyCacheValue(array: CachedArray, key: unknown): unknown { if (array._dataByKeyMap) { return array._dataByKeyMap[JSON.stringify(key)]; } @@ -77,39 +92,98 @@ function getHasKeyCacheValue(array, key) { return true; } -function setDataByKeyMapValue(array, key, data) { +function setDataByKeyMapValue(array: CachedArray, key: unknown, data: unknown): void { if (array._dataByKeyMap) { array._dataByKeyMap[JSON.stringify(key)] = data; - array._dataByKeyMapLength += data ? 1 : -1; + array._dataByKeyMapLength = (array._dataByKeyMapLength ?? 0) + (data ? 1 : -1); } } -function cloneInstanceWithChangedPaths(instance, changes, clonedInstances) { +function indexByKey(keyInfo: KeyInfo, array: unknown[], key: unknown): number { + const keyExpr = keyInfo.key(); + + if (!getHasKeyCacheValue(array, key)) { + return -1; + } + + for (let i = 0, arrayLength = array.length; i < arrayLength; i += 1) { + if (keysEqual(keyExpr, keyInfo.keyOf(array[i]), key)) { + return i; + } + } + return -1; +} + +function findItems( + keyInfo: KeyInfo, + items: unknown[], + key: unknown, + groupCount: number, +): unknown[] | undefined { + if (groupCount) { + for (const item of items) { + const group: GroupItem = isObject(item) ? item : {}; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const childItems = group.items || group.collapsedItems || []; + const result = findItems(keyInfo, childItems, key, groupCount - 1); + if (result) { + return result; + } + } + } else if (indexByKey(keyInfo, items, key) >= 0) { + return items; + } + + return undefined; +} + +function getItems( + keyInfo: KeyInfo, + items: unknown[], + key: unknown, + groupCount: number | undefined, +): unknown[] { + if (groupCount) { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + return findItems(keyInfo, items, key, groupCount) || []; + } + + return items; +} + +function cloneInstanceWithChangedPaths( + instance: unknown, + changes: unknown, + clonedInstances: WeakMap = new WeakMap(), +): unknown { if (isCollectionLike(instance)) { return instance; } - clonedInstances = clonedInstances || new WeakMap(); + const source: object = isObject(instance) ? instance : {}; + const result: object = isObject(instance) + ? Object.create(Object.getPrototypeOf(instance)) + : {}; - const result = instance ? Object.create(Object.getPrototypeOf(instance)) : {}; - if (instance) { + if (isObject(instance)) { clonedInstances.set(instance, result); } - const instanceWithoutPrototype = { ...instance }; + const instanceWithoutPrototype = { ...source }; deepExtendArraySafe(result, instanceWithoutPrototype, true, true, true); // eslint-disable-next-line no-restricted-syntax, guard-for-in for (const name in instanceWithoutPrototype) { - const value = instanceWithoutPrototype[name]; - const change = changes?.[name]; + const value: unknown = instanceWithoutPrototype[name]; + const change: unknown = isObject(changes) ? changes[name] : undefined; - if (isObject(value) && !isPlainObject(value) && isObject(change) && !clonedInstances.has(value)) { + if (isObject(value) && !isPlainObject(value) + && isObject(change) && !clonedInstances.has(value)) { result[name] = cloneInstanceWithChangedPaths(value, change, clonedInstances); } } // eslint-disable-next-line no-restricted-syntax, guard-for-in for (const name in result) { - const prop = result[name]; + const prop: unknown = result[name]; if (isObject(prop) && clonedInstances.has(prop)) { result[name] = clonedInstances.get(prop); @@ -119,59 +193,51 @@ function cloneInstanceWithChangedPaths(instance, changes, clonedInstances) { return result; } -function createObjectWithChanges(target, changes) { - // @ts-expect-error +function createObjectWithChanges( + target: unknown, + changes?: unknown, +): Record { const result = cloneInstanceWithChangedPaths(target, changes); + const extended: Record = deepExtendArraySafe(result, changes, true, true, true); - return deepExtendArraySafe(result, changes, true, true, true); + return extended; } -function applyBatch({ - keyInfo, data, changes, groupCount, useInsertIndex, immutable, disableCache, logError, skipCopying, -}) { - const resultItems = immutable === true ? [...data] : data; - - changes.forEach((item) => { - const items = item.type === 'insert' ? resultItems : getItems(keyInfo, resultItems, item.key, groupCount); +function getErrorResult( + isBatch: boolean | undefined, + logError: boolean | undefined, + errorCode: string, +): DeferredObj | undefined { + if (!isBatch) { + const error: unknown = errors.Error(errorCode); + return rejectedPromise(error); + } - !disableCache && generateDataByKeyMap(keyInfo, items); - // eslint-disable-next-line default-case - switch (item.type) { - case 'update': update(keyInfo, items, item.key, item.data, true, immutable, logError); break; - case 'insert': insert(keyInfo, items, item.data, useInsertIndex && isDefined(item.index) ? item.index : -1, true, logError, skipCopying); break; - case 'remove': remove(keyInfo, items, item.key, true, logError); break; - } - }); - return resultItems; -} + if (logError) { + errors.log(errorCode); + } -function getErrorResult(isBatch, logError, errorCode) { - // @ts-expect-error - return !isBatch ? rejectedPromise(errors.Error(errorCode)) : logError && errors.log(errorCode); + return undefined; } -function applyChanges(data, changes, options = {}) { - // @ts-expect-error - const { keyExpr = 'id', immutable = true } = options; - const keyGetter = compileGetter(keyExpr); - const keyInfo = { - key: () => keyExpr, - // @ts-expect-error - keyOf: (obj) => keyGetter(obj), - }; - // @ts-expect-error - return applyBatch({ - keyInfo, - data, - changes, - immutable, - disableCache: true, - logError: true, - }); -} - -function update(keyInfo, array, key, data, isBatch, immutable, logError) { - let target; +function update( + keyInfo: KeyInfo, array: unknown[], key: unknown, data: unknown, +): DeferredObj; +function update( + keyInfo: KeyInfo, array: unknown[], key: unknown, data: unknown, + isBatch: true, immutable?: boolean, logError?: boolean, +): undefined; +function update( + keyInfo: KeyInfo, + array: unknown[], + key: unknown, + data: unknown, + isBatch?: boolean, + immutable?: boolean, + logError?: boolean, +): DeferredObj | undefined { + // eslint-disable-next-line @typescript-eslint/init-declarations + let target: unknown; const extendComplexObject = true; const keyExpr = keyInfo.key(); @@ -192,8 +258,7 @@ function update(keyInfo, array, key, data, isBatch, immutable, logError) { if (immutable === true && isDefined(target)) { const newTarget = createObjectWithChanges(target, data); array[index] = newTarget; - // @ts-expect-error - return !isBatch && trivialPromise(newTarget, key); + return isBatch ? undefined : trivialPromise(newTarget, key); } } } else { @@ -203,34 +268,56 @@ function update(keyInfo, array, key, data, isBatch, immutable, logError) { deepExtendArraySafe(target, data, extendComplexObject, false, true, true); if (!isBatch) { if (config().useLegacyStoreResult) { - // @ts-expect-error return trivialPromise(key, data); } - // @ts-expect-error return trivialPromise(target, key); } + + return undefined; } -function insert(keyInfo, array, data, index, isBatch, logError, skipCopying) { - let keyValue; +function insert( + keyInfo: KeyInfo, array: unknown[], data: unknown, index?: number, +): DeferredObj; +function insert( + keyInfo: KeyInfo, array: unknown[], data: unknown, index: number | undefined, + isBatch: true, logError?: boolean, skipCopying?: boolean, +): undefined; +function insert( + keyInfo: KeyInfo, + array: unknown[], + data: unknown, + index?: number, + isBatch?: boolean, + logError?: boolean, + skipCopying?: boolean, +): DeferredObj | undefined { + // eslint-disable-next-line @typescript-eslint/init-declarations + let keyValue: unknown; const keyExpr = keyInfo.key(); - const obj = isPlainObject(data) && !skipCopying ? extend({}, data) : data; + const obj: unknown = isPlainObject(data) && !skipCopying ? extend({}, data) : data; if (keyExpr) { keyValue = keyInfo.keyOf(obj); - if (keyValue === undefined || typeof keyValue === 'object' && isEmptyObject(keyValue)) { - if (Array.isArray(keyExpr)) { + if (keyValue === undefined || (typeof keyValue === 'object' && isEmptyObject(keyValue))) { + if (!isString(keyExpr)) { throw errors.Error('E4007'); } - keyValue = obj[keyExpr] = String(new Guid()); + + const generatedKey = String(new Guid()); + + if (isObject(obj)) { + obj[keyExpr] = generatedKey; + } + keyValue = generatedKey; } else if (array[indexByKey(keyInfo, array, keyValue)] !== undefined) { return getErrorResult(isBatch, logError, 'E4008'); } } else { keyValue = obj; } - if (index >= 0) { + if (isDefined(index) && index >= 0) { array.splice(index, 0, obj); } else { array.push(obj); @@ -239,38 +326,95 @@ function insert(keyInfo, array, data, index, isBatch, logError, skipCopying) { setDataByKeyMapValue(array, keyValue, obj); if (!isBatch) { - // @ts-expect-error return trivialPromise(config().useLegacyStoreResult ? data : obj, keyValue); } + + return undefined; } -function remove(keyInfo, array, key, isBatch, logError) { +function remove( + keyInfo: KeyInfo, array: unknown[], key: unknown, +): DeferredObj; +function remove( + keyInfo: KeyInfo, array: unknown[], key: unknown, isBatch: true, logError?: boolean, +): undefined; +function remove( + keyInfo: KeyInfo, + array: unknown[], + key: unknown, + isBatch?: boolean, + logError?: boolean, +): DeferredObj | undefined { const index = indexByKey(keyInfo, array, key); if (index > -1) { array.splice(index, 1); setDataByKeyMapValue(array, key, null); } if (!isBatch) { - // @ts-expect-error return trivialPromise(key); - } if (index < 0) { + } + if (index < 0) { return getErrorResult(isBatch, logError, 'E4009'); } + + return undefined; } -function indexByKey(keyInfo, array, key) { - const keyExpr = keyInfo.key(); +function applyBatch({ + keyInfo, data, changes, groupCount, useInsertIndex, immutable, + disableCache, logError, skipCopying, +}: ApplyBatchOptions): unknown[] { + const resultItems = immutable === true ? [...data] : data; - if (!getHasKeyCacheValue(array, key)) { - return -1; - } + changes.forEach((item) => { + const items = item.type === 'insert' + ? resultItems + : getItems(keyInfo, resultItems, item.key, groupCount); - for (let i = 0, arrayLength = array.length; i < arrayLength; i++) { - if (keysEqual(keyExpr, keyInfo.keyOf(array[i]), key)) { - return i; + if (!disableCache) { + generateDataByKeyMap(keyInfo, items); } - } - return -1; + + const insertIndex = useInsertIndex && isDefined(item.index) ? item.index : -1; + + // eslint-disable-next-line default-case + switch (item.type) { + case 'update': + update(keyInfo, items, item.key, item.data, true, immutable, logError); + break; + case 'insert': + insert(keyInfo, items, item.data, insertIndex, true, logError, skipCopying); + break; + case 'remove': + remove(keyInfo, items, item.key, true, logError); + break; + } + }); + return resultItems; +} + +function applyChanges( + data: unknown[], + changes: StoreChange[], + options: ApplyChangesOptions = {}, +): unknown[] { + const { keyExpr = 'id', immutable = true } = options; + // @ts-expect-error core/utils/data.d.ts types compileGetter as `(expr: string) => unknown`, + // although it also accepts a compound key expression and returns a getter function + const keyGetter: Function = compileGetter(keyExpr); + const keyInfo: KeyInfo = { + key: () => keyExpr, + keyOf: (obj: unknown): unknown => keyGetter(obj), + }; + + return applyBatch({ + keyInfo, + data, + changes, + immutable, + disableCache: true, + logError: true, + }); } export { diff --git a/packages/devextreme/js/__internal/data/custom_store.ts b/packages/devextreme/js/__internal/data/custom_store.ts index 90e720d4056c..77d22482a4c7 100644 --- a/packages/devextreme/js/__internal/data/custom_store.ts +++ b/packages/devextreme/js/__internal/data/custom_store.ts @@ -209,7 +209,7 @@ function runRawLoadWithQuery( const rawDataQuery = arrayQuery(rawData, { errorHandler: store._errorHandler }); const waitList: DeferredObj[] = []; - const result: { items?: unknown[]; totalCount?: unknown } = {}; + const result: { items?: unknown; totalCount?: unknown } = {}; if (!countOnly) { const itemsQuery = storeHelper.queryByOptions(rawDataQuery, loadOptions, false); @@ -217,7 +217,7 @@ function runRawLoadWithQuery( result.items = rawData.slice(0); } else { const itemsPromise: DeferredObj = itemsQuery.enumerate() - .done((asyncResult: unknown[]) => { + .done((asyncResult: unknown) => { result.items = asyncResult; }); waitList.push(itemsPromise); @@ -367,7 +367,6 @@ class CustomStore extends Store { _pushImpl(changes: StoreChange[]): void { if (this.__rawData) { - // @ts-expect-error array_utils is untyped: `applyBatch` destructures every option as required applyBatch({ keyInfo: this, data: this.__rawData, diff --git a/packages/devextreme/js/__internal/data/data_controller/data_controller.ts b/packages/devextreme/js/__internal/data/data_controller/data_controller.ts index 840781fd8302..3b4d5b4bf14f 100644 --- a/packages/devextreme/js/__internal/data/data_controller/data_controller.ts +++ b/packages/devextreme/js/__internal/data/data_controller/data_controller.ts @@ -65,7 +65,6 @@ class DataController { this._isSharedDataSource = true; this._dataSource = dataSourceOptions; } else { - // @ts-expect-error const normalizedDataSourceOptions = normalizeDataSourceOptions(dataSourceOptions); this._dataSource = new DataSource( extend(true, {}, {}, normalizedDataSourceOptions), diff --git a/packages/devextreme/js/__internal/data/data_source/data_source.ts b/packages/devextreme/js/__internal/data/data_source/data_source.ts index 72aae3df3fab..1f1cc8ee8fec 100644 --- a/packages/devextreme/js/__internal/data/data_source/data_source.ts +++ b/packages/devextreme/js/__internal/data/data_source/data_source.ts @@ -28,6 +28,7 @@ import type { ChangedEvent, DataSourceEventName, EventOptionName, LoadOperation, LoadResult, NormalizedDataSourceOptions, StoreLoadOptions, } from './types'; +import type { Mapper } from './utils'; // Mirrors the coercion the global `isFinite` applies to non-numeric values. const isFiniteValue = (value: unknown): value is number => isFinite(Number(value)); @@ -58,11 +59,11 @@ export class DataSource { _onPushHandler: Function; - _aggregationTimeoutId?: number; + _aggregationTimeoutId?: ReturnType; _storeLoadOptions: StoreLoadOptions; - _mapFunc?: Function; + _mapFunc?: Mapper; _postProcessFunc?: Function; @@ -567,7 +568,6 @@ export class DataSource { dataSourceChanges = changingArgs.postProcessChanges(dataSourceChanges); } - // @ts-expect-error array_utils is untyped: `applyBatch` destructures every option as required applyBatch({ keyInfo: this.store(), data: items, diff --git a/packages/devextreme/js/__internal/data/data_source/operation_manager.ts b/packages/devextreme/js/__internal/data/data_source/operation_manager.ts index cfde35fffb6f..4cb21cb79b92 100644 --- a/packages/devextreme/js/__internal/data/data_source/operation_manager.ts +++ b/packages/devextreme/js/__internal/data/data_source/operation_manager.ts @@ -1,32 +1,24 @@ import { CANCELED_TOKEN } from '@js/common/data/data_source/utils'; +import type { DeferredObj } from '@js/core/utils/deferred'; export default class OperationManager { - constructor() { - // @ts-expect-error - this._counter = -1; - // @ts-expect-error - this._deferreds = {}; - } + _counter = -1; + + _deferreds: Record> = {}; - add(deferred) { - // @ts-expect-error - this._counter++; - // @ts-expect-error + add(deferred: DeferredObj): number { + this._counter += 1; this._deferreds[this._counter] = deferred; - // @ts-expect-error return this._counter; } - remove(operationId) { - // @ts-expect-error + remove(operationId: number): boolean { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete return delete this._deferreds[operationId]; } - cancel(operationId) { - // @ts-expect-error + cancel(operationId: number): boolean { if (operationId in this._deferreds) { - // @ts-expect-error this._deferreds[operationId].reject(CANCELED_TOKEN); return true; } @@ -34,13 +26,10 @@ export default class OperationManager { return false; } - cancelAll() { - // @ts-expect-error + cancelAll(): void { while (this._counter > -1) { - // @ts-expect-error this.cancel(this._counter); - // @ts-expect-error - this._counter--; + this._counter -= 1; } } } diff --git a/packages/devextreme/js/__internal/data/data_source/utils.ts b/packages/devextreme/js/__internal/data/data_source/utils.ts index 40192c669e20..d7a37ca492dc 100644 --- a/packages/devextreme/js/__internal/data/data_source/utils.ts +++ b/packages/devextreme/js/__internal/data/data_source/utils.ts @@ -2,120 +2,175 @@ import ArrayStore from '@js/common/data/array_store'; import { CustomStore } from '@js/common/data/custom_store'; import { normalizeSortingInfo } from '@js/common/data/utils'; import ajaxUtils from '@js/core/utils/ajax'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { extend } from '@js/core/utils/extend'; -import { each, map } from '@js/core/utils/iterator'; -import { isPlainObject } from '@js/core/utils/type'; -import Store from '@js/data/abstract_store'; +import { isObject, isPlainObject } from '@js/core/utils/type'; +import Store from '@ts/data/abstract_store'; + +import type { NormalizedDataSourceOptions } from './types'; export const CANCELED_TOKEN = 'canceled'; -export const isPending = (deferred) => deferred.state() === 'pending'; +export type Mapper = (item: unknown) => unknown; + +interface GroupItem { + items?: unknown[]; +} + +export interface NormalizationOptions { + fromUrlLoadMode?: string; +} + +interface DataSourceOptionsInput { + store?: unknown; +} + +export const isPending = (deferred: DeferredObj): boolean => deferred.state() === 'pending'; -export const normalizeStoreLoadOptionAccessorArguments = (originalArguments) => { - // eslint-disable-next-line default-case +export const normalizeStoreLoadOptionAccessorArguments = ( + originalArguments: unknown[], +): unknown => { switch (originalArguments.length) { case 0: return undefined; case 1: return originalArguments[0]; + default: + return originalArguments.slice(); } - return [].slice.call(originalArguments); }; -const mapGroup = (group, level, mapper) => map(group, (item) => { - const { items, ...restItem } = item; - return { - ...restItem, - items: mapRecursive(item.items, level - 1, mapper), - }; -}); - -const mapRecursive = (items, level, mapper) => { +const mapRecursive = (items: unknown, level: number, mapper: Mapper): unknown => { if (!Array.isArray(items)) return items; - return level ? mapGroup(items, level, mapper) : map(items, mapper); + + if (!level) { + return items.map(mapper); + } + + return items.map((item) => { + const groupItem: GroupItem = isObject(item) ? item : {}; + + return { + ...groupItem, + items: mapRecursive(groupItem.items, level - 1, mapper), + }; + }); }; -export const mapDataRespectingGrouping = (items, mapper, groupInfo) => { +export const mapDataRespectingGrouping = ( + items: unknown[], + mapper: Mapper, + groupInfo?: unknown, +): unknown[] => { const level = groupInfo ? normalizeSortingInfo(groupInfo).length : 0; + const mapped = mapRecursive(items, level, mapper); - return mapRecursive(items, level, mapper); + return Array.isArray(mapped) ? mapped : []; }; -export const normalizeLoadResult = (data, extra) => { - if (data?.data) { - extra = data; - data = data.data; - } +export interface NormalizedLoadResult { + data: unknown[]; + extra: unknown; +} - if (!Array.isArray(data)) { - data = [data]; - } +export const normalizeLoadResult = (data: unknown, extra?: unknown): NormalizedLoadResult => { + const loadResult: { data?: unknown } = isObject(data) ? data : {}; + + const resultData: unknown = loadResult.data ? loadResult.data : data; + const resultExtra: unknown = loadResult.data ? data : extra; return { - data, - extra, + data: Array.isArray(resultData) ? resultData : [resultData], + extra: resultExtra, }; }; -const createCustomStoreFromLoadFunc = (options) => { - const storeConfig = {}; +const CUSTOM_STORE_OPTION_NAMES = [ + 'useDefaultSearch', 'key', 'load', 'loadMode', 'cacheRawData', 'byKey', + 'lookup', 'totalCount', 'insert', 'update', 'remove', +]; + +const createCustomStoreFromLoadFunc = (options: DataSourceOptionsInput): CustomStore => { + const storeConfig: Record = {}; - each(['useDefaultSearch', 'key', 'load', 'loadMode', 'cacheRawData', 'byKey', 'lookup', 'totalCount', 'insert', 'update', 'remove'], function () { - storeConfig[this] = options[this]; + CUSTOM_STORE_OPTION_NAMES.forEach((optionName) => { + storeConfig[optionName] = options[optionName]; // eslint-disable-next-line @typescript-eslint/no-dynamic-delete - delete options[this]; + delete options[optionName]; }); + return new CustomStore(storeConfig); }; -const createStoreFromConfig = (storeConfig) => { - const alias = storeConfig.type; +const createStoreFromConfig = (storeConfig: Record): Store => { + const alias = String(storeConfig.type); delete storeConfig.type; - // @ts-expect-error - return Store.create(alias, storeConfig); + + const store: Store = Store.create(alias, storeConfig); + + return store; }; -const createCustomStoreFromUrl = (url, normalizationOptions) => new CustomStore({ - load: () => ajaxUtils.sendRequest({ url, dataType: 'json' }), +const createCustomStoreFromUrl = ( + url: string, + normalizationOptions?: NormalizationOptions, +): CustomStore => new CustomStore({ + load: (): unknown => ajaxUtils.sendRequest({ url, dataType: 'json' }), loadMode: normalizationOptions?.fromUrlLoadMode, }); -export const normalizeDataSourceOptions = (options, normalizationOptions) => { - let store; - - if (typeof options === 'string') { - options = { - paginate: false, - store: createCustomStoreFromUrl(options, normalizationOptions), - }; +const resolveStore = (options: DataSourceOptionsInput): Store => { + if ('load' in options) { + return createCustomStoreFromLoadFunc(options); } - if (options === undefined) { - options = []; + const { store } = options; + + if (Array.isArray(store)) { + return new ArrayStore(store); + } + if (isPlainObject(store)) { + return createStoreFromConfig(extend({}, store)); } - if (Array.isArray(options) || options instanceof Store) { - options = { store: options }; - } else { - options = extend({}, options); + // Anything else is passed through the way it was before this module was typed: + // a value that is not a store fails later, in the data source itself. + // @ts-expect-error the `store` option is user-provided and is not necessarily a Store + const passedThrough: Store = store; + + return passedThrough; +}; + +export const normalizeDataSourceOptions = ( + options: unknown, + normalizationOptions?: NormalizationOptions, +): NormalizedDataSourceOptions => { + let source: unknown = options; + + if (typeof source === 'string') { + source = { + paginate: false, + store: createCustomStoreFromUrl(source, normalizationOptions), + }; } - if (options.store === undefined) { - options.store = []; + if (source === undefined) { + source = []; } - store = options.store; + const normalized: DataSourceOptionsInput = Array.isArray(source) || source instanceof Store + ? { store: source } + : extend({}, source); - if ('load' in options) { - store = createCustomStoreFromLoadFunc(options); - } else if (Array.isArray(store)) { - store = new ArrayStore(store); - } else if (isPlainObject(store)) { - store = createStoreFromConfig(extend({}, store)); + if (normalized.store === undefined) { + normalized.store = []; } - options.store = store; + const store = resolveStore(normalized); - return options; + return { + ...normalized, + store, + }; }; diff --git a/packages/devextreme/js/__internal/data/endpoint_selector.ts b/packages/devextreme/js/__internal/data/endpoint_selector.ts index 22f200d13ddd..7726e93a3db4 100644 --- a/packages/devextreme/js/__internal/data/endpoint_selector.ts +++ b/packages/devextreme/js/__internal/data/endpoint_selector.ts @@ -4,36 +4,44 @@ import { getWindow } from '@js/core/utils/window'; const window = getWindow(); -let IS_WINJS_ORIGIN; -let IS_LOCAL_ORIGIN; +interface Endpoint { + local: string; + production?: string; +} + +type EndpointConfig = Record; + +let isWinJsOrigin = false; +let isLocalOrigin = false; -function isLocalHostName(url) { +function isLocalHostName(url: string): boolean { return /^(localhost$|127\.)/i.test(url); // TODO more precise check for 127.x.x.x IP } -const EndpointSelector = function (config) { - this.config = config; - IS_WINJS_ORIGIN = window.location.protocol === 'ms-appx:'; - IS_LOCAL_ORIGIN = isLocalHostName(window.location.hostname); -}; +class EndpointSelector { + config: EndpointConfig; -EndpointSelector.prototype = { - urlFor(key) { + constructor(config: EndpointConfig) { + this.config = config; + isWinJsOrigin = window.location.protocol === 'ms-appx:'; + isLocalOrigin = isLocalHostName(window.location.hostname); + } + + urlFor(key: string): string { const bag = this.config[key]; if (!bag) { throw errors.Error('E0006'); } if (bag.production) { - // @ts-expect-error - if (IS_WINJS_ORIGIN && !Debug.debuggerEnabled || !IS_WINJS_ORIGIN && !IS_LOCAL_ORIGIN) { + // @ts-expect-error `Debug` is a WinJS global that has no ambient declaration here + if ((isWinJsOrigin && !Debug.debuggerEnabled) || (!isWinJsOrigin && !isLocalOrigin)) { return bag.production; } } return bag.local; - }, - -}; + } +} export default EndpointSelector; diff --git a/packages/devextreme/js/__internal/data/errors.ts b/packages/devextreme/js/__internal/data/errors.ts index 0268bd78af3d..076821979898 100644 --- a/packages/devextreme/js/__internal/data/errors.ts +++ b/packages/devextreme/js/__internal/data/errors.ts @@ -1,5 +1,6 @@ import coreErrors from '@js/core/errors'; import errorUtils from '@js/core/utils/error'; +import { isObject } from '@js/core/utils/type'; export const errors = errorUtils(coreErrors.ERROR_MESSAGES, { @@ -59,15 +60,20 @@ export const errors = errorUtils(coreErrors.ERROR_MESSAGES, { W4002: 'Data loading has failed for some cells due to the following error: {0}', }); + +export type DataErrorHandler = (error: unknown) => void; + // eslint-disable-next-line import/no-mutable-exports -export let errorHandler = null; -export const handleError = function (error) { +export let errorHandler: DataErrorHandler | null = null; + +export const handleError = function (error: unknown): void { /// #DEBUG - const id = error && '__id' in error ? error.__id : 'E4000'; + const id = isObject(error) && '__id' in error ? error.__id : 'E4000'; errors.log(id, error); /// #ENDDEBUG - // @ts-expect-error errorHandler?.(error); }; -// eslint-disable-next-line no-return-assign -export const setErrorHandler = (handler) => errorHandler = handler; + +export const setErrorHandler = (handler: DataErrorHandler | null): void => { + errorHandler = handler; +}; diff --git a/packages/devextreme/js/__internal/data/local_store.ts b/packages/devextreme/js/__internal/data/local_store.ts index 14c956b01cd9..7bb8e4121ab8 100644 --- a/packages/devextreme/js/__internal/data/local_store.ts +++ b/packages/devextreme/js/__internal/data/local_store.ts @@ -3,14 +3,26 @@ import eventsEngine from '@js/common/core/events/core/events_engine'; import ArrayStore from '@js/common/data/array_store'; import { errors } from '@js/common/data/errors'; import domAdapter from '@js/core/dom_adapter'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { getWindow } from '@js/core/utils/window'; +import type { ArrayStoreOptions } from '@ts/data/array_store'; import Store from './abstract_store'; const window = getWindow(); +export interface LocalStoreOptions extends ArrayStoreOptions { + name?: string; + immediate?: boolean; + flushInterval?: number; +} + +interface LocalStoreData { + _array: unknown[]; +} + class LocalStoreBackend { - _store: any; + _store: LocalStoreData; _dirty: boolean; @@ -18,7 +30,7 @@ class LocalStoreBackend { _key: string; - constructor(store, storeOptions) { + constructor(store: LocalStoreData, storeOptions: LocalStoreOptions) { this._store = store; this._dirty = !!storeOptions.data; @@ -31,14 +43,16 @@ class LocalStoreBackend { this.save(); - const immediate = this._immediate = storeOptions.immediate; + const immediate = storeOptions.immediate ?? false; + this._immediate = immediate; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const flushInterval = Math.max(100, storeOptions.flushInterval || 10 * 1000); if (!immediate) { const saveProxy = this.save.bind(this); setInterval(saveProxy, flushInterval); eventsEngine.on(window, 'beforeunload', saveProxy); - // @ts-expect-error + // @ts-expect-error `cordova` is injected by the Cordova container, `Window` has no such field if (window.cordova) { domAdapter.listen(domAdapter.getDocument(), 'pause', saveProxy, false); } @@ -66,16 +80,17 @@ class LocalStoreBackend { this._dirty = false; } - _loadImpl(): any { + _loadImpl(): unknown[] { const raw = window.localStorage.getItem(this._key); if (raw) { - return JSON.parse(raw); + const stored: unknown = JSON.parse(raw); + return Array.isArray(stored) ? stored : []; } return []; } - _saveImpl(array): void { + _saveImpl(array: unknown[]): void { if (!array.length) { window.localStorage.removeItem(this._key); } else { @@ -83,22 +98,21 @@ class LocalStoreBackend { } } } + class LocalStore extends ArrayStore { _backend: LocalStoreBackend; - _array: any; - - constructor(options) { - if (typeof options === 'string') { - options = { name: options }; - } else { - options = options || {}; - } + constructor(options?: LocalStoreOptions | string) { + const storeOptions: LocalStoreOptions = typeof options === 'string' + ? { name: options } + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + : options || {}; - super(options); - this._array = options.data || []; + super(storeOptions); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + this._array = storeOptions.data || []; - this._backend = new LocalStoreBackend(this, options); + this._backend = new LocalStoreBackend(this, storeOptions); this._backend.load(); } @@ -111,17 +125,17 @@ class LocalStore extends ArrayStore { this._backend.notifyChanged(); } - _insertImpl(values): any { + _insertImpl(values: unknown): DeferredObj { const b = this._backend; return super._insertImpl(values).done(b.notifyChanged.bind(b)); } - _updateImpl(key, values): any { + _updateImpl(key: unknown, values: unknown): DeferredObj { const b = this._backend; return super._updateImpl(key, values).done(b.notifyChanged.bind(b)); } - _removeImpl(key): any { + _removeImpl(key: unknown): DeferredObj { const b = this._backend; return super._removeImpl(key).done(b.notifyChanged.bind(b)); } diff --git a/packages/devextreme/js/__internal/data/odata/query_adapter.ts b/packages/devextreme/js/__internal/data/odata/query_adapter.ts index 3adfa9edc6f9..932de8a1bef3 100644 --- a/packages/devextreme/js/__internal/data/odata/query_adapter.ts +++ b/packages/devextreme/js/__internal/data/odata/query_adapter.ts @@ -1,8 +1,9 @@ import queryAdapters from '@js/common/data/query_adapters'; import config from '@js/core/config'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { extend } from '@js/core/utils/extend'; -import { each } from '@js/core/utils/iterator'; -import { isFunction } from '@js/core/utils/type'; +import { isFunction, isString } from '@js/core/utils/type'; +import type { QueryAdapter, RemoteQueryOptions, RemoteTask } from '@ts/data/remote_query'; import { errors } from '../errors'; import { @@ -12,6 +13,7 @@ import { } from '../utils'; import { convertPrimitiveValue, + EdmLiteral, generateExpand, generateSelect, sendRequest, @@ -22,96 +24,112 @@ import { const DEFAULT_PROTOCOL_VERSION = 4; const STRING_FUNCTIONS = ['contains', 'notcontains', 'startswith', 'endswith']; +type Formatter = (prop: string, val: string) => string; + +type FieldTypes = Record; + +interface ODataRequestParams { + [param: string]: unknown; + $orderby?: string; + $skip?: number; + $top?: number; + $select?: string; + $expand?: string; + $filter?: string; + // eslint-disable-next-line spellcheck/spell-checker + $inlinecount?: string; + $count?: string; +} + const compileCriteria = (() => { - let protocolVersion; - let forceLowerCase; - let fieldTypes; + // eslint-disable-next-line @typescript-eslint/init-declarations + let protocolVersion: number; + // eslint-disable-next-line @typescript-eslint/init-declarations + let forceLowerCase: boolean | undefined; + // eslint-disable-next-line @typescript-eslint/init-declarations + let fieldTypes: FieldTypes | undefined; - const createBinaryOperationFormatter = (op) => (prop, val) => `${prop} ${op} ${val}`; + const createBinaryOperationFormatter = (op: string): Formatter => (prop, val) => `${prop} ${op} ${val}`; - const createStringFuncFormatter = (op, reverse) => (prop, val) => { + const createStringFuncFormatter = (op: string, reverse?: boolean): Formatter => (prop, val) => { const bag = [op, '(']; - - if (forceLowerCase) { - prop = prop.indexOf('tolower(') === -1 ? `tolower(${prop})` : prop; - val = val.toLowerCase(); - } + const propName = forceLowerCase && !prop.includes('tolower(') ? `tolower(${prop})` : prop; + const value = forceLowerCase ? val.toLowerCase() : val; if (reverse) { - bag.push(val, ',', prop); + bag.push(value, ',', propName); } else { - bag.push(prop, ',', val); + bag.push(propName, ',', value); } bag.push(')'); return bag.join(''); }; - const isStringFunction = function (name) { + const isStringFunction = function (name: string): boolean { return STRING_FUNCTIONS.some((funcName) => funcName === name); }; - const formatters = { + const formatters: Record = { '=': createBinaryOperationFormatter('eq'), '<>': createBinaryOperationFormatter('ne'), '>': createBinaryOperationFormatter('gt'), '>=': createBinaryOperationFormatter('ge'), '<': createBinaryOperationFormatter('lt'), '<=': createBinaryOperationFormatter('le'), - // @ts-expect-error startswith: createStringFuncFormatter('startswith'), - // @ts-expect-error endswith: createStringFuncFormatter('endswith'), }; - const formattersV2 = extend({}, formatters, { - /* eslint-disable spellcheck/spell-checker */ + /* eslint-disable spellcheck/spell-checker */ + const formattersV2: Record = { + ...formatters, contains: createStringFuncFormatter('substringof', true), notcontains: createStringFuncFormatter('not substringof', true), - }); + }; + /* eslint-enable spellcheck/spell-checker */ - const formattersV4 = extend({}, formatters, { - // @ts-expect-error + /* eslint-disable spellcheck/spell-checker */ + const formattersV4: Record = { + ...formatters, contains: createStringFuncFormatter('contains'), - // @ts-expect-error notcontains: createStringFuncFormatter('not contains'), - }); + }; + /* eslint-enable spellcheck/spell-checker */ - const compileBinary = (criteria) => { - criteria = normalizeBinaryCriterion(criteria); + const compileBinary = (criteria: unknown[]): string => { + const crit = normalizeBinaryCriterion(criteria); - const op = criteria[1]; - const fieldName = criteria[0]; - const fieldType = fieldTypes && fieldTypes[fieldName]; + const [rawFieldName, op] = crit; + const fieldName = isString(rawFieldName) || rawFieldName instanceof EdmLiteral + ? rawFieldName + : String(rawFieldName); + const fieldType = fieldTypes?.[String(fieldName)]; if (fieldType && isStringFunction(op) && fieldType !== 'String') { - // @ts-expect-error - throw new errors.Error('E4024', op, fieldName, fieldType); + throw errors.Error('E4024', op, fieldName, fieldType); } - const formatters = protocolVersion === 4 + const criterionFormatters = protocolVersion === 4 ? formattersV4 : formattersV2; - const formatter = formatters[op.toLowerCase()]; + const formatter = criterionFormatters[op.toLowerCase()]; if (!formatter) { throw errors.Error('E4003', op); } - let value = criteria[2]; - - if (fieldTypes?.[fieldName]) { - value = convertPrimitiveValue(fieldTypes[fieldName], value); - } + const value = fieldType ? convertPrimitiveValue(fieldType, crit[2]) : crit[2]; return formatter( serializePropName(fieldName), - serializeValue(value, protocolVersion, fieldTypes?.[fieldName]), + serializeValue(value, protocolVersion, fieldType), ); }; - const compileUnary = (criteria) => { + const compileUnary = (criteria: unknown[]): string => { const op = criteria[0]; + // eslint-disable-next-line @typescript-eslint/no-use-before-define const crit = compileCore(criteria[1]); if (op === '!') { @@ -121,43 +139,51 @@ const compileCriteria = (() => { throw errors.Error('E4003', op); }; - const compileGroup = (criteria) => { - const bag = []; - let groupOperator; - let nextGroupOperator; + const compileGroup = (criteria: unknown[]): string => { + const bag: string[] = []; + // eslint-disable-next-line @typescript-eslint/init-declarations + let groupOperator: string | undefined; + // eslint-disable-next-line @typescript-eslint/init-declarations + let nextGroupOperator: string | undefined; - each(criteria, function (index, criterion) { + criteria.forEach((criterion) => { if (Array.isArray(criterion)) { if (bag.length > 1 && groupOperator !== nextGroupOperator) { - // @ts-expect-error - throw new errors.Error('E4019'); + throw errors.Error('E4019'); } - // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-use-before-define bag.push(`(${compileCore(criterion)})`); groupOperator = nextGroupOperator; nextGroupOperator = 'and'; } else { - nextGroupOperator = isConjunctiveOperator(this) ? 'and' : 'or'; + nextGroupOperator = isConjunctiveOperator(criterion) ? 'and' : 'or'; } }); return bag.join(` ${groupOperator} `); }; - const compileCore = (criteria) => { - if (Array.isArray(criteria[0])) { - return compileGroup(criteria); + const compileCore = (criteria: unknown): string => { + const criterion: unknown[] = Array.isArray(criteria) ? criteria : [criteria]; + + if (Array.isArray(criterion[0])) { + return compileGroup(criterion); } - if (isUnaryOperation(criteria)) { - return compileUnary(criteria); + if (isUnaryOperation(criterion)) { + return compileUnary(criterion); } - return compileBinary(criteria); + return compileBinary(criterion); }; - return (criteria, version, types, filterToLower) => { + return ( + criteria: unknown, + version: number, + types: FieldTypes | undefined, + filterToLower: boolean | undefined, + ): string => { fieldTypes = types; forceLowerCase = filterToLower ?? config().oDataFilterToLower; protocolVersion = version; @@ -166,76 +192,62 @@ const compileCriteria = (() => { }; })(); -const createODataQueryAdapter = (queryOptions) => { - /* eslint-disable @typescript-eslint/naming-convention */ - let _sorting = []; - const _criteria = []; - const _expand = queryOptions.expand; - let _select; - let _skip; - let _take; - let _countQuery; - - const _oDataVersion = queryOptions.version || DEFAULT_PROTOCOL_VERSION; - - const hasSlice = () => _skip || _take !== undefined; - - const hasFunction = (criterion) => { - for (let i = 0; i < criterion.length; i++) { - if (isFunction(criterion[i])) { - return true; - } - - if (Array.isArray(criterion[i]) && hasFunction(criterion[i])) { - return true; - } - } - return false; - }; - - const requestData = () => { - const result = {}; - - if (!_countQuery) { - if (_sorting.length) { - // @ts-expect-error - result.$orderby = _sorting.join(','); +const createODataQueryAdapter = (queryOptions: RemoteQueryOptions): QueryAdapter => { + let sorting: string[] = []; + const criteria: unknown[] = []; + const { expand } = queryOptions; + // eslint-disable-next-line @typescript-eslint/init-declarations + let select: string[] | undefined; + // eslint-disable-next-line @typescript-eslint/init-declarations + let skip: number | undefined; + // eslint-disable-next-line @typescript-eslint/init-declarations + let take: number | undefined; + let countQuery = false; + + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const oDataVersion = queryOptions.version || DEFAULT_PROTOCOL_VERSION; + + const hasSlice = (): boolean => !!skip || take !== undefined; + + const hasFunction = (criterion: unknown[]): boolean => criterion.some( + (item) => isFunction(item) || (Array.isArray(item) && hasFunction(item)), + ); + + const requestData = (): ODataRequestParams => { + const result: ODataRequestParams = {}; + + if (!countQuery) { + if (sorting.length) { + result.$orderby = sorting.join(','); } - if (_skip) { - // @ts-expect-error - result.$skip = _skip; + if (skip) { + result.$skip = skip; } - if (_take !== undefined) { - // @ts-expect-error - result.$top = _take; + if (take !== undefined) { + result.$top = take; } - // @ts-expect-error - result.$select = generateSelect(_oDataVersion, _select) || undefined; - // @ts-expect-error // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - result.$expand = generateExpand(_oDataVersion, _expand, _select) || undefined; + result.$select = generateSelect(oDataVersion, select) || undefined; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + result.$expand = generateExpand(oDataVersion, expand, select) || undefined; } - if (_criteria.length) { - const criteria = _criteria.length < 2 ? _criteria[0] : _criteria; - const fieldTypes = queryOptions?.fieldTypes; - const filterToLower = queryOptions?.filterToLower; - // @ts-expect-error - result.$filter = compileCriteria(criteria, _oDataVersion, fieldTypes, filterToLower); + if (criteria.length) { + const filterCriteria: unknown = criteria.length < 2 ? criteria[0] : criteria; + const { fieldTypes, filterToLower } = queryOptions; + result.$filter = compileCriteria(filterCriteria, oDataVersion, fieldTypes, filterToLower); } - if (_countQuery) { - // @ts-expect-error + if (countQuery) { result.$top = 0; } - if (queryOptions.requireTotalCount || _countQuery) { + if (queryOptions.requireTotalCount || countQuery) { // todo: tests!!! - if (_oDataVersion !== 4) { - // @ts-expect-error + if (oDataVersion !== 4) { + // eslint-disable-next-line spellcheck/spell-checker result.$inlinecount = 'allpages'; } else { - // @ts-expect-error result.$count = 'true'; } } @@ -243,19 +255,13 @@ const createODataQueryAdapter = (queryOptions) => { return result; }; - const tryLiftSelect = (tasks) => { - let selectIndex = -1; - for (let i = 0; i < tasks.length; i++) { - if (tasks[i].name === 'select') { - selectIndex = i; - break; - } - } + const tryLiftSelect = (tasks: RemoteTask[]): void => { + const selectIndex = tasks.findIndex((task) => task.name === 'select'); if (selectIndex < 0 || !isFunction(tasks[selectIndex].args[0])) return; const nextTask = tasks[1 + selectIndex]; - if (!nextTask || nextTask.name !== 'slice') return; + if (nextTask?.name !== 'slice') return; tasks[1 + selectIndex] = tasks[selectIndex]; tasks[selectIndex] = nextTask; @@ -265,96 +271,94 @@ const createODataQueryAdapter = (queryOptions) => { optimize: tryLiftSelect, - exec(url) { + exec(url: string): DeferredObj { return sendRequest( - _oDataVersion, + oDataVersion, { url, - params: extend(requestData(), queryOptions?.params), + params: extend(requestData(), queryOptions.params), }, { beforeSend: queryOptions.beforeSend, jsonp: queryOptions.jsonp, withCredentials: queryOptions.withCredentials, - countOnly: _countQuery, + countOnly: countQuery, processDatesAsUtc: queryOptions.processDatesAsUtc, fieldTypes: queryOptions.fieldTypes, - isPaged: isFinite(_take), + isPaged: isFinite(Number(take)), }, ); }, - /* eslint-disable @typescript-eslint/no-invalid-void-type */ - multiSort(args): boolean | void { - let rules; + + multiSort(args: unknown[]): boolean | undefined { + const rules: string[] = []; if (hasSlice()) { return false; } - for (let i = 0; i < args.length; i++) { - const getter = args[i][0]; - const desc = !!args[i][1]; - let rule; + for (const arg of args) { + const [getter, desc] = Array.isArray(arg) ? arg : []; if (typeof getter !== 'string') { return false; } - rule = serializePropName(getter); - if (desc) { - rule += ' desc'; - } - - rules = rules || []; - rules.push(rule); + rules.push(desc ? `${serializePropName(getter)} desc` : serializePropName(getter)); } - _sorting = rules; + sorting = rules; + + return undefined; }, - slice(skipCount, takeCount): boolean | void { + slice(skipCount: number, takeCount: number): boolean | undefined { if (hasSlice()) { return false; } - _skip = skipCount; - _take = takeCount; + skip = skipCount; + take = takeCount; + + return undefined; }, - filter(criterion): boolean | void { + filter(...args: unknown[]): boolean | undefined { if (hasSlice()) { return false; } - if (!Array.isArray(criterion)) { - criterion = [].slice.call(arguments); - } + const [first] = args; + const criterion: unknown[] = Array.isArray(first) ? first : args.slice(); if (hasFunction(criterion)) { return false; } - if (_criteria.length) { - // @ts-expect-error - _criteria.push('and'); + if (criteria.length) { + criteria.push('and'); } - // @ts-expect-error - _criteria.push(criterion); + criteria.push(criterion); + + return undefined; }, - select(expr): boolean | void { - if (_select || isFunction(expr)) { + select(...args: unknown[]): boolean | undefined { + const [expr] = args; + + if (select || isFunction(expr)) { return false; } - if (!Array.isArray(expr)) { - expr = [].slice.call(arguments); - } + select = (Array.isArray(expr) ? expr : args.slice()).map(String); + + return undefined; + }, - _select = expr; + count(): boolean { + countQuery = true; + return countQuery; }, - // eslint-disable-next-line no-return-assign - count: () => _countQuery = true, }; }; diff --git a/packages/devextreme/js/__internal/data/odata/request_dispatcher.ts b/packages/devextreme/js/__internal/data/odata/request_dispatcher.ts index e37f10217123..36acb68ebdde 100644 --- a/packages/devextreme/js/__internal/data/odata/request_dispatcher.ts +++ b/packages/devextreme/js/__internal/data/odata/request_dispatcher.ts @@ -1,72 +1,91 @@ import '@js/common/data/odata/query_adapter'; import { sendRequest } from '@js/common/data/odata/utils'; +import type { DeferredObj } from '@js/core/utils/deferred'; const DEFAULT_PROTOCOL_VERSION = 4; +export interface RequestDispatcherOptions { + url?: string; + beforeSend?: Function; + jsonp?: boolean; + version?: number; + withCredentials?: boolean; + processDatesAsUtc?: boolean; + deserializeDates?: boolean; + filterToLower?: boolean; +} + export default class RequestDispatcher { - constructor(options) { - options = options || {}; - // @ts-expect-error - this._url = String(options.url).replace(/\/+$/, ''); - // @ts-expect-error - this._beforeSend = options.beforeSend; - // @ts-expect-error - this._jsonp = options.jsonp; - // @ts-expect-error - this._version = options.version || DEFAULT_PROTOCOL_VERSION; - // @ts-expect-error - this._withCredentials = options.withCredentials; - // @ts-expect-error - this._processDatesAsUtc = options.processDatesAsUtc ?? options.deserializeDates ?? false; - // @ts-expect-error - this._filterToLower = options.filterToLower; + _url: string; + + _beforeSend?: Function; + + _jsonp?: boolean; + + _version: number; + + _withCredentials?: boolean; + + _processDatesAsUtc: boolean; + + _filterToLower?: boolean; + + constructor(options?: RequestDispatcherOptions) { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const dispatcherOptions: RequestDispatcherOptions = options || {}; + + this._url = String(dispatcherOptions.url).replace(/\/+$/, ''); + this._beforeSend = dispatcherOptions.beforeSend; + this._jsonp = dispatcherOptions.jsonp; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + this._version = dispatcherOptions.version || DEFAULT_PROTOCOL_VERSION; + this._withCredentials = dispatcherOptions.withCredentials; + this._processDatesAsUtc = dispatcherOptions.processDatesAsUtc + ?? dispatcherOptions.deserializeDates ?? false; + this._filterToLower = dispatcherOptions.filterToLower; } - sendRequest(url, method, params, payload) { + sendRequest( + url: string, + method?: string, + params?: Record | null, + payload?: unknown, + ): DeferredObj { return sendRequest( this.version, { url, method, - params: params || {}, + params: params ?? {}, payload, }, { - // @ts-expect-error beforeSend: this._beforeSend, - // @ts-expect-error jsonp: this._jsonp, - // @ts-expect-error withCredentials: this._withCredentials, - // @ts-expect-error processDatesAsUtc: this._processDatesAsUtc, }, ); } - get version() { - // @ts-expect-error + get version(): number { return this._version; } - get beforeSend() { - // @ts-expect-error + get beforeSend(): Function | undefined { return this._beforeSend; } - get url() { - // @ts-expect-error + get url(): string { return this._url; } - get jsonp() { - // @ts-expect-error + get jsonp(): boolean | undefined { return this._jsonp; } - get filterToLower() { - // @ts-expect-error + get filterToLower(): boolean | undefined { return this._filterToLower; } } diff --git a/packages/devextreme/js/__internal/data/odata/store.ts b/packages/devextreme/js/__internal/data/odata/store.ts index 4464dc43d888..d02f3cf322fe 100644 --- a/packages/devextreme/js/__internal/data/odata/store.ts +++ b/packages/devextreme/js/__internal/data/odata/store.ts @@ -35,13 +35,6 @@ export interface ODataLoadOptions extends StoreLoadOptions { customQueryParams?: Record; } -// `RequestDispatcher` does not declare its own fields yet, so the members the store reads -// directly are described here until that class is typed. -type StoreRequestDispatcher = RequestDispatcher & { - _withCredentials?: unknown; - _processDatesAsUtc?: unknown; -}; - const expandKeyType = (key: StoreKey, keyType: string): FieldTypes => ({ [String(key)]: keyType }); const getProperty = (source: unknown, name: string): unknown => ( @@ -72,7 +65,7 @@ const mergeFieldTypesWithKeyType = ( }; class ODataStore extends Store { - _requestDispatcher: StoreRequestDispatcher; + _requestDispatcher: RequestDispatcher; _fieldTypes: FieldTypes; diff --git a/packages/devextreme/js/__internal/data/query.ts b/packages/devextreme/js/__internal/data/query.ts index c35f346ae08c..f25f87f4734f 100644 --- a/packages/devextreme/js/__internal/data/query.ts +++ b/packages/devextreme/js/__internal/data/query.ts @@ -1,9 +1,16 @@ import { queryImpl } from '@js/common/data/query_implementation'; +import type { ArrayQuery, QueryOptions } from '@ts/data/array_query'; +import type { RemoteQuery, RemoteQueryOptions } from '@ts/data/remote_query'; -const query = function () { - const impl = Array.isArray(arguments[0]) ? 'array' : 'remote'; - // @ts-expect-error - return queryImpl[impl].apply(this, arguments); -}; +function query(array: unknown[], queryOptions?: QueryOptions): ArrayQuery; +function query(url: string, queryOptions?: RemoteQueryOptions): RemoteQuery; +function query( + source: unknown[] | string, + queryOptions?: QueryOptions & RemoteQueryOptions, +): ArrayQuery | RemoteQuery { + return Array.isArray(source) + ? queryImpl.array(source, queryOptions) + : queryImpl.remote(source, queryOptions); +} export default query; diff --git a/packages/devextreme/js/__internal/data/remote_query.ts b/packages/devextreme/js/__internal/data/remote_query.ts index 53fb79c54c50..dbf79b08c62f 100644 --- a/packages/devextreme/js/__internal/data/remote_query.ts +++ b/packages/devextreme/js/__internal/data/remote_query.ts @@ -1,29 +1,73 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import arrayQueryImpl from '@js/common/data/array_query'; import { errors, handleError } from '@js/common/data/errors'; import queryAdapters from '@js/common/data/query_adapters'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { Deferred } from '@js/core/utils/deferred'; -import { each } from '@js/core/utils/iterator'; import { isFunction } from '@js/core/utils/type'; +import type { ArrayQuery, LangParams } from '@ts/data/array_query'; + +export interface RemoteTask { + name: string; + args: unknown[]; +} + +export interface QueryAdapter { + [taskName: string]: unknown; + optimize?: (tasks: RemoteTask[]) => void; + exec: (url: string) => DeferredObj; +} + +export interface RemoteQueryOptions { + adapter?: string | ((options: RemoteQueryOptions) => QueryAdapter); + errorHandler?: (error: unknown) => void; + langParams?: LangParams; + version?: number; + expand?: string | string[] | Function; + fieldTypes?: Record; + filterToLower?: boolean; + requireTotalCount?: boolean; + params?: Record; + beforeSend?: Function; + jsonp?: boolean; + withCredentials?: boolean; + processDatesAsUtc?: boolean; + deserializeDates?: boolean; +} + +export interface RemoteQuery { + sortBy: (...args: unknown[]) => RemoteQuery; + thenBy: (...args: unknown[]) => RemoteQuery; + filter: (...args: unknown[]) => RemoteQuery; + slice: (...args: unknown[]) => RemoteQuery; + select: (...args: unknown[]) => RemoteQuery; + groupBy: (...args: unknown[]) => RemoteQuery; + count: (...args: unknown[]) => DeferredObj; + min: (...args: unknown[]) => DeferredObj; + max: (...args: unknown[]) => DeferredObj; + sum: (...args: unknown[]) => DeferredObj; + avg: (...args: unknown[]) => DeferredObj; + aggregate: (...args: unknown[]) => DeferredObj; + enumerate: (...args: unknown[]) => DeferredObj; +} + +const createTask = function (name: string, args: unknown[]): RemoteTask { + return { name, args }; +}; -const remoteQueryImpl = function (url, queryOptions, tasks) { - tasks = tasks || []; - queryOptions = queryOptions || {}; - - const createTask = function (name, args) { - return { name, args }; - }; +const remoteQueryImpl = function ( + url: string, + options?: RemoteQueryOptions, + previousTasks?: RemoteTask[], +): RemoteQuery { + const tasks: RemoteTask[] = previousTasks ?? []; + const queryOptions: RemoteQueryOptions = options ?? {}; - const exec = function (executorTask) { - // @ts-expect-error - const d = new Deferred(); - let _adapterFactory; - let _adapter; - let _taskQueue; - let _currentTask; - let _mergedSortArgs; + const exec = function (executorTask: RemoteTask): DeferredObj { + const d = Deferred(); + // eslint-disable-next-line @typescript-eslint/init-declarations + let mergedSortArgs: unknown[][] | undefined; - const rejectWithNotify = function (error) { + const rejectWithNotify = function (error: unknown): void { const handler = queryOptions.errorHandler; if (handler) { handler(error); @@ -33,90 +77,109 @@ const remoteQueryImpl = function (url, queryOptions, tasks) { d.reject(error); }; - function mergeSortTask(task) { - // eslint-disable-next-line default-case + function mergeSortTask(task: RemoteTask): boolean { switch (task.name) { case 'sortBy': - _mergedSortArgs = [task.args]; + mergedSortArgs = [task.args]; return true; case 'thenBy': - if (!_mergedSortArgs) { + if (!mergedSortArgs) { throw errors.Error('E4004'); } - _mergedSortArgs.push(task.args); + mergedSortArgs.push(task.args); return true; - } - return false; + default: + return false; + } } - function unmergeSortTasks() { - const head = _taskQueue[0]; - const unmergedTasks = []; - - if (head && head.name === 'multiSort') { - _taskQueue.shift(); - each(head.args[0], function () { - // @ts-expect-error - unmergedTasks.push(createTask(unmergedTasks.length ? 'thenBy' : 'sortBy', this)); - }); + function unmergeSortTasks(queue: RemoteTask[]): RemoteTask[] { + const head = queue[0]; + const unmergedTasks: RemoteTask[] = []; + + if (head?.name === 'multiSort') { + queue.shift(); + const [sortArgsList] = head.args; + if (Array.isArray(sortArgsList)) { + sortArgsList.forEach((sortArgs: unknown) => { + unmergedTasks.push(createTask( + unmergedTasks.length ? 'thenBy' : 'sortBy', + Array.isArray(sortArgs) ? sortArgs : [sortArgs], + )); + }); + } } - _taskQueue = unmergedTasks.concat(_taskQueue); + return unmergedTasks.concat(queue); } - try { - _adapterFactory = queryOptions.adapter; - if (!isFunction(_adapterFactory)) { - _adapterFactory = queryAdapters[_adapterFactory]; + function passTaskToAdapter(adapter: QueryAdapter, task: RemoteTask): boolean { + if (String(task.name) === 'enumerate') { + return true; } - _adapter = _adapterFactory(queryOptions); - - _taskQueue = [].concat(tasks).concat(executorTask); + const taskHandler = adapter[task.name]; - const { optimize } = _adapter; - if (optimize) optimize(_taskQueue); + return isFunction(taskHandler) && taskHandler.apply(adapter, task.args) !== false; + } - while (_taskQueue.length) { - /* eslint-disable-next-line prefer-destructuring */ - _currentTask = _taskQueue[0]; + function collectAdapterTasks(adapter: QueryAdapter, queue: RemoteTask[]): void { + while (queue.length) { + const currentTask = queue[0]; - if (!mergeSortTask(_currentTask)) { - if (_mergedSortArgs) { - _taskQueue.unshift(createTask('multiSort', [_mergedSortArgs])); - _mergedSortArgs = null; + if (!mergeSortTask(currentTask)) { + if (mergedSortArgs) { + queue.unshift(createTask('multiSort', [mergedSortArgs])); + mergedSortArgs = undefined; + // eslint-disable-next-line no-continue continue; } - if (String(_currentTask.name) !== 'enumerate') { - if (!_adapter[_currentTask.name] || _adapter[_currentTask.name].apply(_adapter, _currentTask.args) === false) { - break; - } + if (!passTaskToAdapter(adapter, currentTask)) { + break; } } - _taskQueue.shift(); + queue.shift(); } + } - unmergeSortTasks(); + try { + const adapterFactory = isFunction(queryOptions.adapter) + ? queryOptions.adapter + : queryAdapters[String(queryOptions.adapter)]; + + const adapter: QueryAdapter = adapterFactory(queryOptions); + + let taskQueue: RemoteTask[] = [...tasks, executorTask]; + + const { optimize } = adapter; + if (optimize) optimize(taskQueue); + + collectAdapterTasks(adapter, taskQueue); - _adapter.exec(url) - .done((result, extra) => { - if (!_taskQueue.length) { + taskQueue = unmergeSortTasks(taskQueue); + + adapter.exec(url) + .done((result: unknown, extra: unknown) => { + if (!taskQueue.length) { d.resolve(result, extra); } else { - let clientChain = arrayQueryImpl(result, { + // @ts-expect-error the adapter resolves with whatever the service returned + let clientChain: ArrayQuery = arrayQueryImpl(result, { errorHandler: queryOptions.errorHandler, }); - each(_taskQueue, function () { - clientChain = clientChain[this.name].apply(clientChain, this.args); + taskQueue.forEach((task) => { + const method = clientChain[task.name]; + if (isFunction(method)) { + clientChain = method.apply(clientChain, task.args); + } }); - clientChain - // @ts-expect-error - .done(d.resolve) - .fail(d.reject); + // @ts-expect-error the queue always ends with `enumerate`, so + // the chain ends with a Deferred rather than with a query + clientChain.done(d.resolve).fail(d.reject); } }) .fail(rejectWithNotify); @@ -124,32 +187,32 @@ const remoteQueryImpl = function (url, queryOptions, tasks) { rejectWithNotify(x); } + // @ts-expect-error DeferredObj typings: promise() is declared as a plain Promise return d.promise(); }; - const query = {}; - - each( - ['sortBy', 'thenBy', 'filter', 'slice', 'select', 'groupBy'], - function () { - const name = String(this); - query[name] = function () { - return remoteQueryImpl(url, queryOptions, tasks.concat(createTask(name, arguments))); - }; - }, + const chain = (name: string, args: unknown[]): RemoteQuery => remoteQueryImpl( + url, + queryOptions, + tasks.concat(createTask(name, args)), ); - each( - ['count', 'min', 'max', 'sum', 'avg', 'aggregate', 'enumerate'], - function () { - const name = String(this); - query[name] = function () { - return exec.call(this, createTask(name, arguments)); - }; - }, - ); - - return query; + return { + sortBy: (...args: unknown[]): RemoteQuery => chain('sortBy', args), + thenBy: (...args: unknown[]): RemoteQuery => chain('thenBy', args), + filter: (...args: unknown[]): RemoteQuery => chain('filter', args), + slice: (...args: unknown[]): RemoteQuery => chain('slice', args), + select: (...args: unknown[]): RemoteQuery => chain('select', args), + groupBy: (...args: unknown[]): RemoteQuery => chain('groupBy', args), + + count: (...args: unknown[]): DeferredObj => exec(createTask('count', args)), + min: (...args: unknown[]): DeferredObj => exec(createTask('min', args)), + max: (...args: unknown[]): DeferredObj => exec(createTask('max', args)), + sum: (...args: unknown[]): DeferredObj => exec(createTask('sum', args)), + avg: (...args: unknown[]): DeferredObj => exec(createTask('avg', args)), + aggregate: (...args: unknown[]): DeferredObj => exec(createTask('aggregate', args)), + enumerate: (...args: unknown[]): DeferredObj => exec(createTask('enumerate', args)), + }; }; export default remoteQueryImpl; diff --git a/packages/devextreme/js/__internal/data/store_helper.ts b/packages/devextreme/js/__internal/data/store_helper.ts index 9c30c9243f73..93f46a79203f 100644 --- a/packages/devextreme/js/__internal/data/store_helper.ts +++ b/packages/devextreme/js/__internal/data/store_helper.ts @@ -1,85 +1,133 @@ import arrayQuery from '@js/common/data/array_query'; +import type { SortingInfo } from '@js/common/data/utils'; import { normalizeSortingInfo } from '@js/common/data/utils'; -// @ts-expect-error -import { grep } from '@js/core/utils/common'; import { extend } from '@js/core/utils/extend'; -import { each } from '@js/core/utils/iterator'; +import { isObject } from '@js/core/utils/type'; +import type { LangParams } from '@ts/data/array_query'; + +export interface DataQuery { + /* eslint-disable @typescript-eslint/method-signature-style */ + setLangParams?(langParams: LangParams): void; + filter(criteria: unknown): this; + sortBy(getter: unknown, desc?: unknown, compare?: unknown): this; + thenBy(getter: unknown, desc?: unknown, compare?: unknown): this; + select(getter: unknown): this; + slice(skip: number, take?: number): this; + groupBy(getter: unknown): this; + toArray(): unknown[]; + /* eslint-enable @typescript-eslint/method-signature-style */ +} + +interface GroupingInfo extends Array { + keepInitialKeyOrder?: boolean; +} + +export interface QueryByOptions { + filter?: unknown; + sort?: unknown; + select?: unknown; + group?: unknown; + skip?: number; + take?: number; + langParams?: LangParams; +} + +interface GroupResult { + items?: unknown[]; +} -function multiLevelGroup(query, groupInfo) { - query = query.groupBy(groupInfo[0].selector); +function multiLevelGroup( + query: TQuery, + groupInfo: SortingInfo[], +): TQuery { + let result = query.groupBy(groupInfo[0].selector); if (groupInfo.length > 1) { - query = query.select((g) => extend({}, g, { - items: multiLevelGroup(arrayQuery(g.items), groupInfo.slice(1)).toArray(), - })); + result = result.select((group: unknown) => { + const { items }: GroupResult = isObject(group) ? group : {}; + + const merged: unknown = extend({}, group, { + items: multiLevelGroup(arrayQuery(items ?? []), groupInfo.slice(1)).toArray(), + }); + + return merged; + }); } - return query; + return result; } -function arrangeSortingInfo(groupInfo, sortInfo) { - const filteredGroup = []; - each(groupInfo, (_, group) => { - const collision = grep(sortInfo, (sort) => group.selector === sort.selector); +function arrangeSortingInfo(groupInfo: SortingInfo[], sortInfo: SortingInfo[]): SortingInfo[] { + const filteredGroup = groupInfo.filter( + (group) => !sortInfo.some((sort) => group.selector === sort.selector), + ); - if (collision.length < 1) { - // @ts-expect-error - filteredGroup.push(group); - } - }); return filteredGroup.concat(sortInfo); } -function queryByOptions(query, options, isCountQuery) { - options = options || {}; +function queryByOptions( + query: TQuery, + options?: QueryByOptions, + isCountQuery?: boolean, +): TQuery { + const queryOptions: QueryByOptions = options ?? {}; + let result = query; - const { filter } = options; + const { filter } = queryOptions; - if (options?.langParams) { - query.setLangParams?.(options.langParams); + if (queryOptions.langParams) { + result.setLangParams?.(queryOptions.langParams); } if (filter) { - query = query.filter(filter); + result = result.filter(filter); } if (isCountQuery) { - return query; + return result; } - let { sort } = options; - const { select } = options; - let { group } = options; - const { skip } = options; - const { take } = options; + const { + sort, select, skip, take, + } = queryOptions; - if (group) { - group = normalizeSortingInfo(group); - group.keepInitialKeyOrder = !!options.group.keepInitialKeyOrder; + // eslint-disable-next-line @typescript-eslint/init-declarations + let group: GroupingInfo | undefined; + if (queryOptions.group) { + const groupOption: { keepInitialKeyOrder?: unknown } = isObject(queryOptions.group) + ? queryOptions.group + : {}; + + group = normalizeSortingInfo(queryOptions.group); + group.keepInitialKeyOrder = !!groupOption.keepInitialKeyOrder; } + if (sort || group) { - sort = normalizeSortingInfo(sort || []); - if (group && !group.keepInitialKeyOrder) { - sort = arrangeSortingInfo(group, sort); - } - each(sort, function (index) { - query = query[index ? 'thenBy' : 'sortBy'](this.selector, this.desc, this.compare); + const sortInfo = normalizeSortingInfo(sort ?? []); + const sortRules = group && !group.keepInitialKeyOrder + ? arrangeSortingInfo(group, sortInfo) + : sortInfo; + + sortRules.forEach((rule, index) => { + result = index + ? result.thenBy(rule.selector, rule.desc, rule.compare) + : result.sortBy(rule.selector, rule.desc, rule.compare); }); } if (select) { - query = query.select(select); + result = result.select(select); } if (group) { - query = multiLevelGroup(query, group); + result = multiLevelGroup(result, group); } if (take || skip) { - query = query.slice(skip || 0, take); + result = result.slice(skip ?? 0, take); } - return query; + return result; } export default { diff --git a/packages/devextreme/js/__internal/data/utils.ts b/packages/devextreme/js/__internal/data/utils.ts index ea0a65a494d8..3be3ae6daf20 100644 --- a/packages/devextreme/js/__internal/data/utils.ts +++ b/packages/devextreme/js/__internal/data/utils.ts @@ -1,17 +1,20 @@ /* eslint-disable spellcheck/spell-checker */ import domAdapter from '@js/core/dom_adapter'; import { equalByValue } from '@js/core/utils/common'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { Deferred } from '@js/core/utils/deferred'; import { map } from '@js/core/utils/iterator'; import readyCallbacks from '@js/core/utils/ready_callbacks'; -import { isFunction } from '@js/core/utils/type'; +import { isFunction, isObject, isString } from '@js/core/utils/type'; import { getWindow } from '@js/core/utils/window'; const ready = readyCallbacks.add; export const XHR_ERROR_UNLOAD = 'DEVEXTREME_XHR_ERROR_UNLOAD'; -export const normalizeBinaryCriterion = function (crit) { +export type BinaryCriterion = [unknown, string, unknown]; + +export const normalizeBinaryCriterion = function (crit: unknown[]): BinaryCriterion { return [ crit[0], crit.length < 3 ? '=' : String(crit[1]).toLowerCase(), @@ -19,40 +22,60 @@ export const normalizeBinaryCriterion = function (crit) { ]; }; -export const normalizeSortingInfo = function (info) { - if (!Array.isArray(info)) { - info = [info]; - } +export type SortingSelector = string | Function; + +export interface SortingInfo { + selector: SortingSelector | undefined; + desc: boolean; + compare?: unknown; + isExpanded?: boolean; + groupInterval?: unknown; +} + +interface SortingDescriptor { + getter?: SortingSelector; + field?: SortingSelector; + selector?: SortingSelector; + desc?: unknown; + dir?: unknown; + compare?: unknown; +} - return map(info, (i) => { - const result = { - selector: isFunction(i) || typeof i === 'string' ? i : i.getter || i.field || i.selector, - desc: !!(i.desc || String(i.dir).charAt(0).toLowerCase() === 'd'), +export const normalizeSortingInfo = function (info: unknown): SortingInfo[] { + const infoArray: unknown[] = Array.isArray(info) ? info : [info]; + + return infoArray.map((item: unknown): SortingInfo => { + const descriptor: SortingDescriptor = isObject(item) ? item : {}; + const result: SortingInfo = { + selector: isFunction(item) || isString(item) + ? item + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + : descriptor.getter || descriptor.field || descriptor.selector, + desc: !!(descriptor.desc || String(descriptor.dir).charAt(0).toLowerCase() === 'd'), }; - if (i.compare) { - // @ts-expect-error - result.compare = i.compare; + if (descriptor.compare) { + result.compare = descriptor.compare; } return result; }); }; export const errorMessageFromXhr = (function () { - const textStatusMessages = { + const textStatusMessages: Record = { timeout: 'Network connection timeout', error: 'Unspecified network error', parsererror: 'Unexpected server response', }; /// #DEBUG - const textStatusDetails = { + const textStatusDetails: Record = { timeout: 'possible causes: the remote host is not accessible, overloaded or is not included into the domain white-list when being run in the native container', error: 'if the remote host is located on another domain, make sure it properly supports cross-origin resource sharing (CORS), or use the JSONP approach instead', parsererror: 'the remote host did not respond with valid JSON data', }; /// #ENDDEBUG - const explainTextStatus = function (textStatus) { + const explainTextStatus = function (textStatus: string): string { let result = textStatusMessages[textStatus]; if (!result) { @@ -67,79 +90,94 @@ export const errorMessageFromXhr = (function () { }; // T542570, https://stackoverflow.com/a/18170879 - let unloading; + let unloading = false; ready(() => { const window = getWindow(); domAdapter.listen(window, 'beforeunload', () => { unloading = true; }); }); - return function (xhr, textStatus) { + return function (xhr: unknown, textStatus: unknown): string { if (unloading) { return XHR_ERROR_UNLOAD; } - if (xhr.status < 400) { - return explainTextStatus(textStatus); + + const status = isObject(xhr) && 'status' in xhr ? Number(xhr.status) : NaN; + + if (status < 400) { + return explainTextStatus(String(textStatus)); } - return xhr.statusText; + + return isObject(xhr) && 'statusText' in xhr ? String(xhr.statusText) : ''; }; }()); +type AggregatableValue = number | string | Date; + export const aggregators = { count: { seed: 0, - step(count) { return 1 + count; }, + step(count: number): number { return 1 + count; }, }, sum: { seed: 0, - step(sum, item) { return sum + item; }, + step(sum: number, item: number): number { return sum + item; }, }, min: { - step(min, item) { return item < min ? item : min; }, + step(min: AggregatableValue, item: AggregatableValue): AggregatableValue { + return item < min ? item : min; + }, }, max: { - step(max, item) { return item > max ? item : max; }, + step(max: AggregatableValue, item: AggregatableValue): AggregatableValue { + return item > max ? item : max; + }, }, avg: { seed: [0, 0], - step(pair, value) { + step(pair: [number, number], value: number): [number, number] { return [pair[0] + value, pair[1] + 1]; }, - finalize(pair) { + finalize(pair: [number, number]): number { return pair[1] ? pair[0] / pair[1] : NaN; }, }, }; -export const processRequestResultLock = (function () { +interface RequestResultLock { + obtain: () => void; + release: () => void; + promise: () => DeferredObj; + reset: () => void; +} + +export const processRequestResultLock: RequestResultLock = (function (): RequestResultLock { let lockCount = 0; - let lockDeferred; + // eslint-disable-next-line @typescript-eslint/init-declarations + let lockDeferred: DeferredObj | undefined; - const obtain = function () { + const obtain = function (): void { if (lockCount === 0) { - // @ts-expect-error - lockDeferred = new Deferred(); + lockDeferred = Deferred(); } - lockCount++; + lockCount += 1; }; - const release = function () { - lockCount--; + const release = function (): void { + lockCount -= 1; if (lockCount < 1) { - lockDeferred.resolve(); + lockDeferred?.resolve(); } }; - const promise = function () { - // @ts-expect-error - const deferred = lockCount === 0 ? new Deferred().resolve() : lockDeferred; - return deferred.promise(); + const promise = function (): DeferredObj { + return lockCount === 0 || !lockDeferred + ? Deferred().resolve() + : lockDeferred; }; - const reset = function () { + const reset = function (): void { lockCount = 0; - if (lockDeferred) { - lockDeferred.resolve(); - } + lockDeferred?.resolve(); }; return { @@ -150,21 +188,24 @@ export const processRequestResultLock = (function () { }; }()); -export function isDisjunctiveOperator(condition) { - return /^(or|\|\||\|)$/i.test(condition); +export function isDisjunctiveOperator(condition: unknown): boolean { + return /^(or|\|\||\|)$/i.test(String(condition)); } -export function isConjunctiveOperator(condition) { - return /^(and|&&|&)$/i.test(condition); +export function isConjunctiveOperator(condition: unknown): boolean { + return /^(and|&&|&)$/i.test(String(condition)); } -export const keysEqual = function (keyExpr, key1, key2) { +const isRecord = (value: unknown): value is Record => isObject(value); + +export const keysEqual = function (keyExpr: unknown, key1: unknown, key2: unknown): boolean { if (Array.isArray(keyExpr)) { - const names = map(key1, (v, k) => k); - let name; - for (let i = 0; i < names.length; i++) { - name = names[i]; - if (!equalByValue(key1[name], key2[name], { strict: false })) { + const names: string[] = map(key1, (value: unknown, name: string): string => name); + const values1: Record = isRecord(key1) ? key1 : {}; + const values2: Record = isRecord(key2) ? key2 : {}; + + for (const name of names) { + if (!equalByValue(values1[name], values2[name], { strict: false })) { return false; } } @@ -176,44 +217,13 @@ export const keysEqual = function (keyExpr, key1, key2) { const BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; -// eslint-disable-next-line @typescript-eslint/naming-convention -export const base64_encode = function (input) { - if (!Array.isArray(input)) { - input = stringToByteArray(String(input)); - } - - let result = ''; - - function getBase64Char(index) { - return BASE64_CHARS.charAt(index); - } - - for (let i = 0; i < input.length; i += 3) { - const octet1 = input[i]; - const octet2 = input[i + 1]; - const octet3 = input[i + 2]; - - result += map( - [ - octet1 >> 2, - ((octet1 & 3) << 4) | octet2 >> 4, - isNaN(octet2) ? 64 : ((octet2 & 15) << 2) | octet3 >> 6, - isNaN(octet3) ? 64 : octet3 & 63, - ], - getBase64Char, - ).join(''); - } - - return result; -}; +/* eslint-disable no-bitwise */ -function stringToByteArray(str) { +function stringToByteArray(str: string): number[] { const bytes: number[] = []; - let code: number; - let i; - for (i = 0; i < str.length; i++) { - code = str.charCodeAt(i); + for (let i = 0; i < str.length; i += 1) { + const code = str.charCodeAt(i); if (code < 128) { bytes.push(code); @@ -222,21 +232,54 @@ function stringToByteArray(str) { } else if (code < 65536) { bytes.push(224 + (code >> 12), 128 + ((code >> 6) & 63), 128 + (code & 63)); } else if (code < 2097152) { - bytes.push(240 + (code >> 18), 128 + ((code >> 12) & 63), 128 + ((code >> 6) & 63), 128 + (code & 63)); + bytes.push( + 240 + (code >> 18), + 128 + ((code >> 12) & 63), + 128 + ((code >> 6) & 63), + 128 + (code & 63), + ); } } return bytes; } -export const isUnaryOperation = function (crit) { - return crit[0] === '!' && Array.isArray(crit[1]); +// eslint-disable-next-line @typescript-eslint/naming-convention +export const base64_encode = function (input: string | number[]): string { + const bytes: number[] = Array.isArray(input) ? input : stringToByteArray(String(input)); + + let result = ''; + + function getBase64Char(index: number): string { + return BASE64_CHARS.charAt(index); + } + + for (let i = 0; i < bytes.length; i += 3) { + const octet1 = bytes[i]; + const octet2 = bytes[i + 1]; + const octet3 = bytes[i + 2]; + + result += [ + octet1 >> 2, + ((octet1 & 3) << 4) | (octet2 >> 4), + isNaN(octet2) ? 64 : ((octet2 & 15) << 2) | (octet3 >> 6), + isNaN(octet3) ? 64 : octet3 & 63, + ].map(getBase64Char).join(''); + } + + return result; }; -const isGroupOperator = function (value) { +/* eslint-enable no-bitwise */ + +export const isUnaryOperation = function (crit: unknown): boolean { + return Array.isArray(crit) && crit[0] === '!' && Array.isArray(crit[1]); +}; + +const isGroupOperator = function (value: unknown): boolean { return value === 'and' || value === 'or'; }; -export const isUniformEqualsByOr = function (crit) { +export const isUniformEqualsByOr = function (crit: unknown[]): boolean { if (crit.length > 2 && Array.isArray(crit[0]) && crit[1] === 'or' && typeof crit[0][0] === 'string' && crit[0][1] === '=') { const [prop] = crit[0]; return !crit.find((el, i) => (i % 2 !== 0 ? el !== 'or' @@ -245,7 +288,11 @@ export const isUniformEqualsByOr = function (crit) { return false; }; -export const isGroupCriterion = function (crit) { +export const isGroupCriterion = function (crit: unknown): boolean { + if (!Array.isArray(crit)) { + return false; + } + const first = crit[0]; const second = crit[1]; @@ -261,21 +308,29 @@ export const isGroupCriterion = function (crit) { return false; }; -export const trivialPromise = function () { - // @ts-expect-error - const d = new Deferred(); - return d.resolve.apply(d, arguments).promise(); +export const trivialPromise = function (...args: T[]): DeferredObj { + const d = Deferred(); + // @ts-expect-error DeferredObj typings: promise() is declared as a plain Promise + return d.resolve(...args).promise(); }; -export const rejectedPromise = function () { - // @ts-expect-error - const d = new Deferred(); - return d.reject.apply(d, arguments).promise(); +export const rejectedPromise = function (...args: T[]): DeferredObj { + const d = Deferred(); + // @ts-expect-error DeferredObj typings: promise() is declared as a plain Promise + return d.reject(...args).promise(); }; -function throttle(func, timeout) { - let timeoutId; - return function () { +type ThrottleTimeout = number | (() => number); + +type TimeoutId = ReturnType; + +function throttle( + func: (this: unknown) => void, + timeout: ThrottleTimeout, +): (this: unknown) => TimeoutId | undefined { + // eslint-disable-next-line @typescript-eslint/init-declarations + let timeoutId: TimeoutId | undefined; + return function (this: unknown): TimeoutId | undefined { if (!timeoutId) { timeoutId = setTimeout(() => { timeoutId = undefined; @@ -286,19 +341,20 @@ function throttle(func, timeout) { }; } -export function throttleChanges(func, timeout) { - let cache = []; - const throttled = throttle(function () { +export function throttleChanges( + func: (this: unknown, changes: T[]) => void, + timeout: ThrottleTimeout, +): (this: unknown, changes: T[]) => TimeoutId | undefined { + let cache: T[] = []; + const throttled = throttle(function (this: unknown): void { func.call(this, cache); cache = []; }, timeout); - return function (changes) { + return function (this: unknown, changes: T[]): TimeoutId | undefined { if (Array.isArray(changes)) { - // @ts-expect-error cache.push(...changes); } - // @ts-expect-error - return throttled.call(this, cache); + return throttled.call(this); }; } diff --git a/packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping.ts b/packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping.ts index 608c733636ba..fa6a5a10b27a 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping.ts @@ -105,6 +105,9 @@ const dataSourceAdapterExtender = (Base: ModuleType) => class groups[i].isExpanded = group[i].isExpanded; } } + // @ts-expect-error `normalizeSortingInfo()` types the selector as `string | Function`, + // which is wider than the public `KeySelector`; it is the same descriptor the data + // source itself returned above. dataSource.group(groups); that._grouping.foreachGroups((groupInfo, parents) => { if (groupIndex === undefined || groupIndex === parents.length - 1) { diff --git a/packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping_expanded.ts b/packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping_expanded.ts index 023624dca8b4..f153e81939df 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping_expanded.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping_expanded.ts @@ -343,7 +343,6 @@ export class GroupingHelper extends GroupingHelperCore { if (groupCount) { let { data } = options; - // @ts-expect-error const query = dataQuery(data); storeHelper.multiLevelGroup(query, groups).enumerate().done((groupedData) => { data = groupedData; diff --git a/packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts b/packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts index baeb374a1b7b..b6d8e21f13df 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/summary/m_summary.ts @@ -117,7 +117,6 @@ const sortGroupsBySummaryCore = function (items, groups, sortByGroups) { let query; if (group && sorts && sorts.length) { - // @ts-expect-error query = dataQuery(items); each(sorts, function (index) { if (index === 0) { @@ -307,7 +306,6 @@ export const summaryDataSourceAdapterExtender = ( } private sortLastLevelGroupItems(items, groups, paths) { - // @ts-expect-error const groupedItems = storeHelper.multiLevelGroup(dataQuery(items), groups).toArray(); let result = []; diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts index aeab55d20f08..859b58d185bc 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts @@ -1823,7 +1823,6 @@ export class ColumnsController extends modules.Controller { } if (isPlainObject(dataSource) || (dataSource instanceof Store) || Array.isArray(dataSource)) { if (that.valueExpr) { - // @ts-expect-error const dataSourceOptions = normalizeDataSourceOptions(dataSource); dataSourceOptions.paginate = false; dataSource = new DataSource(dataSourceOptions); diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts index 4b135fbd8680..62db3e4c9f96 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_source_adapter/m_data_source_adapter.ts @@ -297,7 +297,6 @@ export default class DataSourceAdapter extends modules.Controller { this.resetPagesCache(true); if (this._cachedStoreData) { - // @ts-expect-error applyBatch({ keyInfo: store, data: this._cachedStoreData, @@ -365,7 +364,6 @@ export default class DataSourceAdapter extends modules.Controller { const getItemCount = () => (groupCount ? this.itemsCount() : this.items().length); const oldItemCount = getItemCount(); - // @ts-expect-error applyBatch({ keyInfo, data: this._items, @@ -374,7 +372,6 @@ export default class DataSourceAdapter extends modules.Controller { useInsertIndex: true, skipCopying: !this._needToCopyDataObject(), }); - // @ts-expect-error applyBatch({ keyInfo, data: dataSource.items(), diff --git a/packages/devextreme/js/__internal/grids/grid_core/header_filter/m_header_filter.ts b/packages/devextreme/js/__internal/grids/grid_core/header_filter/m_header_filter.ts index 10d151fa7380..f25d8b75be75 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/header_filter/m_header_filter.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/header_filter/m_header_filter.ts @@ -79,13 +79,11 @@ function ungroupUTCDates(items, dateParts?, dates?) { export function convertDataFromUTCToLocal(data, column) { const dates = ungroupUTCDates(data); - // @ts-expect-error const query = dataQuery(dates); const group = gridCoreUtils.getHeaderFilterGroupParameters({ ...column, calculateCellValue: (date) => date, }); - // @ts-expect-error return storeHelper.queryByOptions(query, { group }).toArray(); } @@ -245,7 +243,6 @@ export class HeaderFilterController extends Modules.ViewController { if (!dataSource) return; if (isDefined(headerFilterDataSource) && !isFunction(headerFilterDataSource)) { - // @ts-expect-error options.dataSource = normalizeDataSourceOptions(headerFilterDataSource); } else if (column.lookup) { isLookup = true; @@ -275,6 +272,8 @@ export class HeaderFilterController extends Modules.ViewController { dataSource.customLoader.load(options).done(({ data }) => { const convertUTCDates = remoteGrouping && isUTCFormat(column.serializationFormat) && cutoffLevel > 3; if (convertUTCDates) { + // @ts-expect-error data/store_helper types `toArray()` as `unknown[]`; + // typing the grid rows is left to a later iteration data = convertDataFromUTCToLocal(data, column); } that._processGroupItems(data, null, null, { diff --git a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts index ae057d8d7009..abdc6d0299b8 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts @@ -674,7 +674,6 @@ export default { lookupDataSourceOptions = lookupDataSourceOptions({}); } } - // @ts-expect-error return normalizeDataSourceOptions(lookupDataSourceOptions); }, diff --git a/packages/devextreme/js/__internal/grids/grid_core/search/m_search.ts b/packages/devextreme/js/__internal/grids/grid_core/search/m_search.ts index f35045af3838..71faffb9cc83 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/search/m_search.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/search/m_search.ts @@ -106,9 +106,7 @@ const dataController = ( const filterValue = parseValue(column, text); if (lookup?.items) { - // @ts-expect-error dataQuery(lookup.items, { langParams }) - // @ts-expect-error .filter( column.createFilterExpression.call( { diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/utils.ts b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/utils.ts index b03222a5303b..0e5f18efaacb 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/utils.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/data_controller/utils.ts @@ -35,7 +35,8 @@ export function normalizeDataSource( }; } - // TODO: research making second param not required + // @ts-expect-error the public DataSource constructor is declared with the public + // option types, while `normalizeDataSourceOptions()` returns the internal ones return new DataSource(normalizeDataSourceOptions(dataSourceLike, undefined)); } diff --git a/packages/devextreme/js/__internal/grids/new/grid_core/filtering/header_filter/legacy_header_filter.ts b/packages/devextreme/js/__internal/grids/new/grid_core/filtering/header_filter/legacy_header_filter.ts index 761f198bce92..1072316e25ba 100644 --- a/packages/devextreme/js/__internal/grids/new/grid_core/filtering/header_filter/legacy_header_filter.ts +++ b/packages/devextreme/js/__internal/grids/new/grid_core/filtering/header_filter/legacy_header_filter.ts @@ -158,7 +158,6 @@ export const getDataSourceOptions = ( const options: any = {}; if (isDefined(headerFilterDataSource) && !isFunction(headerFilterDataSource)) { - // @ts-expect-error options.dataSource = oldNormalizeDataSourceOptions(headerFilterDataSource); } else { const cutoffLevel = Array.isArray(group) ? group.length - 1 : 0; diff --git a/packages/devextreme/js/__internal/grids/pivot_grid/data_source/m_data_source.ts b/packages/devextreme/js/__internal/grids/pivot_grid/data_source/m_data_source.ts index b5b1683bc03f..ef1a61eaa54e 100644 --- a/packages/devextreme/js/__internal/grids/pivot_grid/data_source/m_data_source.ts +++ b/packages/devextreme/js/__internal/grids/pivot_grid/data_source/m_data_source.ts @@ -341,7 +341,6 @@ class PivotGridDataSource { createLocalOrRemoteStore(dataSourceOptions, notifyProgress) { const StoreConstructor = dataSourceOptions.remoteOperations || dataSourceOptions.paginate ? RemoteStore : LocalStore; - // @ts-expect-error return new StoreConstructor(extend(normalizeDataSourceOptions(dataSourceOptions), { onChanged: null, onLoadingChanged: null, diff --git a/packages/devextreme/js/__internal/grids/pivot_grid/local_store/m_local_store.ts b/packages/devextreme/js/__internal/grids/pivot_grid/local_store/m_local_store.ts index 174166bf6091..4ee6f3179b67 100644 --- a/packages/devextreme/js/__internal/grids/pivot_grid/local_store/m_local_store.ts +++ b/packages/devextreme/js/__internal/grids/pivot_grid/local_store/m_local_store.ts @@ -424,7 +424,6 @@ const LocalStore = Class.inherit((function () { if (dataSource.store() instanceof CustomStore && filter) { filter = processFilter(filter, fieldSelectors); - // @ts-expect-error return dataQuery(dataSource.items()).filter(filter).toArray(); } diff --git a/packages/devextreme/js/__internal/grids/pivot_grid/remote_store/m_remote_store.ts b/packages/devextreme/js/__internal/grids/pivot_grid/remote_store/m_remote_store.ts index 3412120c14f2..b514df7df779 100644 --- a/packages/devextreme/js/__internal/grids/pivot_grid/remote_store/m_remote_store.ts +++ b/packages/devextreme/js/__internal/grids/pivot_grid/remote_store/m_remote_store.ts @@ -561,7 +561,6 @@ class RemoteStore { skip: 0, take: 20, }).done((data) => { - // @ts-expect-error const normalizedArguments = normalizeLoadResult(data); d.resolve(pivotGridUtils.discoverObjectFields(normalizedArguments.data, fields)); }).fail(d.reject); diff --git a/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts b/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts index b76ec5607f45..d0a2002ba0b5 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/data_source_adapter/m_data_source_adapter.ts @@ -39,9 +39,7 @@ const getChildKeys = function (that, keys) { return childKeys; }; -// @ts-expect-error const applySorting = (data: any[], sort: any): any => queryByOptions( - // @ts-expect-error query(data), { sort, @@ -227,7 +225,6 @@ export class DataSourceAdapterTreeList extends DataSourceAdapter { result = result || []; for (let i = 0; i < data.length; i++) { - // @ts-expect-error const item = createObjectWithChanges(data[i]); key = this._keyGetter(item); @@ -479,7 +476,6 @@ export class DataSourceAdapterTreeList extends DataSourceAdapter { if (loadedData.length) { if (needLocalFiltering) { - // @ts-expect-error loadedData = query(loadedData).filter(filter).toArray(); } @@ -625,7 +621,6 @@ export class DataSourceAdapterTreeList extends DataSourceAdapter { public customizeLoadResultHandler(options) { const data = options.data = this._convertDataToPlainStructure(options.data); if (!options.remoteOperations.filtering && options.loadOptions.filter) { - // @ts-expect-error options.fullData = queryByOptions(query(options.data), { sort: options.loadOptions && options.loadOptions.sort }).toArray(); } this._updateHasItemsMap(options); diff --git a/packages/devextreme/js/__internal/scheduler/utils/loader/utils.ts b/packages/devextreme/js/__internal/scheduler/utils/loader/utils.ts index 8a2f31585a13..730fe0f87d52 100644 --- a/packages/devextreme/js/__internal/scheduler/utils/loader/utils.ts +++ b/packages/devextreme/js/__internal/scheduler/utils/loader/utils.ts @@ -19,6 +19,8 @@ export const normalizeDataSource = ( ...options, }; + // @ts-expect-error the public DataSource constructor is declared with the public + // option types, while `normalizeDataSourceOptions()` returns the internal ones return new DataSource(result); }; diff --git a/packages/devextreme/js/__internal/ui/collection/collection_widget.edit.ts b/packages/devextreme/js/__internal/ui/collection/collection_widget.edit.ts index 06d89a472973..85bcffe6447f 100644 --- a/packages/devextreme/js/__internal/ui/collection/collection_widget.edit.ts +++ b/packages/devextreme/js/__internal/ui/collection/collection_widget.edit.ts @@ -277,7 +277,6 @@ class CollectionWidget< if (that._disposed) { return; } - // @ts-expect-error arguments const items = normalizeLoadResult(loadResult).data; dataController.applyMapFunction(items); diff --git a/packages/devextreme/js/__internal/ui/collection/collection_widget.live_update.ts b/packages/devextreme/js/__internal/ui/collection/collection_widget.live_update.ts index d1d0c0fdfd0b..a73e374f5136 100644 --- a/packages/devextreme/js/__internal/ui/collection/collection_widget.live_update.ts +++ b/packages/devextreme/js/__internal/ui/collection/collection_widget.live_update.ts @@ -204,7 +204,6 @@ class CollectionWidgetLiveUpdate< } else { const changedItem = items[indexByKey(keyInfo, items, change.key)]; if (changedItem) { - // @ts-expect-error ts-error update(keyInfo, items, change.key, change.data).done(() => { this._renderItem( items.indexOf(changedItem), @@ -224,7 +223,6 @@ class CollectionWidgetLiveUpdate< isPartialRefresh?: boolean, ): void { when( - // @ts-expect-error ts-error isPartialRefresh ?? insert(keyInfo, items, change.data, change.index), ).done(() => { this._beforeItemElementInserted(change); diff --git a/packages/devextreme/js/__internal/ui/hierarchical_collection/data_adapter.ts b/packages/devextreme/js/__internal/ui/hierarchical_collection/data_adapter.ts index 4e5370ec834f..b5a5feb230b7 100644 --- a/packages/devextreme/js/__internal/ui/hierarchical_collection/data_adapter.ts +++ b/packages/devextreme/js/__internal/ui/hierarchical_collection/data_adapter.ts @@ -691,8 +691,9 @@ class DataAdapter { lookForParents(matches, 0); if (this.options.sort) { + // @ts-expect-error data/store_helper types `toArray()` as `unknown[]`; + // typing the node collection is left to a later iteration matches = storeHelper - // @ts-expect-error ts-error .queryByOptions(query(matches), { sort: this.options.sort, langParams: this.options.langParams, diff --git a/packages/devextreme/js/__internal/ui/selection/selection.strategy.deferred.ts b/packages/devextreme/js/__internal/ui/selection/selection.strategy.deferred.ts index d6e370a5d233..a261105c07d6 100644 --- a/packages/devextreme/js/__internal/ui/selection/selection.strategy.deferred.ts +++ b/packages/devextreme/js/__internal/ui/selection/selection.strategy.deferred.ts @@ -89,7 +89,6 @@ export default class DeferredStrategy< const queryParams = this._getQueryParams(); - // @ts-expect-error dataQuery return !!dataQuery([itemData], queryParams).filter(selectionFilter).toArray().length; } diff --git a/packages/devextreme/js/__internal/ui/selection/selection.strategy.standard.ts b/packages/devextreme/js/__internal/ui/selection/selection.strategy.standard.ts index 4e1ae4bd8b5d..81d5cd3ec7d5 100644 --- a/packages/devextreme/js/__internal/ui/selection/selection.strategy.standard.ts +++ b/packages/devextreme/js/__internal/ui/selection/selection.strategy.standard.ts @@ -139,11 +139,12 @@ export default class StandardStrategy< forceCombinedFilter, ); - let deselectedItems = []; + let deselectedItems: TItem[] = []; if (isDeselect) { const { selectedItems } = this.options; + // @ts-expect-error data/array_query types `toArray()` as `unknown[]`; + // typing the query result by item is left to a later iteration deselectedItems = combinedFilter && keys.length !== selectedItems.length - // @ts-expect-error dataQuery ? dataQuery(selectedItems).filter(combinedFilter).toArray() : selectedItems.slice(0); } diff --git a/packages/devextreme/js/__internal/ui/selection/selection.strategy.ts b/packages/devextreme/js/__internal/ui/selection/selection.strategy.ts index 932eb2111af9..1a7b9056d022 100644 --- a/packages/devextreme/js/__internal/ui/selection/selection.strategy.ts +++ b/packages/devextreme/js/__internal/ui/selection/selection.strategy.ts @@ -199,7 +199,6 @@ export default class SelectionStrategy< if (localFilter && !isSelectAll) { filteredItems = filteredItems.filter(localFilter); } else if (needLoadAllData) { - // @ts-expect-error dataQuary filteredItems = dataQuery(filteredItems).filter(remoteFilter).toArray(); } diff --git a/packages/devextreme/js/__internal/ui/shared/ui.editor_factory_mixin.ts b/packages/devextreme/js/__internal/ui/shared/ui.editor_factory_mixin.ts index 0458f8f1c274..501d8e2b300b 100644 --- a/packages/devextreme/js/__internal/ui/shared/ui.editor_factory_mixin.ts +++ b/packages/devextreme/js/__internal/ui/shared/ui.editor_factory_mixin.ts @@ -180,7 +180,6 @@ function prepareLookupEditor(options): void { } if (isObject(dataSource) || Array.isArray(dataSource)) { - // @ts-expect-error ts- dataSource = normalizeDataSourceOptions(dataSource); if (isFilterRow) { postProcess = dataSource.postProcess;