Inference heavy mapping function (Typescript challenge!)
Struggling a little bit with getting proper inference working here. This seems to work well enough, but I need to be explicit about the type of my objects going into these functions. Passing in the raw object literal breaks TS. Any advice on making this a bit smarter?
import { lat, lon } from '~/types/coordinates' // branded types
import { EntityState } from '~/modules/simulation/entity-state.type'
export interface GeoJsonFeature<T extends WithLatLon<object>> {
type: 'Feature'
geometry: {
type: 'Point' | 'LineString' | 'Polygon' | 'MultiPoint' | 'MultiLineString' | 'MultiPolygon'
coordinates: [lon, lat]
}
properties: Omit<T, 'latitude' | 'longitude'>
}
type WithLatLon<T extends object> = {
latitude: number
longitude: number
} & T
/**
* Convert an object that has latitude and longitude properties to a GeoJson with its other properties spread
* to the properties object
* @param data
*/
function toGeoJsonFeature<T extends object>(data: WithLatLon<T>): GeoJsonFeature<T> {
const { latitude, longitude, ...propsWithoutLatLon } = data
return {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [longitude as lon, latitude as lat]
},
properties: propsWithoutLatLon
}
}
/**
* Extract a GeoJson's coordinates and properties into a flattened object
* @param feature
*/
function fromGeoJsonFeature<T extends WithLatLon<object>>(feature: GeoJsonFeature<T>): T {
const { geometry, properties } = feature
const [longitude, latitude] = geometry.coordinates
return {
...properties,
latitude: latitude as number,
longitude: longitude as number
} as T
}
const sampleEntityState: EntityState = {
sourceType: 'exampleSourceType',
id: '123',
name: 'Sample Entity',
latitude: 40.7128,
longitude: -74.006,
altitude: 30,
azimuth: 180,
speed: 60,
time: '2024-02-07T12:00:00Z',
symbol: 'sampleSymbol'
}
const sampleEntityStateGeoJson: GeoJsonFeature<EntityState> = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [123 as lon, 345 as lat]
},
properties: {
sourceType: 'exampleSourceType',
id: '123',
name: 'Sample Entity',
altitude: 30,
azimuth: 180,
speed: 60,
time: '2024-02-07T12:00:00Z',
symbol: 'sampleSymbol'
}
}
const geojson = toGeoJsonFeature(sampleEntityState) // Type: GeoJsonFeature<EntityState>
const entity = fromGeoJsonFeature(sampleEntityStateGeoJson) // Type: EntityState
import { lat, lon } from '~/types/coordinates' // branded types
import { EntityState } from '~/modules/simulation/entity-state.type'
export interface GeoJsonFeature<T extends WithLatLon<object>> {
type: 'Feature'
geometry: {
type: 'Point' | 'LineString' | 'Polygon' | 'MultiPoint' | 'MultiLineString' | 'MultiPolygon'
coordinates: [lon, lat]
}
properties: Omit<T, 'latitude' | 'longitude'>
}
type WithLatLon<T extends object> = {
latitude: number
longitude: number
} & T
/**
* Convert an object that has latitude and longitude properties to a GeoJson with its other properties spread
* to the properties object
* @param data
*/
function toGeoJsonFeature<T extends object>(data: WithLatLon<T>): GeoJsonFeature<T> {
const { latitude, longitude, ...propsWithoutLatLon } = data
return {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [longitude as lon, latitude as lat]
},
properties: propsWithoutLatLon
}
}
/**
* Extract a GeoJson's coordinates and properties into a flattened object
* @param feature
*/
function fromGeoJsonFeature<T extends WithLatLon<object>>(feature: GeoJsonFeature<T>): T {
const { geometry, properties } = feature
const [longitude, latitude] = geometry.coordinates
return {
...properties,
latitude: latitude as number,
longitude: longitude as number
} as T
}
const sampleEntityState: EntityState = {
sourceType: 'exampleSourceType',
id: '123',
name: 'Sample Entity',
latitude: 40.7128,
longitude: -74.006,
altitude: 30,
azimuth: 180,
speed: 60,
time: '2024-02-07T12:00:00Z',
symbol: 'sampleSymbol'
}
const sampleEntityStateGeoJson: GeoJsonFeature<EntityState> = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [123 as lon, 345 as lat]
},
properties: {
sourceType: 'exampleSourceType',
id: '123',
name: 'Sample Entity',
altitude: 30,
azimuth: 180,
speed: 60,
time: '2024-02-07T12:00:00Z',
symbol: 'sampleSymbol'
}
}
const geojson = toGeoJsonFeature(sampleEntityState) // Type: GeoJsonFeature<EntityState>
const entity = fromGeoJsonFeature(sampleEntityStateGeoJson) // Type: EntityState
0 Replies