From b20a301857743d3e300aaf64a28847725f5c86ac 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] Grids: type DataController.filter and the filter expression types --- .../data_controller/data_controller.ts | 30 +++--- .../grids/grid_core/data_controller/types.ts | 100 ++++++++++++------ .../grids/grid_core/search/m_search.ts | 6 +- 3 files changed, 87 insertions(+), 49 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 df7e6c2009b6..08f651f90e7e 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 ArrayStore from '@js/common/data/array_store'; import { CustomStore } from '@js/common/data/custom_store'; @@ -50,10 +48,11 @@ 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, - Filter, HandleDataChangedArguments, Item, PagingChanges, @@ -582,8 +581,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 { @@ -1232,7 +1231,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { /** * @extended: filter_row, filter_sync, header_filter, search */ - protected _calculateAdditionalFilter(): Filter { + protected _calculateAdditionalFilter(): DataFilter { return null; } @@ -1264,23 +1263,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 3b635258dc4e..9a29028f0b97 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 { 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; @@ -52,10 +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; @@ -101,10 +76,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');