Skip to Content
Tipos TypeScript

Tipos TypeScript

@mapvx/web-components-ssr incluye declaraciones de TypeScript para los datos que intercambian tu página y <mapvx-website>: el contenido del evento userAction, la estructura de initial-data y los modelos de datos del SDK de mapas a los que hacen referencia. Todo lo que puedes usar de ellas es un tipo, así que impórtalo con import type. Las declaraciones no describen el elemento en sí; Tipado del elemento muestra qué agregar a tu proyecto.

import type { InitialData, UserAction } from '@mapvx/web-components-ssr'

Importar los tipos

Las declaraciones vienen dentro del paquete de npm; no hay un paquete @types aparte. Instala la versión exacta que probaste (consulta npm):

npm install --save-exact @mapvx/web-components-ssr@1.6.1
  • Punto de entrada: el campo types del paquete apunta a dist/types/public-api.d.ts. Es un único archivo sin imports, así que TypeScript no necesita ningún otro paquete para resolverlo.
  • Ruta de importación: siempre la raíz del paquete, '@mapvx/web-components-ssr', con import type. Se resuelve con moduleResolution en bundler, node16 o nodenext, y también con node10, que TypeScript 6.0 marca como obsoleto (ahí exige "ignoreDeprecations": "6.0").
  • Biblioteca DOM: el archivo hace referencia a un tipo del DOM (GeolocationPosition). En un tsconfig.json sin la biblioteca DOM, como uno para código que solo corre en el servidor, TypeScript muestra el error Cannot find name 'GeolocationPosition' dentro del archivo. Agrega "DOM" a lib o activa skipLibCheck.
  • JavaScript sin TypeScript: para tener los tipos en un editor que revisa JavaScript, haz referencia a ellos desde JSDoc. Los imports de JSDoc son comentarios, así que nunca cargan el bundle.
/** @typedef {import('@mapvx/web-components-ssr').UserAction} UserAction */

Qué exporta el archivo:

GrupoExportacionesDetalles
Contenido de eventosUserAction, SelectFilterAction, ShowPlaceAction, SelectDestinationAction, SearchAction, ReturnToHomeActionUserAction
Datos inicialesInitialData, PlaceResponse, InstitutionResponseInitialData
Modelos del componenteAnimationState, CategoryFiltersData, CurrentLocation, FormattedSchedule, RoutePoint, SearchDataOtros modelos
Tipos de datos del SDK de mapasMVXPlace, Institution, Banner, MVXRoute, MVXRouteLeg, MVXRouteStep, MVXTransport, MVXLocation, CityFilterOption, OpeningHours, OpeningTime, DaysOfTheWeek, LatLng, InnerFloor, PropertyTipos del SDK de mapas
Valores declarados, no importablesEMPTY_SEARCH_DATA, FALLBACK_IMAGE_URL, MEASUREMENT_ID, extractBannerColor, getSchedule, isCurrentLocation, isDealOrEventBanner, isPlace, prepareAllSchedules, toCurrentLocationSolo tipos

Solo tipos

El punto de entrada en tiempo de ejecución del paquete (main) es dist/browser/main.js, el bundle del componente. Cargarlo registra <mapvx-website>; no es una biblioteca. Sus únicas exportaciones son nombres internos que importan sus propios chunks diferidos, así que ninguno de los nombres del archivo de declaraciones se puede importar en tiempo de ejecución.

Aun así, el archivo de declaraciones incluye algunos valores de tiempo de ejecución: las constantes y funciones de la última fila de la tabla anterior, el enum AnimationState y las clases del SDK (MVXPlace, Institution, Banner, MVXRoute, MVXRouteLeg, MVXRouteStep, MVXTransport). TypeScript acepta importar cualquiera de ellos como valor, pero la importación falla después:

  • Un bundler detiene la compilación porque main.js no tiene esa exportación. esbuild, por ejemplo, muestra No matching export in "node_modules/@mapvx/web-components-ssr/dist/browser/main.js" for import "isPlace".
  • Un navegador que carga el módulo de forma nativa lanza un SyntaxError porque el módulo no ofrece la exportación pedida.
  • Cualquier import de '@mapvx/web-components-ssr' que quede en el código compilado carga main.js, que registra el elemento. En código de servidor lanza ReferenceError: HTMLElement is not defined (consulta Solución de problemas).

Los nombres importados con import type solo se pueden usar como tipos. new MVXPlace(...), value instanceof MVXPlace y AnimationState.Playing no compilan (TS1361: ... cannot be used as a value because it was imported using 'import type'). Lo que te entrega el componente, como event.detail, son datos simples, así que no necesitas ningún constructor.

Escribe import type, no el modificador type en línea:

// Correcto: se elimina por completo del código compilado import type { UserAction } from '@mapvx/web-components-ssr' // Evítalo: con verbatimModuleSyntax se compila como // import {} from '@mapvx/web-components-ssr' import { type UserAction } from '@mapvx/web-components-ssr'

Con verbatimModuleSyntax activado, si todos los especificadores de un import llevan el modificador type en línea, TypeScript lo conserva como un import vacío, y un import vacío de todos modos carga el bundle.

Para cargar el componente, importa el paquete solo por su efecto secundario en código que corre únicamente en el navegador (import '@mapvx/web-components-ssr') o usa una etiqueta <script type="module">. Consulta Bundlers y CDN.

UserAction

El detail de cada evento userAction es un UserAction, una unión discriminada por type. Las declaraciones:

export interface FilterSelection { /** Primera categoría seleccionada, o 'All' si no hay ninguna. Se mantiene por compatibilidad. */ filter: string /** La selección completa. Vacía significa sin filtro de categoría. */ filters: string[] } export interface SelectFilterAction { type: 'select-filter' data: FilterSelection } export interface ShowPlaceAction { type: 'show-place' data: { placeId: string; alias: string | undefined } } export interface SelectDestinationAction { type: 'select-destination' data: { destinationId: string; alias: string | undefined } } export interface SearchAction { type: 'search' data: { searchTerm: string } } export interface ReturnToHomeAction { type: 'return-to-home' data: { filter: string } } export type UserAction = | SelectFilterAction | ShowPlaceAction | SelectDestinationAction | SearchAction | ReturnToHomeAction
typedataSe emite cuando
select-filter{ filter, filters }El visitante prende o apaga una categoría. filters es la selección completa.
show-place{ placeId, alias }Se abre el perfil de un lugar: clic en una tarjeta o en un marcador del mapa, un deep link tenant o Back desde la pantalla de ruta.
select-destination{ destinationId, alias }El visitante hace clic en Pedestrian-friendly directions en un perfil.
search{ searchTerm }Se ejecuta una búsqueda con un término nuevo.
return-to-home{ filter }El componente vuelve a su pantalla de inicio desde un perfil o desde la búsqueda. filter es el nombre de la categoría seleccionada, o un string vacío cuando no hay categorías.
  • Identificadores: placeId y destinationId son el id de MapVX del lugar, que es mapvxId en MVXPlace e id en PlaceResponse. alias es el clientId del lugar: su alias en los datos de MapVX, o su externalId cuando no tiene alias, y undefined cuando no tiene ninguno de los dos. La clave alias siempre está presente.
  • cardSelected: este evento heredado no tiene un tipo exportado. Su detail es el id de MapVX del lugar, así que usa el tipo CustomEvent<string> (consulta cardSelected).
  • Detalles: cuándo se dispara cada acción, y con qué frecuencia, se describe en Tipos de acción.

Un listener tipado, sin tipar el elemento:

import type { UserAction } from '@mapvx/web-components-ssr' const el = document.querySelector('mapvx-website') el?.addEventListener('userAction', (event) => { const action = (event as CustomEvent<UserAction>).detail switch (action.type) { case 'select-filter': case 'return-to-home': console.log('Categoría:', action.data.filter) break case 'show-place': console.log('Lugar:', action.data.placeId, action.data.alias) break case 'select-destination': console.log('Indicaciones a:', action.data.destinationId) break case 'search': console.log('Búsqueda:', action.data.searchTerm) break default: { // Error de compilación aquí si un tipo de acción nuevo no se maneja arriba const unhandled: never = action console.warn('Acción no manejada', unhandled) } } })

Cada case acota action.data a la estructura de ese miembro. La asignación a never convierte un miembro sin manejar en un error de compilación, por ejemplo después de una actualización que agregue uno. Agrega el listener al propio elemento con el nombre exacto userAction, porque el evento no se propaga hacia los elementos padre (consulta Escuchar eventos).

InitialData

La estructura del atributo initial-data:

export interface InitialData { availablePlaces: PlaceResponse[] | null institutions: InstitutionResponse[] | null }
  • Un string JSON: el atributo y la propiedad initialData reciben este objeto serializado como JSON. prepareInitialData de @mapvx/web-components-ssr-helpers ya se resuelve con ese string, así que pásalo sin cambios. Un objeto asignado a la propiedad lanza un TypeError dentro del componente.
  • Objetos sin procesar de la API: PlaceResponse e InstitutionResponse son los objetos que devuelve la API de MapVX, con el id en id. El componente los convierte en MVXPlace e Institution, donde el id es mapvxId.
  • Solo estas dos claves: las demás claves se ignoran, y una clave en null se omite. El helper deja una clave en null cuando su petición recibe un código de error HTTP.
  • Dónde está el tipo: el paquete del helper no exporta InitialData; impórtalo desde @mapvx/web-components-ssr.

Código de servidor que revisa los datos antes de renderizar:

import type { InitialData } from '@mapvx/web-components-ssr' import { prepareInitialData } from '@mapvx/web-components-ssr-helpers' const initialData = await prepareInitialData('YOUR_API_KEY') // string JSON const { availablePlaces, institutions } = JSON.parse(initialData) as InitialData console.log(`${availablePlaces?.length ?? 0} lugares, ${institutions?.length ?? 0} instituciones`) // Pasa initialData (el string) al atributo initial-data, no el objeto parseado

El manejo de errores y los tiempos de espera del helper se explican en prepareInitialData.

En 1.6.1, usar initial-data junto con api-key muestra cada lugar dos veces. Consulta el problema conocido.

Otros modelos

Las exportaciones restantes describen el estado interno del componente. Ninguna aparece en los atributos ni en los eventos del elemento.

TipoEstructuraDescribe
AnimationStateenum: NotStarted = 0, Playing = 1, Paused = 2, Finished = 3, Stepping = 4La animación de la ruta. Stepping, agregado en 1.4.0 como último miembro, hace que la ruta avance un paso por cada acción del visitante en lugar de reproducirse sola.
CategoryFiltersData{ name: string; placeData: string[] }Una categoría en oferta: su etiqueta y los ids de MapVX de sus lugares (vacío para All). También es el payload de categoriesChange.
FormattedSchedule{ day: string; schedule: string[] }Una fila del horario semanal. day puede ser un rango como Monday - Friday.
SearchData{ term: string; results: MVXPlace[] }Un término de búsqueda y sus resultados.
CurrentLocation{ type: 'current-location'; title: 'Current location'; position: LatLng }La posición del visitante, usada como punto de una ruta.
RoutePointMVXPlace o CurrentLocationEl origen o el destino de una ruta.

Estos valores declarados existen dentro del bundle, pero main.js no los exporta (consulta Solo tipos):

NombreQué es
EMPTY_SEARCH_DATAUn SearchData con el término vacío y sin resultados.
FALLBACK_IMAGE_URLLa imagen de reemplazo que muestran las tarjetas y los perfiles cuando un lugar no tiene fotos ni logo.
MEASUREMENT_IDEl ID de medición de la propiedad de Google Analytics de MapVX, a la que reporta el componente (consulta Google Analytics).
toCurrentLocation(position)Convierte un GeolocationPosition en un CurrentLocation.
isCurrentLocation(point)Type guard: el punto es un CurrentLocation.
isPlace(point)Type guard: el punto es un MVXPlace (tiene mapvxId).
isDealOrEventBanner(banner)Si un banner tiene la etiqueta deals o events.
extractBannerColor(description)El color declarado en el primer atributo style en línea de la descripción de un banner, o #EE5845.
getSchedule(openingHours)El horario de hoy como string HTML: el del día anterior mientras sigue abierto un intervalo nocturno, o un texto como Open on Monday cuando el lugar no abre hoy. Se calcula con el reloj y la zona horaria del visitante.
prepareAllSchedules(openingHours)El horario semanal como FormattedSchedule[], a partir del lunes. Los días consecutivos con el mismo horario comparten una fila (la última fila, que termina el domingo, se une a la primera cuando tienen el mismo horario), y los días sin horario muestran Closed. Los horarios son strings HTML.

Tipos del SDK de mapas

El componente está construido sobre @mapvx/web-js, el SDK de mapas de MapVX, que va incluido dentro de main.js (1.6.1 incluye la versión 3.1.0 del SDK). El paquete reexporta los tipos de datos del SDK, y sus declaraciones están copiadas en public-api.d.ts, así que no necesitas instalar @mapvx/web-js para usarlos.

TipoDeclaraciónDescripción
MVXPlaceclassUn lugar: mapvxId, clientId, title, position, category, localizedCategory, address, openingHours, images, logo, banners, pisos, parentId y parentInstitutionId.
InstitutionclassUn conjunto de lugares, como un centro comercial: mapvxId, title, country, city opcional, category, properties.
BannerclassUn banner de un lugar: url, tags, urlAction, sameLink y una description opcional que puede contener HTML.
MVXRouteclassUna ruta formada por legs, con los getters steps, duration (segundos) y length (metros).
MVXRouteLegclassEl tramo de una ruta entre dos puntos: distance, duration, ubicaciones de inicio y fin, steps.
MVXRouteStepclassUn paso de navegación: instrucciones, distancia, duración, ubicaciones, pisos y elevatorStep.
MVXTransportclassHorarios de transporte y recorridos en tiempo real.
MVXLocationinterfaceUn LatLng con floorId y placeId opcionales.
CityFilterOptionuniónUna entrada del filtro de ciudad: una opción de institución (type: 'institution') o una opción de lugar padre (type: 'parent-place', con parentPlaceId), cada una con filterId, name e institutionId.
OpeningHourstypeLos intervalos de apertura de cada día, indexados por DaysOfTheWeek.
OpeningTimeinterfaceUn intervalo: open y close como horas en formato de 24 horas, por ejemplo 09:00.
DaysOfTheWeektype'mo', 'tu', 'we', 'th', 'fr', 'sa' o 'su'.
LatLnginterface{ lat: number; lng: number }
InnerFloorinterfaceUn piso de un edificio: key, index, level, name, defaultFloor.
PropertyinterfaceUna propiedad de un lugar o una institución: title, subtitle y los opcionales icon, url y type. Las pestañas de categoría salen de las propiedades de la institución.
PlaceResponseinterfaceUn lugar tal como lo devuelve la API de MapVX (id, title, lat, lng y más). Se usa en InitialData.
InstitutionResponseinterfaceUna institución tal como la devuelve la API de MapVX. Se usa en InitialData.

MVXPlace, Institution, Banner y los tipos de rutas y transporte están declarados como clases. Úsalos solo como tipos: sus constructores y métodos estáticos no están disponibles para tu código (Solo tipos).

Tipado del elemento

El paquete no tipa el elemento <mapvx-website>: no tiene una entrada en HTMLElementTagNameMap, ni una entrada JSX en IntrinsicElements, ni un mapa de eventos. Sin ellos, document.querySelector('mapvx-website') queda tipado como un Element | null genérico, y TSX muestra el error Property 'mapvx-website' does not exist on type 'JSX.IntrinsicElements'. Agrega un archivo de declaraciones a tu proyecto. Las declaraciones de abajo se verificaron con TypeScript 5.9 y 6.0.

  • Sirve cualquier archivo .d.ts que cubra el include de tu tsconfig.json. No le pongas el mismo nombre que un archivo .ts o .tsx de la misma carpeta (por ejemplo, mapvx-website.d.ts junto a mapvx-website.tsx): en ese caso TypeScript ignora el .d.ts sin ningún aviso.
  • Las declaraciones del DOM y de React pueden ir en el mismo archivo.

DOM y eventos

Esta declaración tipa querySelector, createElement, las propiedades del elemento y sus dos eventos:

// types/mapvx-website.d.ts import type { UserAction } from '@mapvx/web-components-ssr' export interface MapvxWebsiteEventMap extends HTMLElementEventMap { userAction: CustomEvent<UserAction> cardSelected: CustomEvent<string> } export interface MapvxWebsiteElement extends HTMLElement { apiKey: string institutionId: string initialData: string // un string JSON, nunca un objeto showCategoryFilters: boolean showCityFilter: boolean defaultToMap: boolean inheritFontFamily: boolean hideDeals: boolean showDealLabels: boolean cardWidth: number | undefined rowsPerPage: number addEventListener<K extends keyof MapvxWebsiteEventMap>( type: K, listener: (this: MapvxWebsiteElement, event: MapvxWebsiteEventMap[K]) => void, options?: boolean | AddEventListenerOptions, ): void addEventListener( type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions, ): void removeEventListener<K extends keyof MapvxWebsiteEventMap>( type: K, listener: (this: MapvxWebsiteElement, event: MapvxWebsiteEventMap[K]) => void, options?: boolean | EventListenerOptions, ): void removeEventListener( type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions, ): void } declare global { interface HTMLElementTagNameMap { 'mapvx-website': MapvxWebsiteElement } }

Con ella, no hace falta ningún cast:

const el = document.querySelector('mapvx-website') // MapvxWebsiteElement | null el?.addEventListener('userAction', (event) => { // event: CustomEvent<UserAction> if (event.detail.type === 'search') console.log('Búsqueda:', event.detail.data.searchTerm) }) el?.addEventListener('cardSelected', (event) => console.log('Tarjeta:', event.detail)) // string const created = document.createElement('mapvx-website') created.rowsPerPage = 3
  • Los tipos de las propiedades son los valores que usa el componente después de interpretar sus entradas. Cómo acepta y devuelve valores cada propiedad se describe en Propiedades JavaScript.
  • Un nombre de evento mal escrito, como 'useraction', no coincide con ninguna entrada del mapa de eventos. El listener recibe entonces un Event genérico, así que leer event.detail se marca como error.

Función auxiliar para escuchar

Cuando la referencia al elemento no está tipada, por ejemplo una ref de un framework tipada como HTMLElement, esta función te da un UserAction tipado y devuelve una función que quita el listener:

// on-user-action.ts import type { UserAction } from '@mapvx/web-components-ssr' export function onUserAction(element: Element, handler: (action: UserAction) => void): () => void { const listener = (event: Event) => handler((event as CustomEvent<UserAction>).detail) element.addEventListener('userAction', listener) return () => element.removeEventListener('userAction', listener) }
import { onUserAction } from './on-user-action' const el = document.querySelector('mapvx-website') if (el) { const stop = onUserAction(el, (action) => { if (action.type === 'show-place') console.log('Lugar:', action.data.placeId) }) // Llama a stop() para quitar el listener, por ejemplo cuando se desmonta tu vista }

React

TSX necesita una declaración JSX para la etiqueta. Ambas versiones usan la forma declare module 'react'; el archivo debe tener un import de nivel superior, como estos, para que TypeScript lo trate como una ampliación del módulo react.

// types/mapvx-website-react.d.ts import type { DetailedHTMLProps, HTMLAttributes } from 'react' import type { UserAction } from '@mapvx/web-components-ssr' // React 19 renderiza {true} como "" y omite {false}: acepta solo strings type BooleanAttribute = 'true' | 'false' interface MapvxWebsiteProps extends DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> { 'api-key'?: string 'institution-id'?: string 'initial-data'?: string 'show-category-filters'?: BooleanAttribute 'show-city-filter'?: BooleanAttribute 'show-view-toggle'?: BooleanAttribute 'show-search-bar'?: BooleanAttribute 'show-drop-pin'?: BooleanAttribute 'selected-categories'?: string 'search-term'?: string 'full-width-map'?: BooleanAttribute 'hide-unselected-markers'?: BooleanAttribute 'default-to-map'?: BooleanAttribute 'inherit-font-family'?: BooleanAttribute 'hide-deals'?: BooleanAttribute 'show-deal-labels'?: BooleanAttribute 'card-width'?: number | string 'rows-per-page'?: number | string 'zoom'?: number | string 'center'?: string // React 19 escucha el evento cuyo nombre sigue a "on", respetando mayúsculas y minúsculas onuserAction?: (event: CustomEvent<UserAction>) => void oncardSelected?: (event: CustomEvent<string>) => void } declare module 'react' { namespace JSX { interface IntrinsicElements { 'mapvx-website': MapvxWebsiteProps } } }
import type { UserAction } from '@mapvx/web-components-ssr' export function Directory({ initialData }: { initialData?: string }) { const handleAction = (event: CustomEvent<UserAction>) => { const action = event.detail if (action.type === 'show-place') console.log('Lugar:', action.data.placeId) } return ( <mapvx-website api-key="YOUR_API_KEY" institution-id="YOUR_INSTITUTION_ID" initial-data={initialData} default-to-map="true" show-city-filter="false" rows-per-page={3} onuserAction={handleAction} oncardSelected={(event) => console.log('Tarjeta:', event.detail)} /> ) }
  • React 19 renderiza {true} como un atributo vacío, que el componente lee como false, y omite {false}, así que en el primer render se aplica el valor por defecto (true para show-category-filters y show-city-filter). Tipar los booleanos como 'true' | 'false' convierte ambos errores en errores de compilación.
  • Las props de evento son onuserAction y oncardSelected: on seguido del nombre del evento con sus mayúsculas y minúsculas exactas. onUserAction nunca se dispara, y esta declaración lo rechaza.
  • @types/react 19 no tiene un namespace JSX global, así que un bloque declare global { namespace JSX { ... } } no tiene ningún efecto ahí.
  • Esta forma se verificó con @types/react 19.

Carga el bundle solo en el navegador; consulta React y Next.js.

La declaración JSX se aplica al TSX de React. Con otros frameworks, la declaración del DOM y la función auxiliar tipan el elemento en tu código de script. Cómo cargar el bundle y configurar las plantillas se explica en Vue, Angular y Otros frameworks.

Última actualización: