Improve Collection type-safety

This commit is contained in:
Maximilian Krög
2022-07-30 02:32:54 +02:00
parent 6e4e49644d
commit 75c17e55b1
9 changed files with 77 additions and 77 deletions

View File

@@ -18,11 +18,12 @@ const Property = {
* @classdesc * @classdesc
* Events emitted by {@link module:ol/Collection~Collection} instances are instances of this * Events emitted by {@link module:ol/Collection~Collection} instances are instances of this
* type. * type.
* @template T
*/ */
export class CollectionEvent extends Event { export class CollectionEvent extends Event {
/** /**
* @param {import("./CollectionEventType.js").default} type Type. * @param {import("./CollectionEventType.js").default} type Type.
* @param {*} element Element. * @param {T} element Element.
* @param {number} index The index of the added or removed element. * @param {number} index The index of the added or removed element.
*/ */
constructor(type, element, index) { constructor(type, element, index) {
@@ -30,7 +31,7 @@ export class CollectionEvent extends Event {
/** /**
* The element that is added to or removed from the collection. * The element that is added to or removed from the collection.
* @type {*} * @type {T}
* @api * @api
*/ */
this.element = element; this.element = element;
@@ -45,10 +46,11 @@ export class CollectionEvent extends Event {
} }
/*** /***
* @template T
* @template Return * @template Return
* @typedef {import("./Observable").OnSignature<import("./Observable").EventTypes, import("./events/Event.js").default, Return> & * @typedef {import("./Observable").OnSignature<import("./Observable").EventTypes, import("./events/Event.js").default, Return> &
* import("./Observable").OnSignature<import("./ObjectEventType").Types|'change:length', import("./Object").ObjectEvent, Return> & * import("./Observable").OnSignature<import("./ObjectEventType").Types|'change:length', import("./Object").ObjectEvent, Return> &
* import("./Observable").OnSignature<'add'|'remove', CollectionEvent, Return> & * import("./Observable").OnSignature<'add'|'remove', CollectionEvent<T>, Return> &
* import("./Observable").CombinedOnSignature<import("./Observable").EventTypes|import("./ObjectEventType").Types| * import("./Observable").CombinedOnSignature<import("./Observable").EventTypes|import("./ObjectEventType").Types|
* 'change:length'|'add'|'remove',Return>} CollectionOnSignature * 'change:length'|'add'|'remove',Return>} CollectionOnSignature
*/ */
@@ -81,17 +83,17 @@ class Collection extends BaseObject {
super(); super();
/*** /***
* @type {CollectionOnSignature<import("./events").EventsKey>} * @type {CollectionOnSignature<T, import("./events").EventsKey>}
*/ */
this.on; this.on;
/*** /***
* @type {CollectionOnSignature<import("./events").EventsKey>} * @type {CollectionOnSignature<T, import("./events").EventsKey>}
*/ */
this.once; this.once;
/*** /***
* @type {CollectionOnSignature<void>} * @type {CollectionOnSignature<T, void>}
*/ */
this.un; this.un;
@@ -264,7 +266,9 @@ class Collection extends BaseObject {
this.array_.splice(index, 1); this.array_.splice(index, 1);
this.updateLength_(); this.updateLength_();
this.dispatchEvent( this.dispatchEvent(
/** @type {CollectionEvent<T>} */ (
new CollectionEvent(CollectionEventType.REMOVE, prev, index) new CollectionEvent(CollectionEventType.REMOVE, prev, index)
)
); );
return prev; return prev;
} }
@@ -290,10 +294,14 @@ class Collection extends BaseObject {
const prev = this.array_[index]; const prev = this.array_[index];
this.array_[index] = elem; this.array_[index] = elem;
this.dispatchEvent( this.dispatchEvent(
/** @type {CollectionEvent<T>} */ (
new CollectionEvent(CollectionEventType.REMOVE, prev, index) new CollectionEvent(CollectionEventType.REMOVE, prev, index)
)
); );
this.dispatchEvent( this.dispatchEvent(
/** @type {CollectionEvent<T>} */ (
new CollectionEvent(CollectionEventType.ADD, elem, index) new CollectionEvent(CollectionEventType.ADD, elem, index)
)
); );
} }

View File

@@ -439,7 +439,7 @@ class PluggableMap extends BaseObject {
this.controls.addEventListener( this.controls.addEventListener(
CollectionEventType.ADD, CollectionEventType.ADD,
/** /**
* @param {import("./Collection.js").CollectionEvent} event CollectionEvent. * @param {import("./Collection.js").CollectionEvent<import("./control/Control.js").default>} event CollectionEvent
*/ */
function (event) { function (event) {
event.element.setMap(this); event.element.setMap(this);
@@ -449,7 +449,7 @@ class PluggableMap extends BaseObject {
this.controls.addEventListener( this.controls.addEventListener(
CollectionEventType.REMOVE, CollectionEventType.REMOVE,
/** /**
* @param {import("./Collection.js").CollectionEvent} event CollectionEvent. * @param {import("./Collection.js").CollectionEvent<import("./control/Control.js").default>} event CollectionEvent.
*/ */
function (event) { function (event) {
event.element.setMap(null); event.element.setMap(null);
@@ -459,7 +459,7 @@ class PluggableMap extends BaseObject {
this.interactions.addEventListener( this.interactions.addEventListener(
CollectionEventType.ADD, CollectionEventType.ADD,
/** /**
* @param {import("./Collection.js").CollectionEvent} event CollectionEvent. * @param {import("./Collection.js").CollectionEvent<import("./interaction/Interaction.js").default>} event CollectionEvent.
*/ */
function (event) { function (event) {
event.element.setMap(this); event.element.setMap(this);
@@ -469,7 +469,7 @@ class PluggableMap extends BaseObject {
this.interactions.addEventListener( this.interactions.addEventListener(
CollectionEventType.REMOVE, CollectionEventType.REMOVE,
/** /**
* @param {import("./Collection.js").CollectionEvent} event CollectionEvent. * @param {import("./Collection.js").CollectionEvent<import("./interaction/Interaction.js").default>} event CollectionEvent.
*/ */
function (event) { function (event) {
event.element.setMap(null); event.element.setMap(null);
@@ -479,25 +479,20 @@ class PluggableMap extends BaseObject {
this.overlays_.addEventListener( this.overlays_.addEventListener(
CollectionEventType.ADD, CollectionEventType.ADD,
/** /**
* @param {import("./Collection.js").CollectionEvent} event CollectionEvent. * @param {import("./Collection.js").CollectionEvent<import("./Overlay.js").default>} event CollectionEvent.
*/ */
function (event) { function (event) {
this.addOverlayInternal_( this.addOverlayInternal_(event.element);
/** @type {import("./Overlay.js").default} */ (event.element)
);
}.bind(this) }.bind(this)
); );
this.overlays_.addEventListener( this.overlays_.addEventListener(
CollectionEventType.REMOVE, CollectionEventType.REMOVE,
/** /**
* @param {import("./Collection.js").CollectionEvent} event CollectionEvent. * @param {import("./Collection.js").CollectionEvent<import("./Overlay.js").default>} event CollectionEvent.
*/ */
function (event) { function (event) {
const overlay = /** @type {import("./Overlay.js").default} */ ( const id = event.element.getId();
event.element
);
const id = overlay.getId();
if (id !== undefined) { if (id !== undefined) {
delete this.overlayIdIndex_[id.toString()]; delete this.overlayIdIndex_[id.toString()];
} }
@@ -1709,7 +1704,12 @@ function createOptionsInternal(options) {
options.layers && options.layers &&
typeof (/** @type {?} */ (options.layers).getLayers) === 'function' typeof (/** @type {?} */ (options.layers).getLayers) === 'function'
? /** @type {LayerGroup} */ (options.layers) ? /** @type {LayerGroup} */ (options.layers)
: new LayerGroup({layers: /** @type {Collection} */ (options.layers)}); : new LayerGroup({
layers:
/** @type {Collection<import("./layer/Base.js").default>|Array<import("./layer/Base.js").default>} */ (
options.layers
),
});
values[MapProperty.LAYERGROUP] = layerGroup; values[MapProperty.LAYERGROUP] = layerGroup;
values[MapProperty.TARGET] = options.target; values[MapProperty.TARGET] = options.target;
@@ -1717,6 +1717,7 @@ function createOptionsInternal(options) {
values[MapProperty.VIEW] = values[MapProperty.VIEW] =
options.view instanceof View ? options.view : new View(); options.view instanceof View ? options.view : new View();
/** @type {Collection<import("./control/Control.js").default>} */
let controls; let controls;
if (options.controls !== undefined) { if (options.controls !== undefined) {
if (Array.isArray(options.controls)) { if (Array.isArray(options.controls)) {
@@ -1726,10 +1727,11 @@ function createOptionsInternal(options) {
typeof (/** @type {?} */ (options.controls).getArray) === 'function', typeof (/** @type {?} */ (options.controls).getArray) === 'function',
47 47
); // Expected `controls` to be an array or an `import("./Collection.js").Collection` ); // Expected `controls` to be an array or an `import("./Collection.js").Collection`
controls = /** @type {Collection} */ (options.controls); controls = options.controls;
} }
} }
/** @type {Collection<import("./interaction/Interaction").default>} */
let interactions; let interactions;
if (options.interactions !== undefined) { if (options.interactions !== undefined) {
if (Array.isArray(options.interactions)) { if (Array.isArray(options.interactions)) {
@@ -1740,10 +1742,11 @@ function createOptionsInternal(options) {
'function', 'function',
48 48
); // Expected `interactions` to be an array or an `import("./Collection.js").Collection` ); // Expected `interactions` to be an array or an `import("./Collection.js").Collection`
interactions = /** @type {Collection} */ (options.interactions); interactions = options.interactions;
} }
} }
/** @type {Collection<import("./Overlay.js").default>} */
let overlays; let overlays;
if (options.overlays !== undefined) { if (options.overlays !== undefined) {
if (Array.isArray(options.overlays)) { if (Array.isArray(options.overlays)) {

View File

@@ -50,6 +50,7 @@ export {default as ZoomToExtent} from './control/ZoomToExtent.js';
export function defaults(opt_options) { export function defaults(opt_options) {
const options = opt_options ? opt_options : {}; const options = opt_options ? opt_options : {};
/** @type {Collection<import("./control/Control.js").default>} */
const controls = new Collection(); const controls = new Collection();
const zoomControl = options.zoom !== undefined ? options.zoom : true; const zoomControl = options.zoom !== undefined ? options.zoom : true;

View File

@@ -84,6 +84,7 @@ export {default as Translate} from './interaction/Translate.js';
export function defaults(opt_options) { export function defaults(opt_options) {
const options = opt_options ? opt_options : {}; const options = opt_options ? opt_options : {};
/** @type {Collection<import("./interaction/Interaction.js").default>} */
const interactions = new Collection(); const interactions = new Collection();
const kinetic = new Kinetic(-0.005, 0.05, 100); const kinetic = new Kinetic(-0.005, 0.05, 100);

View File

@@ -361,6 +361,7 @@ class Modify extends PointerInteraction {
*/ */
this.hitDetection_ = null; this.hitDetection_ = null;
/** @type {Collection<Feature>} */
let features; let features;
if (options.features) { if (options.features) {
features = options.features; features = options.features;
@@ -574,11 +575,11 @@ class Modify extends PointerInteraction {
} }
/** /**
* @param {import("../Collection.js").CollectionEvent} evt Event. * @param {import("../Collection.js").CollectionEvent<Feature>} evt Event.
* @private * @private
*/ */
handleFeatureAdd_(evt) { handleFeatureAdd_(evt) {
this.addFeature_(/** @type {Feature} */ (evt.element)); this.addFeature_(evt.element);
} }
/** /**
@@ -594,12 +595,11 @@ class Modify extends PointerInteraction {
} }
/** /**
* @param {import("../Collection.js").CollectionEvent} evt Event. * @param {import("../Collection.js").CollectionEvent<Feature>} evt Event.
* @private * @private
*/ */
handleFeatureRemove_(evt) { handleFeatureRemove_(evt) {
const feature = /** @type {Feature} */ (evt.element); this.removeFeature_(evt.element);
this.removeFeature_(feature);
} }
/** /**

View File

@@ -75,7 +75,7 @@ const SelectEventType = {
* @property {boolean} [multi=false] A boolean that determines if the default * @property {boolean} [multi=false] A boolean that determines if the default
* behaviour should select only single features or all (overlapping) features at * behaviour should select only single features or all (overlapping) features at
* the clicked map position. The default of `false` means single select. * the clicked map position. The default of `false` means single select.
* @property {import("../Collection.js").default<import("../Feature.js").default>} [features] * @property {Collection<Feature>} [features]
* Collection where the interaction will place selected features. Optional. If * Collection where the interaction will place selected features. Optional. If
* not set the interaction will create a collection. In any case the collection * not set the interaction will create a collection. In any case the collection
* used by the interaction is returned by * used by the interaction is returned by
@@ -245,7 +245,7 @@ class Select extends Interaction {
/** /**
* @private * @private
* @type {import("../Collection.js").default} * @type {Collection<Feature>}
*/ */
this.features_ = options.features || new Collection(); this.features_ = options.features || new Collection();
@@ -290,7 +290,7 @@ class Select extends Interaction {
/** /**
* Get the selected features. * Get the selected features.
* @return {import("../Collection.js").default<import("../Feature.js").default>} Features collection. * @return {Collection<Feature>} Features collection.
* @api * @api
*/ */
getFeatures() { getFeatures() {
@@ -367,7 +367,7 @@ class Select extends Interaction {
} }
/** /**
* @param {import("../Collection.js").CollectionEvent} evt Event. * @param {import("../Collection.js").CollectionEvent<Feature>} evt Event.
* @private * @private
*/ */
addFeature_(evt) { addFeature_(evt) {
@@ -396,13 +396,12 @@ class Select extends Interaction {
} }
/** /**
* @param {import("../Collection.js").CollectionEvent} evt Event. * @param {import("../Collection.js").CollectionEvent<Feature>} evt Event.
* @private * @private
*/ */
removeFeature_(evt) { removeFeature_(evt) {
const feature = evt.element;
if (this.style_) { if (this.style_) {
this.restorePreviousStyle_(feature); this.restorePreviousStyle_(evt.element);
} }
} }
@@ -414,7 +413,7 @@ class Select extends Interaction {
} }
/** /**
* @param {import("../Feature.js").default} feature Feature * @param {Feature} feature Feature
* @private * @private
*/ */
applySelectedStyle_(feature) { applySelectedStyle_(feature) {
@@ -426,7 +425,7 @@ class Select extends Interaction {
} }
/** /**
* @param {import("../Feature.js").default} feature Feature * @param {Feature} feature Feature
* @private * @private
*/ */
restorePreviousStyle_(feature) { restorePreviousStyle_(feature) {
@@ -450,7 +449,7 @@ class Select extends Interaction {
} }
/** /**
* @param {import("../Feature.js").default} feature Feature. * @param {Feature} feature Feature.
* @private * @private
*/ */
removeFeatureLayerAssociation_(feature) { removeFeatureLayerAssociation_(feature) {
@@ -476,12 +475,12 @@ class Select extends Interaction {
const features = this.getFeatures(); const features = this.getFeatures();
/** /**
* @type {Array<import("../Feature.js").default>} * @type {Array<Feature>}
*/ */
const deselected = []; const deselected = [];
/** /**
* @type {Array<import("../Feature.js").default>} * @type {Array<Feature>}
*/ */
const selected = []; const selected = [];

View File

@@ -45,7 +45,7 @@ import {listen, unlistenByKey} from '../events.js';
*/ */
/** /**
* @param {import("../source/Vector.js").VectorSourceEvent|import("../Collection.js").CollectionEvent} evt Event. * @param {import("../source/Vector.js").VectorSourceEvent|import("../Collection.js").CollectionEvent<import("../Feature.js").default>} evt Event.
* @return {import("../Feature.js").default} Feature. * @return {import("../Feature.js").default} Feature.
*/ */
function getFeatureFromEvent(evt) { function getFeatureFromEvent(evt) {
@@ -55,11 +55,13 @@ function getFeatureFromEvent(evt) {
return /** @type {import("../source/Vector.js").VectorSourceEvent} */ (evt) return /** @type {import("../source/Vector.js").VectorSourceEvent} */ (evt)
.feature; .feature;
} else if ( } else if (
/** @type {import("../Collection.js").CollectionEvent} */ (evt).element /** @type {import("../Collection.js").CollectionEvent<import("../Feature.js").default>} */ (
evt
).element
) { ) {
return /** @type {import("../Feature.js").default} */ ( return /** @type {import("../Collection.js").CollectionEvent<import("../Feature.js").default>} */ (
/** @type {import("../Collection.js").CollectionEvent} */ (evt).element evt
); ).element;
} }
} }
@@ -261,6 +263,7 @@ class Snap extends PointerInteraction {
* @private * @private
*/ */
getFeatures_() { getFeatures_() {
/** @type {import("../Collection.js").default<import("../Feature.js").default>|Array<import("../Feature.js").default>} */
let features; let features;
if (this.features_) { if (this.features_) {
features = this.features_; features = this.features_;
@@ -284,7 +287,7 @@ class Snap extends PointerInteraction {
} }
/** /**
* @param {import("../source/Vector.js").VectorSourceEvent|import("../Collection.js").CollectionEvent} evt Event. * @param {import("../source/Vector.js").VectorSourceEvent|import("../Collection.js").CollectionEvent<import("../Feature.js").default>} evt Event.
* @private * @private
*/ */
handleFeatureAdd_(evt) { handleFeatureAdd_(evt) {
@@ -293,7 +296,7 @@ class Snap extends PointerInteraction {
} }
/** /**
* @param {import("../source/Vector.js").VectorSourceEvent|import("../Collection.js").CollectionEvent} evt Event. * @param {import("../source/Vector.js").VectorSourceEvent|import("../Collection.js").CollectionEvent<import("../Feature.js").default>} evt Event.
* @private * @private
*/ */
handleFeatureRemove_(evt) { handleFeatureRemove_(evt) {

View File

@@ -66,7 +66,7 @@ export class GroupEvent extends Event {
* visible. * visible.
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will * @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
* be visible. * be visible.
* @property {Array<import("./Base.js").default>|import("../Collection.js").default<import("./Base.js").default>} [layers] Child layers. * @property {Array<import("./Base.js").default>|Collection<import("./Base.js").default>} [layers] Child layers.
* @property {Object<string, *>} [properties] Arbitrary observable properties. Can be accessed with `#get()` and `#set()`. * @property {Object<string, *>} [properties] Arbitrary observable properties. Can be accessed with `#get()` and `#set()`.
*/ */
@@ -214,26 +214,22 @@ class LayerGroup extends BaseLayer {
} }
/** /**
* @param {import("../Collection.js").CollectionEvent} collectionEvent CollectionEvent. * @param {import("../Collection.js").CollectionEvent<import("./Base.js").default>} collectionEvent CollectionEvent.
* @private * @private
*/ */
handleLayersAdd_(collectionEvent) { handleLayersAdd_(collectionEvent) {
const layer = /** @type {import("./Base.js").default} */ ( const layer = collectionEvent.element;
collectionEvent.element
);
this.registerLayerListeners_(layer); this.registerLayerListeners_(layer);
this.dispatchEvent(new GroupEvent('addlayer', layer)); this.dispatchEvent(new GroupEvent('addlayer', layer));
this.changed(); this.changed();
} }
/** /**
* @param {import("../Collection.js").CollectionEvent} collectionEvent CollectionEvent. * @param {import("../Collection.js").CollectionEvent<import("./Base.js").default>} collectionEvent CollectionEvent.
* @private * @private
*/ */
handleLayersRemove_(collectionEvent) { handleLayersRemove_(collectionEvent) {
const layer = /** @type {import("./Base.js").default} */ ( const layer = collectionEvent.element;
collectionEvent.element
);
const key = getUid(layer); const key = getUid(layer);
this.listenerKeys_[key].forEach(unlistenByKey); this.listenerKeys_[key].forEach(unlistenByKey);
delete this.listenerKeys_[key]; delete this.listenerKeys_[key];
@@ -244,13 +240,13 @@ class LayerGroup extends BaseLayer {
/** /**
* Returns the {@link module:ol/Collection~Collection collection} of {@link module:ol/layer/Layer~Layer layers} * Returns the {@link module:ol/Collection~Collection collection} of {@link module:ol/layer/Layer~Layer layers}
* in this group. * in this group.
* @return {!import("../Collection.js").default<import("./Base.js").default>} Collection of * @return {!Collection<import("./Base.js").default>} Collection of
* {@link module:ol/layer/Base~BaseLayer layers} that are part of this group. * {@link module:ol/layer/Base~BaseLayer layers} that are part of this group.
* @observable * @observable
* @api * @api
*/ */
getLayers() { getLayers() {
return /** @type {!import("../Collection.js").default<import("./Base.js").default>} */ ( return /** @type {!Collection<import("./Base.js").default>} */ (
this.get(Property.LAYERS) this.get(Property.LAYERS)
); );
} }
@@ -258,7 +254,7 @@ class LayerGroup extends BaseLayer {
/** /**
* Set the {@link module:ol/Collection~Collection collection} of {@link module:ol/layer/Layer~Layer layers} * Set the {@link module:ol/Collection~Collection collection} of {@link module:ol/layer/Layer~Layer layers}
* in this group. * in this group.
* @param {!import("../Collection.js").default<import("./Base.js").default>} layers Collection of * @param {!Collection<import("./Base.js").default>} layers Collection of
* {@link module:ol/layer/Base~BaseLayer layers} that are part of this group. * {@link module:ol/layer/Base~BaseLayer layers} that are part of this group.
* @observable * @observable
* @api * @api

View File

@@ -302,10 +302,7 @@ class VectorSource extends Source {
/** @type {Array<import("../Feature.js").default<Geometry>>} */ /** @type {Array<import("../Feature.js").default<Geometry>>} */
let features; let features;
if (Array.isArray(options.features)) { if (Array.isArray(options.features)) {
features = features = options.features;
/** @type {Array<import("../Feature.js").default<Geometry>>} */ (
options.features
);
} else if (options.features) { } else if (options.features) {
collection = options.features; collection = options.features;
features = collection.getArray(); features = collection.getArray();
@@ -501,16 +498,12 @@ class VectorSource extends Source {
collection.addEventListener( collection.addEventListener(
CollectionEventType.ADD, CollectionEventType.ADD,
/** /**
* @param {import("../Collection.js").CollectionEvent} evt The collection event * @param {import("../Collection.js").CollectionEvent<import("../Feature.js").default<Geometry>>} evt The collection event
*/ */
function (evt) { function (evt) {
if (!modifyingCollection) { if (!modifyingCollection) {
modifyingCollection = true; modifyingCollection = true;
this.addFeature( this.addFeature(evt.element);
/** @type {import("../Feature.js").default<Geometry>} */ (
evt.element
)
);
modifyingCollection = false; modifyingCollection = false;
} }
}.bind(this) }.bind(this)
@@ -518,16 +511,12 @@ class VectorSource extends Source {
collection.addEventListener( collection.addEventListener(
CollectionEventType.REMOVE, CollectionEventType.REMOVE,
/** /**
* @param {import("../Collection.js").CollectionEvent} evt The collection event * @param {import("../Collection.js").CollectionEvent<import("../Feature.js").default<Geometry>>} evt The collection event
*/ */
function (evt) { function (evt) {
if (!modifyingCollection) { if (!modifyingCollection) {
modifyingCollection = true; modifyingCollection = true;
this.removeFeature( this.removeFeature(evt.element);
/** @type {import("../Feature.js").default<Geometry>} */ (
evt.element
)
);
modifyingCollection = false; modifyingCollection = false;
} }
}.bind(this) }.bind(this)