Skip to content

Commit 9a95b45

Browse files
Map: improve types (#31001)
Co-authored-by: Alexander Kozlovskiy <[email protected]>
1 parent f5a7fbc commit 9a95b45

File tree

8 files changed

+842
-541
lines changed

8 files changed

+842
-541
lines changed

packages/devextreme/js/__internal/ui/map/m_map.ts

Lines changed: 119 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { DefaultOptionsRule } from '@js/common';
12
import eventsEngine from '@js/common/core/events/core/events_engine';
23
import pointerEvents from '@js/common/core/events/pointer';
34
import { addNamespace } from '@js/common/core/events/utils/index';
@@ -6,21 +7,22 @@ import devices from '@js/core/devices';
67
import type { dxElementWrapper } from '@js/core/renderer';
78
import $ from '@js/core/renderer';
89
import { wrapToArray } from '@js/core/utils/array';
9-
// @ts-expect-error
10-
import { fromPromise } from '@js/core/utils/deferred';
1110
import { extend } from '@js/core/utils/extend';
1211
import { titleize } from '@js/core/utils/inflector';
13-
import { each } from '@js/core/utils/iterator';
1412
import { isNumeric } from '@js/core/utils/type';
13+
import type { DxEvent } from '@js/events';
1514
import type { Properties } from '@js/ui/map';
1615
import errors from '@js/ui/widget/ui.errors';
16+
import { fromPromise } from '@ts/core/utils/m_deferred';
1717
import type { OptionChanged } from '@ts/core/widget/types';
1818
import Widget from '@ts/core/widget/widget';
1919

20+
import type { LocationOption } from './m_provider.dynamic';
2021
import azure from './m_provider.dynamic.azure';
2122
import bing from './m_provider.dynamic.bing';
2223
import google from './m_provider.dynamic.google';
23-
// NOTE external urls must have protocol explicitly specified (because inside Cordova package the protocol is "file:")
24+
// NOTE external urls must have protocol explicitly specified
25+
// (because inside Cordova package the protocol is "file:")
2426
import googleStatic from './m_provider.google_static';
2527

2628
const PROVIDERS = {
@@ -37,11 +39,18 @@ const MAP_SHIELD_CLASS = 'dx-map-shield';
3739
export interface MapProperties extends Properties {
3840
onUpdated?: () => {};
3941

40-
bounds?: Record<string, unknown>;
42+
bounds?: {
43+
southWest?: LocationOption | null;
44+
northEast?: LocationOption | null;
45+
};
4146
}
4247

4348
class Map extends Widget<MapProperties> {
44-
_optionChangeBag?: Record<string, unknown> | null;
49+
_optionChangeBag?: {
50+
resolve: (value?: unknown) => void;
51+
added: MapProperties['markers'] | MapProperties['routes'];
52+
removed: MapProperties['markers'] | MapProperties['routes'];
53+
} | null;
4554

4655
_lastAsyncAction!: Promise<void>;
4756

@@ -51,9 +60,12 @@ class Map extends Widget<MapProperties> {
5160

5261
_suppressAsyncAction?: boolean;
5362

54-
_rendered!: Record<string, unknown>;
63+
_rendered!: {
64+
markers?: MapProperties['markers'] | MapProperties['routes'];
65+
routes?: MapProperties['routes'] | MapProperties['markers'];
66+
};
5567

56-
_$container?: dxElementWrapper;
68+
_$container!: dxElementWrapper;
5769

5870
_getDefaultOptions(): MapProperties {
5971
return {
@@ -104,10 +116,10 @@ class Map extends Widget<MapProperties> {
104116
};
105117
}
106118

107-
_defaultOptionsRules() {
119+
_defaultOptionsRules(): DefaultOptionsRule<MapProperties>[] {
108120
return super._defaultOptionsRules().concat([
109121
{
110-
device() {
122+
device(): boolean {
111123
return devices.real().deviceType === 'desktop' && !devices.isSimulator();
112124
},
113125
options: {
@@ -117,7 +129,7 @@ class Map extends Widget<MapProperties> {
117129
]);
118130
}
119131

120-
ctor(element, options) {
132+
ctor(element: Element, options: MapProperties): void {
121133
super.ctor(element, options);
122134

123135
if (options) {
@@ -165,7 +177,7 @@ class Map extends Widget<MapProperties> {
165177
return false;
166178
}
167179

168-
_checkOption(option): void {
180+
_checkOption(option: 'markers' | 'routes' | 'provider'): void {
169181
const value = this.option(option);
170182

171183
if (option === 'markers' && !Array.isArray(value)) {
@@ -190,16 +202,16 @@ class Map extends Widget<MapProperties> {
190202
eventsEngine.on(this.$element(), eventName, this._cancelEvent.bind(this));
191203
}
192204

193-
_cancelEvent(e): void {
194-
const cancelByProvider = this._provider?.isEventsCanceled(e) && !this.option('disabled');
205+
_cancelEvent(e: DxEvent): void {
206+
const { disabled } = this.option();
207+
const cancelByProvider = this._provider?.isEventsCanceled(e) && !disabled;
195208
if (cancelByProvider) {
196209
e.stopPropagation();
197210
}
198211
}
199212

200-
_saveRendered(option): void {
201-
const value = this.option(option);
202-
// @ts-expect-error ts-error
213+
_saveRendered(option: 'markers' | 'routes'): void {
214+
const { [option]: value = [] } = this.option();
203215
this._rendered[option] = value.slice();
204216
}
205217

@@ -211,28 +223,34 @@ class Map extends Widget<MapProperties> {
211223
this._saveRendered('markers');
212224
this._saveRendered('routes');
213225

214-
const { provider } = this.option();
226+
const { provider = 'google' } = this.option();
227+
const Provider = PROVIDERS[provider];
215228

216-
// @ts-expect-error ts-error
217-
this._provider = new PROVIDERS[provider](this, this._$container);
229+
this._provider = new Provider(this, this._$container);
230+
231+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
218232
this._queueAsyncAction('render', this._rendered.markers, this._rendered.routes);
219233
}
220234

221235
_renderShield(): void {
222-
let $shield;
236+
const { disabled } = this.option();
223237

224-
if (this.option('disabled')) {
225-
$shield = $('<div>').addClass(MAP_SHIELD_CLASS);
238+
if (disabled) {
239+
const $shield = $('<div>').addClass(MAP_SHIELD_CLASS);
226240
this.$element().append($shield);
227-
} else {
228-
$shield = this.$element().find(`.${MAP_SHIELD_CLASS}`);
229-
$shield.remove();
241+
242+
return;
230243
}
244+
245+
this.$element()
246+
.find(`.${MAP_SHIELD_CLASS}`)
247+
.remove();
231248
}
232249

233250
_clean(): void {
234251
this._cleanFocusState();
235252
if (this._provider) {
253+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
236254
this._provider.clean();
237255
}
238256
// @ts-expect-error ts-error
@@ -253,6 +271,7 @@ class Map extends Widget<MapProperties> {
253271
case 'disabled':
254272
this._renderShield();
255273
super._optionChanged(args);
274+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
256275
this._queueAsyncAction('updateDisabled');
257276
break;
258277
case 'width':
@@ -271,21 +290,27 @@ class Map extends Widget<MapProperties> {
271290
errors.log('W1001');
272291
break;
273292
case 'bounds':
293+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
274294
this._queueAsyncAction('updateBounds');
275295
break;
276296
case 'center':
297+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
277298
this._queueAsyncAction('updateCenter');
278299
break;
279300
case 'zoom':
301+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
280302
this._queueAsyncAction('updateZoom');
281303
break;
282304
case 'type':
305+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
283306
this._queueAsyncAction('updateMapType');
284307
break;
285308
case 'controls':
309+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
286310
this._queueAsyncAction('updateControls', this._rendered.markers, this._rendered.routes);
287311
break;
288312
case 'autoAdjust':
313+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
289314
this._queueAsyncAction('adjustViewport');
290315
break;
291316
case 'markers':
@@ -294,19 +319,20 @@ class Map extends Widget<MapProperties> {
294319

295320
const prevValue = this._rendered[name];
296321
this._saveRendered(name);
322+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
297323
this._queueAsyncAction(
298-
`update${titleize(name)}`,
324+
`${name === 'markers' ? 'updateMarkers' : 'updateRoutes'}`,
299325
changeBag ? changeBag.removed : prevValue,
300326
changeBag ? changeBag.added : this._rendered[name],
301327
).then((result) => {
302328
if (changeBag) {
303-
// @ts-expect-error ts-error
304329
changeBag.resolve(result);
305330
}
306331
});
307332
break;
308333
}
309334
case 'markerIconSrc':
335+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
310336
this._queueAsyncAction('updateMarkers', this._rendered.markers, this._rendered.markers);
311337
break;
312338
case 'providerConfig':
@@ -322,8 +348,7 @@ class Map extends Widget<MapProperties> {
322348
case 'onClick':
323349
break;
324350
default:
325-
// @ts-expect-error ts-error
326-
super._optionChanged.apply(this, arguments);
351+
super._optionChanged(args);
327352
}
328353
}
329354

@@ -334,12 +359,16 @@ class Map extends Widget<MapProperties> {
334359
}
335360

336361
_dimensionChanged(): void {
362+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
337363
this._queueAsyncAction('updateDimensions');
338364
}
339365

340-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
341-
_queueAsyncAction(name, markers?, routers?) {
342-
const options = [].slice.call(arguments).slice(1);
366+
_queueAsyncAction(
367+
name: string,
368+
markers?: MapProperties['markers'] | MapProperties['routes'],
369+
routes?: MapProperties['routes'] | MapProperties['markers'],
370+
): Promise<void> {
371+
const markerAndRoutes = [markers, routes].filter(Boolean);
343372
const isActionSuppressed = this._suppressAsyncAction;
344373

345374
this._lastAsyncAction = this._lastAsyncAction.then(() => {
@@ -350,10 +379,11 @@ class Map extends Widget<MapProperties> {
350379
return Promise.resolve();
351380
}
352381

353-
return this._provider[name].apply(this._provider, options).then((result) => {
354-
result = wrapToArray(result);
382+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
383+
return this._provider[name](...markerAndRoutes).then((result) => {
384+
const arrayResult = wrapToArray(result);
355385

356-
const mapRefreshed = result[0];
386+
const mapRefreshed = arrayResult[0];
357387
if (mapRefreshed && !this._disposed) {
358388
this._triggerReadyAction();
359389
}
@@ -363,7 +393,8 @@ class Map extends Widget<MapProperties> {
363393
}
364394
/// #ENDDEBUG
365395

366-
return result[1];
396+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
397+
return arrayResult[1];
367398
});
368399
});
369400

@@ -378,67 +409,97 @@ class Map extends Widget<MapProperties> {
378409
this._createActionByOption('onUpdated')();
379410
}
380411

381-
setOptionSilent(name, value): void {
412+
setOptionSilent(name: string, value: unknown): void {
382413
this._setOptionWithoutOptionChange(name, value);
383414
}
384415

385-
addMarker(marker) {
416+
addMarker(marker: MapProperties['markers']): Promise<unknown> {
386417
return this._addFunction('markers', marker);
387418
}
388419

389-
removeMarker(marker) {
420+
removeMarker(marker: MapProperties['markers'] | number): Promise<void> {
390421
return this._removeFunction('markers', marker);
391422
}
392423

393-
addRoute(route) {
424+
addRoute(route: MapProperties['routes']): Promise<unknown> {
394425
return this._addFunction('routes', route);
395426
}
396427

397-
removeRoute(route) {
428+
removeRoute(route: MapProperties['routes'] | number): Promise<void> {
398429
return this._removeFunction('routes', route);
399430
}
400431

401-
_addFunction(optionName, addingValue) {
402-
const optionValue = this.option(optionName);
432+
_addFunction(
433+
optionName: 'markers',
434+
addingValue: MapProperties['markers']
435+
): Promise<unknown>;
436+
_addFunction(
437+
optionName: 'routes',
438+
addingValue: MapProperties['routes']
439+
): Promise<unknown>;
440+
_addFunction(
441+
optionName: 'markers' | 'routes',
442+
addingValue: MapProperties['markers'] | MapProperties['routes'],
443+
): Promise<unknown> {
444+
const { [optionName]: optionValue = [] } = this.option();
403445
const addingValues = wrapToArray(addingValue);
404-
// @ts-expect-error ts-error
405-
optionValue.push.apply(optionValue, addingValues);
446+
optionValue.push(...addingValues);
406447

407448
return this._partialArrayOptionChange(optionName, optionValue, addingValues, []);
408449
}
409450

410-
_removeFunction(optionName, removingValue) {
411-
const optionValue = this.option(optionName);
451+
_removeFunction(
452+
optionName: 'markers',
453+
removingValue: MapProperties['markers'] | number,
454+
): Promise<void>;
455+
_removeFunction(
456+
optionName: 'routes',
457+
removingValue: MapProperties['routes' ] | number,
458+
): Promise<void>;
459+
_removeFunction(
460+
optionName: 'markers' | 'routes',
461+
removingValue: MapProperties['markers'] | MapProperties['routes' ] | number,
462+
): Promise<void> {
463+
const { [optionName]: optionValue = [] } = this.option();
412464
const removingValues = wrapToArray(removingValue);
413465

414-
each(removingValues, (removingIndex, removingValue) => {
415-
const index = isNumeric(removingValue)
416-
? removingValue
417-
// @ts-expect-error ts-error
418-
: optionValue?.indexOf(removingValue);
466+
removingValues.forEach((value, removingIndex) => {
467+
const index = isNumeric(value)
468+
? value
469+
: optionValue.indexOf(value);
419470

420471
if (index !== -1) {
421-
// @ts-expect-error ts-error
422472
const removing = optionValue.splice(index, 1)[0];
423473
removingValues.splice(removingIndex, 1, removing);
424474
} else {
425-
throw errors.log('E1021', titleize(optionName.substring(0, optionName.length - 1)), removingValue);
475+
throw errors.log('E1021', titleize(optionName.substring(0, optionName.length - 1)), value);
426476
}
427477
});
428478

429479
return this._partialArrayOptionChange(optionName, optionValue, [], removingValues);
430480
}
431481

432-
_partialArrayOptionChange(optionName, optionValue, addingValues, removingValues) {
482+
_partialArrayOptionChange(
483+
optionName: 'markers' | 'routes',
484+
optionValue: MapProperties['markers'] | MapProperties['routes'],
485+
addingValues: MapProperties['markers'] | MapProperties['routes'],
486+
removingValues: MapProperties['markers'] | MapProperties['routes'],
487+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
488+
): Promise<void> | Promise<any> {
489+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
433490
return fromPromise(new Promise((resolve) => {
434491
this._optionChangeBag = {
435492
resolve,
436493
added: addingValues,
437494
removed: removingValues,
438495
};
439496
this.option(optionName, optionValue);
440-
// @ts-expect-error
441-
}).then((result) => (result && result.length === 1 ? result[0] : result)), this);
497+
}).then((result) => {
498+
const resultArray = Array.isArray(result) ? result : [result];
499+
500+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
501+
return resultArray.length === 1 ? resultArray[0] : resultArray;
502+
}), this);
442503
}
443504
}
444505

0 commit comments

Comments
 (0)