exodus
exodus
Explore posts from servers
TTCTheo's Typesafe Cult
Created by exodus on 2/8/2024 in #questions
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
2 replies
TTCTheo's Typesafe Cult
Created by exodus on 2/4/2023 in #questions
Astro as a multi-app host (i.e. microfrontends)
This might be a silly question...but would Astro be a ridiculous thing to use to host multiple apps? I.e. think Google Docs. Could just use Astro for the header navigation and each "app" could just be hosted on its own page. Astro would take care of the auth, and provide a unified SPA like look and feel while hosting multiple apps. The apps would just be a single component on each astro page. I'm trying to think if there are any downsides here. Would simplify deployment, and it would allow apps to be written with different frameworks, all while keeping a common look and feel.
6 replies
TTCTheo's Typesafe Cult
Created by exodus on 1/13/2023 in #questions
Typesafe backend stack
Having a hard time deciding on a backend stack. Happy with where things are going, but nothing’s quite there yet. Looking for decent scalability and type safety. I’ve done NestJS, Typegoose, and openapi generator. Works reasonably well, single source of truth (ish, because you’re still writing DTOs). Ditching Mongo though so I’m looking at TypeORM or prisma now. Not sure if I’ll keep using NestJS. It is a godsend for organization and scalability, and the batteries included nature means it’s reasonably easy to integrate any semi popular tech, but it is also a bit inflexible as a result. Not a huge OO fan, but the Spring bootiness of it does scale nicely. I’ve never properly scaled a purely Express backend and there really isn’t a well documented pattern to follow here. I can manage it, but it’s additional mental overhead. tRPC is really cool and would give the best DX, but we do also need OpenAPI for external consumers, and although there’s an unofficial plug-in for it, I’m a bit hesitant. Lots of decisions to make here, and I’m curious if anyone has any input when it comes to scalability across multiple apps and micro services. Main decisions to make: Nest vs. pure Express tRPC vs monorepo shared types vs codegen
43 replies
TTCTheo's Typesafe Cult
Created by exodus on 1/8/2023 in #questions
NextJS for SPA
Does anyone have any thoughts on using NextJS for a pure SPA? Assuming this is app will be delivered on a private network, therefore SSR and SEO are of no concern. This would be delivered as a pure SPA or at most a Node server with a very limited number of routes (perhaps Auth if NextAuth is a big boon to authenticating with something like LDAP). Is Next adding any benefit here, or is it just adding complexity for the sake of it?
3 replies
TTCTheo's Typesafe Cult
Created by exodus on 12/14/2022 in #questions
Animating a Hero title
18 replies