feat: derive buildOptions return type from PROPS table via Omit<PropTypesFromTable<TProps>, TExclude[number]>
Use a const type parameter on the exclude array so literal tuples narrow correctly. The return type now reflects the actual properties and their kinds (number/string/boolean) instead of Record<string, unknown>. Four call sites needed local casts where PROPS declares str but Leaflet expects a narrower type (crossOrigin, offset) — these are design-level mismatches between HTML attribute types and Leaflet's option types.
This commit is contained in:
parent
348e865dae
commit
18db84e9a4
@ -1,4 +1,5 @@
|
||||
import { ImageOverlay, LatLngBounds, LatLngExpression } from 'leaflet';
|
||||
import type { ImageOverlayOptions } from 'leaflet';
|
||||
import { buildOptions, buildAttrMap, setLayerAttr, parseBoundsAttr } from '../core/utils.js';
|
||||
import { withProps } from '../core/with-props.js';
|
||||
|
||||
@ -27,7 +28,7 @@ export class LeafletImageOverlay extends withProps(HTMLElement, PROPS) {
|
||||
this.#obj = new ImageOverlay(
|
||||
url,
|
||||
parseBoundsAttr(this),
|
||||
buildOptions(this, PROPS, ['url', 'bounds']),
|
||||
buildOptions(this, PROPS, ['url', 'bounds']) as ImageOverlayOptions,
|
||||
);
|
||||
registerChildren(this, this.#obj);
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { SVGOverlay, LatLngBounds, LatLngExpression } from 'leaflet';
|
||||
import type { ImageOverlayOptions } from 'leaflet';
|
||||
import { buildOptions, buildAttrMap, setLayerAttr, parseBoundsAttr } from '../core/utils.js';
|
||||
import { withProps } from '../core/with-props.js';
|
||||
|
||||
@ -25,7 +26,7 @@ export class LeafletSVGOverlay extends withProps(HTMLElement, PROPS) {
|
||||
this.#obj = new SVGOverlay(
|
||||
svg ?? dummy!,
|
||||
parseBoundsAttr(this),
|
||||
buildOptions(this, PROPS, ['bounds']),
|
||||
buildOptions(this, PROPS, ['bounds']) as ImageOverlayOptions,
|
||||
);
|
||||
registerChildren(this, this.#obj);
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ export class LeafletTileLayerWMS extends withProps(HTMLElement, PROPS) {
|
||||
const url = this.getAttribute('url') || '';
|
||||
this.#obj = new TileLayer.WMS(
|
||||
url,
|
||||
buildOptions(this, PROPS, ['url']) as Record<string, string>,
|
||||
buildOptions(this, PROPS, ['url']),
|
||||
);
|
||||
registerChildren(this, this.#obj);
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Tooltip } from 'leaflet';
|
||||
import type { TooltipOptions } from 'leaflet';
|
||||
import { registerWithParent, buildOptions, numAttr } from '../core/utils.js';
|
||||
import { withProps } from '../core/with-props.js';
|
||||
|
||||
@ -23,7 +24,7 @@ export class LeafletTooltip extends withProps(HTMLElement, PROPS) {
|
||||
this.#obj = new Tooltip({
|
||||
...buildOptions(this, PROPS, ['lat', 'lng']),
|
||||
content: this.innerHTML,
|
||||
});
|
||||
} as unknown as TooltipOptions);
|
||||
if (this.hasAttribute('lat') && this.hasAttribute('lng')) {
|
||||
this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]);
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { VideoOverlay, LatLngBounds, LatLngExpression } from 'leaflet';
|
||||
import type { VideoOverlayOptions } from 'leaflet';
|
||||
import { buildOptions, buildAttrMap, setLayerAttr, parseBoundsAttr } from '../core/utils.js';
|
||||
import { withProps } from '../core/with-props.js';
|
||||
|
||||
@ -28,7 +29,7 @@ export class LeafletVideoOverlay extends withProps(HTMLElement, PROPS) {
|
||||
this.#obj = new VideoOverlay(
|
||||
url,
|
||||
parseBoundsAttr(this),
|
||||
buildOptions(this, PROPS, ['url', 'bounds']),
|
||||
buildOptions(this, PROPS, ['url', 'bounds']) as VideoOverlayOptions,
|
||||
);
|
||||
registerChildren(this, this.#obj);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { PropDef } from '../types/props.js';
|
||||
import type { PropDef, PropTypesFromTable } from '../types/props.js';
|
||||
import type { LatLngBoundsExpression } from 'leaflet';
|
||||
|
||||
// Converts kebab-case to camelCase (e.g. "fill-color" → "fillColor").
|
||||
@ -108,12 +108,20 @@ export function setLayerAttr(
|
||||
// constructors, like coordinates or URLs). Null attributes use the
|
||||
// default from PROPS if one exists (skipping empty-string defaults to
|
||||
// avoid Leaflet rejecting them).
|
||||
export function buildOptions(
|
||||
//
|
||||
// The return type is derived from the PROPS table: each prop name maps
|
||||
// to its kind's value type (number for `num`, boolean for `bool-on`/`bool-off`,
|
||||
// string for `str`). The `const` type parameter makes literal exclude arrays
|
||||
// narrow correctly so excluded keys are stripped from the return type.
|
||||
export function buildOptions<
|
||||
TProps extends Record<string, PropDef>,
|
||||
const TExclude extends readonly string[] = [],
|
||||
>(
|
||||
el: HTMLElement,
|
||||
props: Record<string, PropDef>,
|
||||
exclude: string[] = [],
|
||||
): Record<string, unknown> {
|
||||
const opts: Record<string, unknown> = {};
|
||||
props: TProps,
|
||||
exclude: TExclude = [] as unknown as TExclude,
|
||||
): Omit<PropTypesFromTable<TProps>, TExclude[number]> {
|
||||
const opts = {} as Record<string, unknown>;
|
||||
for (const [propName, spec] of Object.entries(props)) {
|
||||
if (exclude.includes(propName)) continue;
|
||||
const val = el.getAttribute(spec.attr);
|
||||
@ -126,7 +134,7 @@ export function buildOptions(
|
||||
else if (spec.kind === 'bool-off') opts[propName] = false;
|
||||
else opts[propName] = val;
|
||||
}
|
||||
return opts;
|
||||
return opts as Omit<PropTypesFromTable<TProps>, TExclude[number]>;
|
||||
}
|
||||
|
||||
// Dispatches a custom `leaflet-register` event upward through the DOM
|
||||
|
||||
Loading…
Reference in New Issue
Block a user