From d8e4b81ba8d5fc2653a30082e410df652127eba2 Mon Sep 17 00:00:00 2001 From: Vasily Strelyaev Date: Wed, 15 Jan 2025 18:16:21 +0200 Subject: [PATCH] address the scenario when extrenal type parameter is passed to DeepPartial cond type --- e2e/compilation-cases/T1263537.ts | 72 +++++++++++++++++++ e2e/compilation-cases/core.ts | 2 +- e2e/compilation-cases/package.json | 4 +- .../devextreme-angular/src/common/index.ts | 1 + packages/devextreme-react/src/common/index.ts | 1 + packages/devextreme-vue/src/common/index.ts | 1 + .../js/__internal/ui/chat/messagelist.ts | 6 +- packages/devextreme/js/common.d.ts | 16 +++-- packages/devextreme/js/common/data.d.ts | 3 +- packages/devextreme/js/common/grids.d.ts | 5 +- packages/devextreme/js/core/index.d.ts | 10 +-- packages/devextreme/js/core/index.types.d.ts | 3 + packages/devextreme/js/data/store.d.ts | 2 +- packages/devextreme/ts/aliases.d.ts | 6 +- packages/devextreme/ts/dx.all.d.ts | 29 ++++---- pnpm-lock.yaml | 15 ++-- 16 files changed, 128 insertions(+), 48 deletions(-) create mode 100644 e2e/compilation-cases/T1263537.ts create mode 100644 packages/devextreme/js/core/index.types.d.ts diff --git a/e2e/compilation-cases/T1263537.ts b/e2e/compilation-cases/T1263537.ts new file mode 100644 index 000000000000..4f29f1132809 --- /dev/null +++ b/e2e/compilation-cases/T1263537.ts @@ -0,0 +1,72 @@ +import { + ODataStore, CustomStore, Store, LoadOptions, +} from 'devextreme/common/data'; + +import { DeepPartial } from 'devextreme/common'; + +// Remove the 'ts-expect-error' below to see the current issue: +// Entity is not compatible with DeepPartial +function initCustomStore(oDataStore: ODataStore): Store { + const customStore = new CustomStore({ + key: 'id', + byKey: (key: Id) => oDataStore.byKey(key), + insert: (e: Entity) => oDataStore.insert(e), + load: (options: LoadOptions) => oDataStore.load(options), + remove: (key: Id) => oDataStore.remove(key), + totalCount: (obj) => oDataStore.totalCount(obj), + // @ts-expect-error - The initial issue + update: (key: Id, updated: Entity) => oDataStore.update(key, updated), + }); + return customStore; +} + +// If this TS issue is fixed: https://github.com/microsoft/TypeScript/issues/23132 +// there will be a good workaround to use type constraints to make such an assignment possible +function initCustomStore3( + oDataStore: ODataStore, +): Store { + const customStore = new CustomStore({ + key: 'id', + byKey: (key: Id) => oDataStore.byKey(key), + insert: (e: Entity) => oDataStore.insert(e), + load: (options: LoadOptions) => oDataStore.load(options), + remove: (key: Id) => oDataStore.remove(key), + totalCount: (obj) => oDataStore.totalCount(obj), + // @ts-expect-error - How it will hopefully work in TypeScript 5.8.0+ + update: (key: Id, updated: Entity) => oDataStore.update(key, updated), + }); + return customStore; +} + +// Currenlty, we can only publish DeepPartial, which will be a black box +// equal to only itself when initialized with a type parameter. +// Users will be able to make DeepPartial type to avoid the TS error. +function initCustomStore4(oDataStore: ODataStore): Store { + const customStore = new CustomStore({ + key: 'id', + byKey: (key: Id) => oDataStore.byKey(key), + insert: (e: Entity) => oDataStore.insert(e), + load: (options: LoadOptions) => oDataStore.load(options), + remove: (key: Id) => oDataStore.remove(key), + totalCount: (obj) => oDataStore.totalCount(obj), + update: (key: Id, updated: DeepPartial) => oDataStore.update(key, updated), + }); + return customStore; +} + +// Proof that it currently works if we are dealing with resolved types, not type parameters +type Key = number; +type Data = object; + +function initCustomStore2(oDataStore: ODataStore): Store { + const customStore = new CustomStore({ + key: 'id', + byKey: (key: Key) => oDataStore.byKey(key), + insert: (e: Data) => oDataStore.insert(e), + load: (options: LoadOptions) => oDataStore.load(options), + remove: (key: Key) => oDataStore.remove(key), + totalCount: (obj) => oDataStore.totalCount(obj), + update: (key: Key, updated: Data) => oDataStore.update(key, updated), + }); + return customStore; +} diff --git a/e2e/compilation-cases/core.ts b/e2e/compilation-cases/core.ts index 8a27372146bc..dda4556609bc 100644 --- a/e2e/compilation-cases/core.ts +++ b/e2e/compilation-cases/core.ts @@ -48,7 +48,7 @@ import { Permutations } from 'devextreme/core'; const expected32: Expected3 = ANY as Permutations<'a' | 'b' | 'c'>; } -import { Scalar } from 'devextreme/core'; +import { Scalar } from 'devextreme/core/index.types'; { interface TestInterface { i: any } diff --git a/e2e/compilation-cases/package.json b/e2e/compilation-cases/package.json index bd5cbcdeaa46..92cefea319b6 100644 --- a/e2e/compilation-cases/package.json +++ b/e2e/compilation-cases/package.json @@ -6,10 +6,12 @@ "scripts": { "test": "tsc --noEmit" }, + "dependencies": { + "devextreme": "workspace:*" + }, "devDependencies": { "@angular/common": "11.2.14", "@types/jquery": "3.5.29", - "devextreme": "workspace:*", "jquery": "3.7.1", "typescript": "4.9.5" } diff --git a/packages/devextreme-angular/src/common/index.ts b/packages/devextreme-angular/src/common/index.ts index 2b0c97f54bd0..2cf699c7801a 100644 --- a/packages/devextreme-angular/src/common/index.ts +++ b/packages/devextreme-angular/src/common/index.ts @@ -9,6 +9,7 @@ export { CustomRule, DataStructure, DataType, + DeepPartial, DefaultOptionsRule, Direction, DisplayMode, diff --git a/packages/devextreme-react/src/common/index.ts b/packages/devextreme-react/src/common/index.ts index c9e79d048002..a148ad8c6a64 100644 --- a/packages/devextreme-react/src/common/index.ts +++ b/packages/devextreme-react/src/common/index.ts @@ -9,6 +9,7 @@ export { CustomRule, DataStructure, DataType, + DeepPartial, DefaultOptionsRule, Direction, DisplayMode, diff --git a/packages/devextreme-vue/src/common/index.ts b/packages/devextreme-vue/src/common/index.ts index c9e79d048002..a148ad8c6a64 100644 --- a/packages/devextreme-vue/src/common/index.ts +++ b/packages/devextreme-vue/src/common/index.ts @@ -9,6 +9,7 @@ export { CustomRule, DataStructure, DataType, + DeepPartial, DefaultOptionsRule, Direction, DisplayMode, diff --git a/packages/devextreme/js/__internal/ui/chat/messagelist.ts b/packages/devextreme/js/__internal/ui/chat/messagelist.ts index 8651083b49a9..3c834437b534 100644 --- a/packages/devextreme/js/__internal/ui/chat/messagelist.ts +++ b/packages/devextreme/js/__internal/ui/chat/messagelist.ts @@ -1,10 +1,10 @@ +import type { + DeepPartial, +} from '@js/common'; import { Guid } from '@js/common'; import type { Format } from '@js/common/core/localization'; import dateLocalization from '@js/common/core/localization/date'; import messageLocalization from '@js/common/core/localization/message'; -import type { - DeepPartial, -} from '@js/core/index'; import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import resizeObserverSingleton from '@js/core/resize_observer'; diff --git a/packages/devextreme/js/common.d.ts b/packages/devextreme/js/common.d.ts index 3410b323816b..05ae181add80 100644 --- a/packages/devextreme/js/common.d.ts +++ b/packages/devextreme/js/common.d.ts @@ -1,16 +1,14 @@ import { PositionConfig } from './common/core/animation'; + import type { + Scalar, OmitInternal, -} from './core'; +} from './core/index.types'; import { Device, } from './common/core/environment'; -import { - DeepPartial, -} from './core'; - import type dxDraggable from './ui/draggable'; import type dxScrollable from './ui/scroll_view/ui.scrollable'; import type dxSortable from './ui/sortable'; @@ -884,6 +882,14 @@ export type DefaultOptionsRule = { options: DeepPartial; }; +/** +* @public +* @namespace DevExpress.common +*/ +export type DeepPartial = T extends Scalar ? T : { + [P in keyof T]?: DeepPartial; +}; + /** @public */ export type FloatingActionButtonDirection = 'auto' | 'up' | 'down'; diff --git a/packages/devextreme/js/common/data.d.ts b/packages/devextreme/js/common/data.d.ts index 92644d033f79..501a229f2462 100644 --- a/packages/devextreme/js/common/data.d.ts +++ b/packages/devextreme/js/common/data.d.ts @@ -1,4 +1,5 @@ /* eslint-disable max-classes-per-file */ +import { DeepPartial } from '../common'; import { DxExtendedPromise, DxPromise } from '../core/utils/deferred'; import AbstractStore, { AbstractStoreOptions } from '../data/abstract_store'; @@ -303,7 +304,7 @@ export type CustomStoreOptions< * @type_function_return Promise * @public */ - update?: ((key: TKey, values: TItem) => PromiseLike); + update?: ((key: TKey, values: DeepPartial) => PromiseLike); /** * @docid * @default undefined diff --git a/packages/devextreme/js/common/grids.d.ts b/packages/devextreme/js/common/grids.d.ts index a698df0baa24..a9a577c9d1ae 100644 --- a/packages/devextreme/js/common/grids.d.ts +++ b/packages/devextreme/js/common/grids.d.ts @@ -13,13 +13,10 @@ import { Sortable, SortOrder, ValidationRule, + DeepPartial, template, } from '../common'; -import { - DeepPartial, -} from '../core/index'; - import { UserDefinedElement, DxElement, diff --git a/packages/devextreme/js/core/index.d.ts b/packages/devextreme/js/core/index.d.ts index d989f627fb1b..faaa1c7114bc 100644 --- a/packages/devextreme/js/core/index.d.ts +++ b/packages/devextreme/js/core/index.d.ts @@ -1,3 +1,6 @@ +export { Scalar, OmitInternal } from './index.types'; +export { DeepPartial } from '../common'; + type KeyOf = T extends never ? never : keyof T; type KeysOf = @@ -19,11 +22,6 @@ export type Xor> | Seal>; -export type Scalar = undefined | null | string | String | number | Number | bigint | BigInteger | boolean | Boolean | Date | Function | Symbol | Array; -export type DeepPartial = T extends Scalar ? T : { - [P in keyof T]?: DeepPartial; -}; - type ItemType = T extends (infer TItem)[] ? TItem : T; type Property = T extends Partial> ? TValue : never; type OwnPropertyType = Property, TPropName>; @@ -33,8 +31,6 @@ export type PropertyType = ? PropertyType, TNestedProps> : OwnPropertyType; -export type OmitInternal = Omit; - /** * IncrementalCounter[1]=2, IncrementalCounter[2]=3, IncrementalCounter[3]=4, ... */ diff --git a/packages/devextreme/js/core/index.types.d.ts b/packages/devextreme/js/core/index.types.d.ts new file mode 100644 index 000000000000..8b610d9cbc49 --- /dev/null +++ b/packages/devextreme/js/core/index.types.d.ts @@ -0,0 +1,3 @@ +export type Scalar = undefined | null | string | String | number | Number | bigint | BigInteger | boolean | Boolean | Date | Function | Symbol | Array; + +export type OmitInternal = Omit; diff --git a/packages/devextreme/js/data/store.d.ts b/packages/devextreme/js/data/store.d.ts index 69688449b727..7ac11f5c2ceb 100644 --- a/packages/devextreme/js/data/store.d.ts +++ b/packages/devextreme/js/data/store.d.ts @@ -1,5 +1,5 @@ import { DxPromise, DxExtendedPromise } from '../core/utils/deferred'; -import { DeepPartial } from '../core'; +import { DeepPartial } from '../common'; import { FilterDescriptor, GroupDescriptor, LoadOptions } from '../common/data'; /** diff --git a/packages/devextreme/ts/aliases.d.ts b/packages/devextreme/ts/aliases.d.ts index 4484af09af53..17c318f75e36 100644 --- a/packages/devextreme/ts/aliases.d.ts +++ b/packages/devextreme/ts/aliases.d.ts @@ -837,6 +837,10 @@ declare namespace DevExpress { export class TransitionExecutor extends common.core.animation.TransitionExecutor {} } +declare namespace DevExpress.core { + export type DeepPartial = common.DeepPartial; +} + declare namespace DevExpress.localization { export const formatDate: typeof common.core.localization.formatDate; export const formatMessage: typeof common.core.localization.formatMessage; @@ -877,7 +881,7 @@ declare namespace DevExpress.data { export const query: typeof common.data.query; export type Query = common.data.Query; export const setErrorHandler: typeof common.data.setErrorHandler; - export class Store extends common.data.Store{} + export class Store extends common.data.Store {} } declare namespace DevExpress.data.CustomStore { diff --git a/packages/devextreme/ts/dx.all.d.ts b/packages/devextreme/ts/dx.all.d.ts index 08cde15f679e..fd44de208214 100644 --- a/packages/devextreme/ts/dx.all.d.ts +++ b/packages/devextreme/ts/dx.all.d.ts @@ -627,7 +627,7 @@ declare module DevExpress { | DevExpress.common.core.environment.Device | DevExpress.common.core.environment.Device[] | ((device: DevExpress.common.core.environment.Device) => boolean); - options: DevExpress.core.DeepPartial; + options: DevExpress.common.DeepPartial; }; /** * [descr:DOMComponent] @@ -981,6 +981,11 @@ declare module DevExpress.common { | 'boolean' | 'object' | 'datetime'; + export type DeepPartial = T extends DevExpress.core.Scalar + ? T + : { + [P in keyof T]?: DeepPartial; + }; export type Direction = 'bottom' | 'left' | 'right' | 'top'; export type DisplayMode = 'adaptive' | 'compact' | 'full'; export type DragDirection = 'both' | 'horizontal' | 'vertical'; @@ -3047,7 +3052,7 @@ declare module DevExpress.common.data { /** * [descr:CustomStoreOptions.update] */ - update?: (key: TKey, values: TItem) => PromiseLike; + update?: (key: TKey, values: DeepPartial) => PromiseLike; /** * [descr:CustomStoreOptions.useDefaultSearch] */ @@ -3811,7 +3816,7 @@ declare module DevExpress.common.data { push( changes: Array<{ type: 'insert' | 'update' | 'remove'; - data?: DevExpress.core.DeepPartial; + data?: DeepPartial; key?: TKey; index?: number; }> @@ -3832,7 +3837,7 @@ declare module DevExpress.common.data { */ update( key: TKey, - values: DevExpress.core.DeepPartial + values: DeepPartial ): DevExpress.core.utils.DxExtendedPromise; } /** @@ -4432,7 +4437,7 @@ declare module DevExpress.common.grids { */ setCellValue?: ( this: ColumnBase, - newData: DevExpress.core.DeepPartial, + newData: DeepPartial, value: any, currentRowData: TRowData ) => void | PromiseLike; @@ -4765,7 +4770,7 @@ declare module DevExpress.common.grids { /** * [descr:DataChange.data] */ - data: DevExpress.core.DeepPartial; + data: DeepPartial; /** * [descr:DataChange.insertAfterKey] */ @@ -6234,7 +6239,7 @@ declare module DevExpress.common.grids { /** * [descr:RowUpdatingInfo.newData] */ - newData: DevExpress.core.DeepPartial; + newData: DeepPartial; /** * [descr:RowUpdatingInfo.key] */ @@ -6264,7 +6269,7 @@ declare module DevExpress.common.grids { /** * [descr:RowValidatingInfo.newData] */ - readonly newData: DevExpress.core.DeepPartial; + readonly newData: DeepPartial; /** * @docid * @type object @@ -6518,14 +6523,6 @@ declare module DevExpress.core { left: number; top: number; } - /** - * @deprecated Attention! This type is for internal purposes only. If you used it previously, please submit a ticket to our {@link https://supportcenter.devexpress.com/ticket/create Support Center}. We will check if there is an alternative solution. - */ - export type DeepPartial = T extends Scalar - ? T - : { - [P in keyof T]?: DeepPartial; - }; /** * [descr:DevicesObject] */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5765bb2df48..309e3c2dd2f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -753,6 +753,10 @@ importers: version: 4.10.0(webpack@5.94.0) e2e/compilation-cases: + dependencies: + devextreme: + specifier: workspace:* + version: link:../../packages/devextreme/artifacts/npm/devextreme devDependencies: '@angular/common': specifier: 11.2.14 @@ -760,9 +764,6 @@ importers: '@types/jquery': specifier: 3.5.29 version: 3.5.29 - devextreme: - specifier: workspace:* - version: link:../../packages/devextreme/artifacts/npm/devextreme jquery: specifier: 3.7.1 version: 3.7.1 @@ -19750,15 +19751,13 @@ snapshots: transitivePeerDependencies: - '@parcel/core' - '@parcel/cache@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.15))(@swc/helpers@0.5.15)': + '@parcel/cache@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.15))': dependencies: '@parcel/core': 2.12.0(@swc/helpers@0.5.15) '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.15))(@swc/helpers@0.5.15) '@parcel/logger': 2.12.0 '@parcel/utils': 2.12.0 lmdb: 2.8.5 - transitivePeerDependencies: - - '@swc/helpers' '@parcel/codeframe@2.12.0': dependencies: @@ -19818,7 +19817,7 @@ snapshots: '@parcel/core@2.12.0(@swc/helpers@0.5.15)': dependencies: '@mischnic/json-sourcemap': 0.1.1 - '@parcel/cache': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.15))(@swc/helpers@0.5.15) + '@parcel/cache': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.15)) '@parcel/diagnostic': 2.12.0 '@parcel/events': 2.12.0 '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.15))(@swc/helpers@0.5.15) @@ -20233,7 +20232,7 @@ snapshots: '@parcel/types@2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.15))(@swc/helpers@0.5.15)': dependencies: - '@parcel/cache': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.15))(@swc/helpers@0.5.15) + '@parcel/cache': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.15)) '@parcel/diagnostic': 2.12.0 '@parcel/fs': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.15))(@swc/helpers@0.5.15) '@parcel/package-manager': 2.12.0(@parcel/core@2.12.0(@swc/helpers@0.5.15))(@swc/helpers@0.5.15)