From f9d30464f324e23c70809fdf9122f7b1e191ba8a Mon Sep 17 00:00:00 2001 From: "anna.shakhova" <68295572+anna-shakhova@users.noreply.github.com> Date: Fri, 7 Aug 2026 16:22:02 +0200 Subject: [PATCH 1/2] Grids: type DataController.filter and the filter expression types --- .../data_controller/data_controller.ts | 30 ++--- .../grids/grid_core/data_controller/types.ts | 103 +++++++++++++----- .../grids/grid_core/search/m_search.ts | 6 +- 3 files changed, 95 insertions(+), 44 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts index 771bf9cfd698..aa9416b374f0 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts @@ -14,8 +14,6 @@ /* eslint-disable max-depth */ /* eslint-disable no-param-reassign */ /* eslint-disable no-plusplus */ -/* eslint-disable prefer-rest-params */ -/* eslint-disable prefer-spread */ import type { DataSource } from '@js/common/data'; import $ from '@js/core/renderer'; import type { Callback } from '@js/core/utils/callbacks'; @@ -49,9 +47,12 @@ import gridCoreUtils from '../m_utils'; import type { VirtualScrollController } from '../virtual_scrolling/m_virtual_scrolling_core'; import { DataHelperMixin } from './data_helper_mixin'; import type { + BinaryDataFilterExpression, CallbackFlags, DataChange, + DataFilter, DataSourceAdapterLike, + HandleDataChangedArguments, Filter, Item, PagingChanges, @@ -607,8 +608,8 @@ export class DataController extends DataHelperMixin(modules.Controller) { this.pushed.fire(changes); } - public fireError(...args: any[]) { - this.dataErrorOccurred.fire(errors.Error.apply(errors, args)); + public fireError(...args: unknown[]) { + this.dataErrorOccurred.fire(errors.Error(...args)); } private applyPagingOptions(dataSource: PagingDataSource): PagingChanges { @@ -1262,7 +1263,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { /** * @extended: filter_row, filter_sync, header_filter, search */ - protected _calculateAdditionalFilter(): Filter { + protected _calculateAdditionalFilter(): DataFilter { return null; } @@ -1294,23 +1295,24 @@ export class DataController extends DataHelperMixin(modules.Controller) { this._isFilterApplying = false; } - private filter(filterExpr) { - const dataSource = this._dataSource; - const filter = dataSource?.filter(); - const langParams = dataSource?.loadOptions?.()?.langParams; + private filter(): DataFilter; + private filter(filterExpr: DataFilter): void; + private filter(...binaryFilterExpr: BinaryDataFilterExpression): void; + private filter(...filterArgs: [] | [DataFilter] | BinaryDataFilterExpression): DataFilter | void { + const filter: DataFilter = this._dataSource?.filter(); + const langParams = this._dataSource?.loadOptions?.()?.langParams; - if (arguments.length === 0) { + if (filterArgs.length === 0) { return filter; } - filterExpr = arguments.length > 1 ? Array.prototype.slice.call(arguments, 0) : filterExpr; + const filterExpr: DataFilter = filterArgs.length === 1 ? filterArgs[0] : filterArgs; if (gridCoreUtils.equalFilterParameters(filter, filterExpr, langParams)) { return; } - if (dataSource) { - dataSource.filter(filterExpr); - } + + this._dataSource?.filter(filterExpr); this._applyFilter(); } diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts index d27b50eeac58..7cac2ddf0979 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts @@ -1,32 +1,11 @@ import type { DataSource } from '@js/common/data'; +import type { SearchOperation } from '@js/common/data.types'; +import type { ScalarFilterValue } from '@js/common/grids'; import type { DeferredObj } from '@js/core/utils/deferred'; import type { ChangedEvent, OperationTypes } from '../data_source_adapter/types'; -export interface SyncPagingOptions { - paginate?: boolean; - pageSize?: number; - pageIndex?: number; -} - -export interface PagingChanges { - hasChanges: boolean; - isPaginateChanged: boolean; - isPageSizeChanged: boolean; - isPageIndexChanged: boolean; -} - -/** - * Either a raw DataSource or a DataSourceAdapter — the two are not - * interchangeable: the adapter's `pageSize()` returns 0 while paginate is off, - * and its `pageIndex()` is routed through virtual scrolling. - */ -export interface PagingDataSource { - paginate: (value?: boolean) => boolean | undefined; - pageSize: (value?: number) => number | undefined; - pageIndex: (value?: number) => number | undefined; - requireTotalCount: (value?: boolean) => boolean | undefined; -} +/** data */ export interface DataSourceAdapterLike { _dataSource: DataSource; @@ -56,6 +35,13 @@ export type FilterExpression = ((data: UserData) => boolean) | unknown[]; export type Filter = FilterExpression | null | undefined; +export interface HandleDataChangedArguments { + changeType?: 'refresh' | 'update' | 'loadError'; + isDelayed?: boolean; + isLiveUpdate?: boolean; + error?: unknown; +} + interface DataChangeBase { isFirstRender?: boolean; repaintChangesOnly?: boolean; @@ -95,10 +81,73 @@ export type DataChange = | UpdateChange | (DataChangeBase & { changeType: 'refresh', event: unknown; virtualColumnsScrolling: boolean }) | (DataChangeBase & { changeType: 'refresh', useProcessedItemsCache: boolean; cancelEmptyChanges: boolean }); -export type PagingOptionName = 'pageIndex' | 'pageSize'; - -export type PagingResult = number | DeferredObj | Promise; +/** callbacks */ export interface CallbackFlags { stopOnFalse: boolean; } + +/** paging */ + +export interface SyncPagingOptions { + paginate?: boolean; + pageSize?: number; + pageIndex?: number; +} + +export interface PagingChanges { + hasChanges: boolean; + isPaginateChanged: boolean; + isPageSizeChanged: boolean; + isPageIndexChanged: boolean; +} + +/** + * Either a raw DataSource or a DataSourceAdapter — the two are not + * interchangeable: the adapter's `pageSize()` returns 0 while paginate is off, + * and its `pageIndex()` is routed through virtual scrolling. + */ +export interface PagingDataSource { + paginate: (value?: boolean) => boolean | undefined; + pageSize: (value?: number) => number | undefined; + pageIndex: (value?: number) => number | undefined; + requireTotalCount: (value?: boolean) => boolean | undefined; +} + +export type PagingOptionName = 'pageIndex' | 'pageSize'; + +export type PagingResult = number | DeferredObj | Promise; + +/** filter */ + +export type FilterCombiner = 'and' | 'or'; + +/** + * The operator may be omitted — `=` is implied. Only data layer operations + * are allowed here: column operations such as `between` or `anyof` belong to + * `filterValue` and are expanded into these before they reach the store. + */ +export type BinaryDataFilterExpression = [string, ScalarFilterValue] + | [string, SearchOperation, ScalarFilterValue]; + +/** + * A binary expression, a negation, or a group of expressions. + * The combiner between neighbors may be omitted — `and` is implied. + */ +export type DataFilterExpression = BinaryDataFilterExpression + | ['!', DataFilterExpression] + | [DataFilterExpression, ...(FilterCombiner | DataFilterExpression)[]]; + +export type DataFilterPredicate = (data: UserData) => boolean; + +/** + * The grid-internal "match nothing" filter. Not a data layer filter expression: + * the data controller intercepts it and resolves the load with an empty result. + */ +export type MatchNothingFilter = ['!']; + +export type DataFilter = DataFilterExpression + | DataFilterPredicate + | MatchNothingFilter + | null + | undefined; 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 617b554e651b..2b44a12cdf3f 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 @@ -12,7 +12,7 @@ import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import type { ToolbarItem } from '@ts/grids/new/grid_core/toolbar/types'; import type { DataController } from '../data_controller/data_controller'; -import type { Filter } from '../data_controller/types'; +import type { DataFilter } from '../data_controller/types'; import type { HeaderPanel } from '../header_panel/m_header_panel'; import modules from '../m_modules'; import type { ModuleType, OptionChanged } from '../m_types'; @@ -62,7 +62,7 @@ const dataController = ( return super.publicMethods().concat(['searchByText']); } - protected _calculateAdditionalFilter(): Filter { + protected _calculateAdditionalFilter(): DataFilter { const dataSource = this._dataController?.getDataSource?.(); const langParams = dataSource?.loadOptions?.()?.langParams; @@ -76,7 +76,7 @@ const dataController = ( this.option('searchPanel.text', text); } - private calculateSearchFilter(text: string | undefined, langParams?: LangParams): Filter { + private calculateSearchFilter(text: string | undefined, langParams?: LangParams): DataFilter { let column; const columns = this._columnsController.getColumns(); const searchVisibleColumnsOnly = this.option('searchPanel.searchVisibleColumnsOnly'); From 98cd59360ab30b9f1a4faa68458d10d30783619a Mon Sep 17 00:00:00 2001 From: "anna.shakhova" <68295572+anna-shakhova@users.noreply.github.com> Date: Mon, 10 Aug 2026 10:15:26 +0200 Subject: [PATCH 2/2] rebase --- .../grid_core/data_controller/data_controller.ts | 8 +++----- .../grids/grid_core/data_controller/types.ts | 11 ----------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts index aa9416b374f0..a5702e8a0f42 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/data_controller.ts @@ -52,8 +52,6 @@ import type { DataChange, DataFilter, DataSourceAdapterLike, - HandleDataChangedArguments, - Filter, Item, PagingChanges, PagingDataSource, @@ -352,16 +350,16 @@ export class DataController extends DataHelperMixin(modules.Controller) { return adapter ? adapter._dataSource : null; } - public getCombinedFilter(returnDataField?: boolean): Filter { + public getCombinedFilter(returnDataField?: boolean): DataFilter { return this.combinedFilter(undefined, returnDataField); } - private combinedFilter(filter: Filter, returnDataField?: boolean): Filter { + private combinedFilter(filter: DataFilter, returnDataField?: boolean): DataFilter { if (!this._dataSource) { return filter; } - let combined: Filter = filter ?? this._dataSource.filter(); + let combined: DataFilter = filter ?? this._dataSource.filter(); const isColumnsTypesDefined = this._columnsController.isDataSourceApplied() || this._columnsController.isAllDataTypesDefined(); diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts index 7cac2ddf0979..a52e7554d004 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/types.ts @@ -31,17 +31,6 @@ export interface Item { removed?: boolean; } -export type FilterExpression = ((data: UserData) => boolean) | unknown[]; - -export type Filter = FilterExpression | null | undefined; - -export interface HandleDataChangedArguments { - changeType?: 'refresh' | 'update' | 'loadError'; - isDelayed?: boolean; - isLiveUpdate?: boolean; - error?: unknown; -} - interface DataChangeBase { isFirstRender?: boolean; repaintChangesOnly?: boolean;