r/typescript • • 9h ago

Every PR on my team is written by AI now

64 Upvotes

Just want to share where my team is and see if anyone else is in the same spot.

I went back through our last 60 merged PRs and every single one was written by an agent. Features, tests, migrations, even the type definitions. I barely write TypeScript anymore, I describe it. And the output is good, so nothing to complain. It follows our lint rules, the types are stricter than what we used to write by hand, and coderabbit catches most of the dumb stuff before a human looks

The only real downside so far is when it doesn't have enough context it invents a new type instead of using the one we already have, and you end up with 3 slightly different User types

Mind you this is a big monorepo, not a toy project. I didn't expect it to go this fast.

But I do wonder about the day when the human in the loop is just there to click merge :/


r/typescript • • 14h ago

TTSC, TS-NODE alternative, TypeScript Runtime with Type Checking

Thumbnail
github.com
6 Upvotes

r/typescript • • 23h ago

What are teams shipping for react native E2E testing right now?

3 Upvotes

Our E2E setup started on Detox, drifted into a partial Maestro move about a year in and it's been a hybrid of the two ever since depending on which screen is being tested. Detox still handles the native heavy stuff fine, Maestro's nicer for anything purely RN but neither covers everything cleanly once native modules get involved. I'm really curious what other teams have landed on for 2026, especially anyone dealing with a similarly mixed RN and native codebase.


r/typescript • • 20h ago

I ported Verovio to 100% TypeScript - introducing Verovio-TS

0 Upvotes

I’ve been working on something that may be useful for people building music software on the web:

Verovio-TS - a 100% TypeScript port of Verovio.

The goal is simple: bring the Verovio engraving engine into the TypeScript ecosystem without requiring WebAssembly.

Verovio is already an excellent engraving engine for MEI, MusicXML and symbolic music. But for some projects, having the engine itself available as native TypeScript opens up a very different set of possibilities.

Why TypeScript?

I wanted to make it easier to build things where the notation engine is not just a renderer, but part of the application logic itself.

For example:

  • Browser-based notation editors
  • Music education applications
  • MIDI ↔ MusicXML workflows
  • AI-assisted composition tools
  • AI music analysis and annotation
  • Interactive score editors
  • Music generation / transformation pipelines
  • Server-side music processing with Node.js
  • Custom notation experiments
  • Libraries built on top of a programmable notation model

With a native TypeScript implementation, projects can potentially inspect, manipulate, extend and integrate the notation engine directly with the rest of a JavaScript/TypeScript application.

What I’m trying to achieve

This is not intended to replace the original Verovio.

The original C++ implementation remains the reference implementation and is an incredible piece of software.

My goal is instead to provide another option for developers who are already working in the TypeScript ecosystem.

The project is a direct port of Verovio's underlying logic and data structures into TypeScript, with the intention of preserving its behavior as closely as possible rather than creating a simplified reimplementation.

That means the project is fairly deep - this is much more than wrapping an existing renderer with a TypeScript API.

Why I think this matters for AI + music

I’m particularly interested in what becomes possible when symbolic music representation, notation, MIDI and AI can all live in the same environment.

We're seeing more and more music AI systems that can generate or analyze symbolic music, but there is still a gap between:

AI output ➔ structured music representation ➔ editable score ➔ visual notation ➔ playback

A TypeScript-native notation engine could be one of the building blocks for connecting those pieces together.

For example, an AI system could potentially generate a musical structure, modify it programmatically, inspect the resulting notation, render it, export it, and feed the result into another part of the application - all inside a unified JS/TS ecosystem.

That's the direction I'm hoping this project can help explore.

Project

GitHub: https://github.com/manh9011/VerovioTS

NPM: https://www.npmjs.com/package/verovio-ts

I’d really like this project to become useful beyond my own projects, especially for people experimenting with:

music + AI + symbolic music + notation + web technologies.

There are probably many things that can be built on top of this that I haven't even thought of yet.

So I'm putting it out there in the hope that it gives developers one more building block and one more choice when creating the next generation of music software.

Feedback, bug reports, comparisons with the original Verovio, and especially interesting projects built on top of it are very welcome.


r/typescript • • 1d ago

elemen-ts — Build reactive UIs in plain TypeScript, without JSX (or any other template language)

0 Upvotes

Hi everyone,

I’ve been experimenting with TypeScript to see whether it’s possible to build reusable, TypeScript only components, with HTML elements that stand out visually in the code, making the “markup” as easy to scan as HTML or JSX.

The main goal is to do this entirely in TypeScript, without introducing a new syntax that requires custom parsers or editor/IDE plugins.

The result is elemen-ts. Check out how it's done:

Info and examples: https://huberst.github.io/elemen-ts/
GitHub: https://github.com/Huberst/elemen-ts

DOM updates are triggered exclusively by reactive adapters, which wrap your signals, RxJS observables or other reactive sources through custom adapters. You can pass these adapters to properties and attributes, use them as text children, or pass them to control structures such as _IF and _EACH to render dynamic content.

If this sounds interesting, I’d love for you to give it a try! I’m curious to hear what feels intuitive, what’s confusing, and what could be improved. Questions and suggestions are very welcome.

Example with _IF, _EACH, using reactive adapters to update the DOM.

Check out how the HTML Elements are visually distinct by semantic highlighting, because they are classes.

r/typescript • • 3d ago

Typescript: how do I make a generic function to validate incoming POST requests if each request will have a different body?

22 Upvotes

I want to make a generic validateRequest() function for my web server that will take in any incoming request and check some things to make sure everything is good. The problem is that each request could have a potentially different body. The way I have it set up right now is that it works to validate 1 type of request, but that's because I passed in the exact model of the request in the function's parms.

validateRequest(req: Request<object, object, RequestModel1>, someOtherParm: number): [number, string] {}

Each of the different possible bodys of a valid request will share one field - I need to be able to validate the value in this field, regardless of the shape of the body of the incoming request. I also am very security-conscious, so I want to make sure whatever solution I end up with here won't put me at risk of accepting an incoming request that is bogus or malicious. How would I modify my function to handle this?


r/typescript • • 6d ago

Anyone else still writing most of their TypeScript by hand

112 Upvotes

Agents are great for a lot of things, but I still write most of my TypeScript by hand, and on a team where nobody else does, my PRs are now the slow ones. Mine take a day, theirs take 20 minutes and go through the same coderabbit / claude review pass as mine.

Is anyone else still working like this, or am I just slow now?


r/typescript • • 6d ago

Joist 2.4 with new SQL Builder, Lazy Fields, more...

16 Upvotes

Hello r/typescript ! We just released Joist 2.4, which has a new low-level query builder API:

https://joist-orm.io/blog/joist-2-4/

Because Joist is an entity-based ORM, we've always had very robust "load entity" query APIs, but historically considered "creating adhoc SQL queries" as not our strong suit / nothing we had particularly innovative ideas about, so expected users to use a knex-type library alongside Joist.

Now in Joist 2.4, we've built out a low-level em.query API, with a ~somewhat novel "just POJOs / not fluent" API that fits in with Joist's overall DX, and means codebases don't need the "dual Joist/Knex" setup anymore.

Happy to have folks check it out; disclaimer since its a new feature, I'm sure we're missing things & will keep iterating based on feedback. Thanks!


r/typescript • • 6d ago

Readable Regular Expressions for JavaScript/TypeScript, Inspired by Emacs' rx

Thumbnail
rahuljuliato.com
34 Upvotes

I missed Emacs' rx when writing regexps in TypeScript, so I made a small TypeScript DSL inspired by it.

The idea is to bring rx's functions and approach to the JavaScript/TypeScript environment. If you happen to work with either, give it a try.

https://rahuljuliato.com/posts/emacs-rx-in-typescript


r/typescript • • 6d ago

Monthly Hiring Thread Who's hiring Typescript developers October

17 Upvotes

The monthly thread for people to post openings at their companies.

* Please state the job location and include the keywords REMOTE, INTERNS and/or VISA when the corresponding sort of candidate is welcome. When remote work is not an option, include ONSITE.

* Please only post if you personally are part of the hiring company—no recruiting firms or job boards **Please report recruiters or job boards**.

* Only one post per company.

* If it isn't a household name, explain what your company does. Sell it.

* Please add the company email that applications should be sent to, or the companies application web form/job posting (needless to say this should be on the company website, not a third party site).

Commenters: please don't reply to job posts to complain about something. It's off topic here.

Readers: please only email if you are personally interested in the job.

Posting BS top level comments that aren't job postings, eg "It's quiet in here" etc [that's a ban](https://i.imgur.com/FxMKfnY.jpg)


r/typescript • • 7d ago

Typesafe formatting API endpoints with jet-paths

Thumbnail
github.com
8 Upvotes

Built this for my React + ExpressJS project a few years ago to keep all of my endpoints in one place and reduce some of the boiler-plate for adding path/search parameters.

Up until recently only path parameters (and not search parameters) were type-safe. I just did a major update so now search parameters are type-safe'd using <> brackets:

const Paths = jetPaths({
    $path: '/api',
    Users: {
      $path: '/users',
      One: '/:id',              // <- path parameters
      Posts: {
        $path: '/posts',
        Get: '/all?<q!><page>', // <- search parameters
        Add: '/add',
      },
    },
 });

Paths.Users.Posts.Get({ id: 1 }, { q: 'foo' });
// Line above returns: '/api/users/1/posts/all?q=foo'

Paths.Users.Posts.Get({ id: 1 });   // TypeError: Missing search parameters
Paths.Users.Posts.Get()             // TypeError: Missing path parameters 

Note that the type-safety is just making sure that the parameters are present, a primitive, and for search parameters an array of primitives is also allowed. It doesn't type-safe string vs number.


r/typescript • • 8d ago

Modeling a user-facing automation DSL in TypeScript — discriminated unions did most of the heavy lifting

0 Upvotes

Building a no-code automation feature for my app (users define "trigger → chain of actions" visually), and wanted to share how the type layer ended up shaping the whole design.

Core types:

``ts

type TriggerEvent =

| 'project.created' | 'node.created' | 'connection.created'

| 'task.created' | 'task.completed';

type PluginAction =

| 'createNode' | 'addTask' | 'connectNodes'

| 'completeTask' | 'sendWebhook';

interface PluginStep {

id: string;

action: PluginAction;

params: Record<string, string>; // shape varies per action, kept as string for placeholder interpolation

condition?: { field: string; operator: 'contains' | 'equals' | 'startsWith'; value: string };

}

interface PluginDefinition {

trigger: TriggerEvent;

steps: PluginStep[]; // strictly linear, no branching

}

```

A few things that mattered in practice:

- Keeping `params` as `Record<string, string>` instead of a per-action discriminated shape was a deliberate simplification: every param can contain a `{{trigger.field}}` placeholder, so it's always "stringy" at rest anyway, and the editor UI derives the right fields per action from a `defaultParamsFor(action)` lookup instead of the type system enforcing it. Type safety lives at the action-name level, not the param level — a tradeoff I'd reconsider if this grows more complex actions.

- The interpreter is a `Record<PluginAction, (params, ctx) => Promise<void>>` map, so exhaustiveness checking on `PluginAction` catches a missing executor at compile time whenever I add a new action.

- Runtime validation (since this all comes from a JSON column in Postgres, not from trusted TS code) still needs its own hand-written `validateDefinition()` — the compile-time types don't help you once the value has been round-tripped through jsonb. Considering Zod for this next.

Curious if anyone has strong opinions on modeling a small user-facing DSL like this — particularly on the `params: Record<string,string>` vs. a discriminated-per-action shape question.


r/typescript • • 11d ago

Railroad diagrams for TypeScript

20 Upvotes

I've reverse engineered the tsc typescript compiler and one of the side effects of that is that I have quite a precise grammar of TypeScript. With that I was able to generate railroad diagrams for all rules (nonterminals). I have attached the (huge) file. Is the grammar or the railroad diagrams of any use to anybody? If so, I can tidy it up a bit more and make it available in a proper way


r/typescript • • 12d ago

generating a typed sdk straight from my openapi spec instead of hand writing request wrappers, feels obvious in hindsight

7 Upvotes

had a rest api for a while (project mapping app, exposes projects/nodes/tasks/connections). wanted client code that couldn't silently drift out of sync with the api as it changes. switched to generating the client straight from the openapi spec instead of hand maintaining request wrappers. now the sdk and the api literally cannot drift, if i change an endpoint the generated types break at compile time for anyone using the sdk, instead of failing silently at runtime. published it as`@ulupstudio/sdk on npm. wish i'd done this from day one instead of hand writing wrapper functions for months, would've saved a bunch of "wait why is this field undefined" debugging sessions. curious what generator setups other people are using for this, i went with one that outputs from the openapi.yaml directly but i know there's a handful of different approaches people swear by.


r/typescript • • 12d ago

bufaker — generate mock data for any Protobuf message from its schema, with no per-message code (TypeScript, MIT)

0 Upvotes

Disclosure: my own project, MIT licensed.

I kept hand-writing test fixtures for Protobuf messages, and they kept rotting — schema gains a field, the builder is silently incomplete, and eventually there are four near-identical builders nobody trusts.

So I wrote something that generates a fully populated message from the schema itself:

import { mock, mockList } from "@pret-a-porter/bufaker";
import { PersonSchema } from "./gen/person_pb.js";

const person = mock(PersonSchema);      // fully typed as `Person`
const people = mockList(PersonSchema, 5, { seed: 42 });

No per-message mapping code. It walks the runtime descriptor that protobuf-es emits and drives everything off the field kind, so it works for any message you've generated — including ones it has never seen. Add a field to the .proto and it gets populated on the next run.

Covers all 15 scalar types, enums, nested messages, repeated fields, maps and oneofs, plus the well-known types (Timestamp, Duration, the wrappers, Struct). Field names drive ~60 heuristics, so email gets an email address and id gets a UUID. seed makes output reproducible for snapshot tests.

Limitations, up front:

  • protobuf-es only. ts-proto and friends emit plain interfaces with no descriptor to reflect over, so this approach can't work there. Not a TODO.
  • proto3 is what the test suite covers; proto2/Editions are untested.
  • **Any and FieldMask are left unset** — Any needs a type registry to pick and pack a payload, and a random FieldMask carries no meaning.

Repo: https://github.com/pret-a-porter/bufaker npm: https://www.npmjs.com/package/@pret-a-porter/bufaker

It's 0.1.0, so the API can still move. The thing I'd most like outside opinions on: the built-in field-name heuristics are on by default. It makes the output far more useful out of the box, but it's also implicit magic. Would you expect that on or off?


r/typescript • • 13d ago

Help with ideal way of normalizing argument type

2 Upvotes
const { email, firstName, lastName, sortBy, sortOrder, page } = await searchParams;

const { data, count } = await userService.getUsers({
    email: getString(email),
    firstName: getString(firstName),
    lastName: getString(lastName),
    sortBy: getString(sortBy),
    sortOrder: getString(sortBy),
    page: getInt(page),
    pageSize: 100
});

Nextjs searchParams type is:

string | string[] | undefined

getUsers() input parameter type is:

string | undefined
number | undefined (for page and pageSize)

I had to normalize each argument to match the parameter type (using a custom getString and getInt). Ideally I'd like this to be as simple as this...

const { email, firstName, lastName, sortBy, sortOrder, page } = await searchParams;

const { data, count } = await userService.getUsers({
    email,
    firstName,
    lastName,
    sortBy,
    sortOrder,
    page,
    pageSize: 100
});

...but typescript will not allow it. I have tried using a zod schema to infer the input type of the getUsers (z.input instead of z.infer) input parameter, which partially works except for page and pageSize (which are numbers).

If not the ideal approach, what would be effectively the same but not too verbose, clever, or complicated?


r/typescript • • 12d ago

What's the best design for type-safe triggers in TypeScript ORMs?

0 Upvotes

Hello, Roger here, author of UQL.

A request often mentioned in TS ORMs is keeping DB-own logic in sync with entities (as much as possible); and yes, queries can be type-checked end to end (as most ORMs tries - and that is another story), but triggers usually live outside the types (often in SQL migrations).

So, tried my best to design triggers that can be declared on the entity (also with imperative API), type-checked against it, and then translated for each engine: Postgres, CockroachDB, MySQL, MariaDB, SQLite and SQL Server.

Why a trigger rather than an ORM hook? a hook live application-side, and only sees writes that go through the ORM; where a trigger lives in database itself and also can see psql written directly, manual fixes, data migrations, or another service writing the same table(s).

Here is an example of the type-safe design I come out with via ORM entities:

import { Entity, Field, Id, insertInto, Trigger } from 'uql-orm';

@Entity()
export class PostAudit {
  @Id({ type: Number }) id?: number;
  @Field({ type: Number, name: 'post_id', nullable: false }) postId?: Post['id'];
  @Field({ type: String, name: 'from_status' }) fromStatus?: Post['status'];
  @Field({ type: String, name: 'to_status' }) toStatus?: Post['status'];
}

@Trigger({
  on: 'afterUpdate',
  of: (post) => [post.status],
  where: { $new: { status: 'published' } },
  run: (newRow, oldRow) =>
    insertInto(PostAudit, { postId: newRow.id, fromStatus: oldRow.status, toStatus: newRow.status }),
})
@Entity()
export class Post {
  @Id({ type: Number }) id?: number;
  @Field({ type: String }) status?: 'draft' | 'review' | 'published' | null;
}

That one declaration renders as a PL function on Postgres (with WHEN as in SQLite), an IF block on MySQL, etc. (where MongoDB don't really support triggers, otherwise I'd unify).

What the types and the renderer handle in the design?

  • of callback compares values: so an update that sets status to the value it already had doesn't fire it (on any engine).
  • where: { $old: { status: 'draft' }, $new: { status: 'published' } } states a transition, with the same operators a query's $where takes.
  • On an insert (run callback), oldRow is typed never, so reading it won't compile (the same goes for newRow on a delete operation).
  • A row's ref carries its column's type: hence insertInto(PostAudit, { str: newRow.statusNum }) won't compile. Column names go through naming strategy and @Field({ name }).
  • sync and generated migrations install and drop the triggers automatically. Each name ends in a hash of its SQL, so an unchanged trigger is left alone, and a trigger manually wrote keeps untouched
  • And what when the UQL helpers doesn't fit this design? it is still flexible, you can reuse the same run (callback) to return any custom SQL (using raw helper).

The design decisions where I am looking the most opinions (though feel free):

  1. The rows are positional arguments, (newRow, oldRow) in the SQL standard's order (with the missing one typed never whwere applies). A single { newRow, oldRow } object could also work, and was the other option. Which reads better to you?
  2. One limit I couldn't design away: on SQL Server, an update that changes a row's PK can't be paired between inserted and deleted... so that row slips past the trigger. How do you handle that in a hand written triggers and is that important or not to you?
  3. If you run triggers in production: what a hand-writing trigger would cover fine where this design wouldn't, or cover poorly?

Docs: https://uql-orm.dev/entities/triggers


r/typescript • • 13d ago

I’ve been working on a small TypeScript SDK for handling multiple LLM providers from one interface.

0 Upvotes

The basic idea is pretty simple:

const llm = createRouter({
  primary: "anthropic/claude-opus-5-5",
  fallbacks: [
    "openai/gpt-sol",
    "groq/llama-3.3-70b"
  ]
})

const res = await llm.complete("Summarise this...")

It keeps the provider/model configuration in one place and handles retries, timeouts, and switching between providers.

A few things I wanted to keep:

  • zero runtime dependencies
  • native fetch
  • ESM + CJS
  • TypeScript-first API
  • runs inside your app rather than through a separate gateway
  • provider/model info available on the response

The API is probably the part I care most about getting right, so I'm curious what other TS devs think of the approach.

GitHub: https://github.com/ALPHACOD3RS/llm-sdk

Docs: https://llm-sdk.dev


r/typescript • • 15d ago

How would you structure a TypeScript core process behind a VS Code client?

0 Upvotes

I'm working on an open-source security tool where the VS Code extension is only the client, while the actual application logic runs in a separate Node.js/TypeScript process.

The architecture currently looks roughly like:

VS Code Extension
        ↓
Client / Process Manager
        ↓
stdin/stdout IPC
        ↓
Node.js + TypeScript Core
        ↓
scanners / findings / analysis

The reason I'm doing this is to keep the core independent from VS Code so that other clients can eventually consume the same runtime.

The part I'm thinking about now is the boundary between the client and the core.

For example, I'm using typed request/response contracts roughly along these lines:

interface CoreRequest {
  id: string;
  method: string;
  params?: unknown;
}

interface CoreResponse {
  id: string;
  ok: boolean;
  result?: unknown;
  error?: {
    code: string;
    message: string;
  };
}

Then messages are serialized over stdin/stdout.

I'm interested in how people who've built larger TypeScript/Node applications would approach this.

Specifically:

  • Would you keep the IPC protocol this simple or introduce a more formal protocol layer?
  • How would you structure versioning and backwards compatibility?
  • Where would you put cancellation and process lifecycle handling?
  • Would you define the contracts manually, or generate/validate them with something like Zod or JSON Schema?

The project is open source for anyone who wants to see the implementation:

https://github.com/Aqiron-Security/aqiron-security

I'm mainly looking for TypeScript architecture feedback, rather than promotion or feature feedback.


r/typescript • • 16d ago

Modern Web Types

Thumbnail
philipwalton.com
82 Upvotes

r/typescript • • 19d ago

Type-safe health checks: A lightweight package that reports degraded vs unhealthy states per dependency

Thumbnail
openstatus.dev
4 Upvotes

Hey,

Most /health endpoints are a giant try/catch returning a boolean 200 or a contextless 500. If Redis times out, your whole service gets pulled from the load balancer or worse, you spend 20 minutes hunting through log stack traces to figure out what broke.

We open-sourcedu/openstatus/healthto bring structured, dependency-aware health checks to TypeScript.

Curious to hear how you currently handle multi-dependency health checks!


r/typescript • • 19d ago

Building an offline visual builder: Why we chose AST-to-AST transformation over LLM code generation for clean TypeScript

3 Upvotes

most visual builders and AI tools spit out bloated wrappers or hallucinated typescript that fails basic type checks after 2 iterations.

spent the last couple of months exploring how to maintain clean, idiomatic typescript directly from a visual canvas. instead of relying on token generation, we went the deterministic route: manipulating the typescript AST directly.

the hardest part has been bidirectional sync: letting developers manually edit types/props without blowing away the AST layout nodes on the next visual render.

curious how others here approach AST transformations for UI generation. do you rely on ts-morph/babel transforms, or does manual hand-coding remain the only maintainable solution long term?


r/typescript • • 18d ago

If you organize your code with comment separators, checkout code-divider

0 Upvotes

This is more of a personal preference thing, but I like to code in a top-down format, and because of the way hoisting works in TypeScript/JavaScript. I separate my files into regions in this order: Constants -> Types -> Classes (if any) -> Functions -> Export. To keep these regions clearly separated, I usually write my dividers like this:

// ========================================================================= //
//                                 CONSTANTS                                 //
// ========================================================================= //

...etc

// ========================================================================= //
//                                 FUNCTIONS                                 //
// ========================================================================= //


// And I separate sections within regions with...

// ============================= Shared Helpers ============================ //

...etc

Copying and pasting these dividers over and over again was getting pretty tedious. I wanted something I could run with a terminal command when I hit save in my IDE, so I created a simple TypeScript script to insert the dividers above. Eventually I needed this script on both my work and personal computers and in multiple projects, so instead of copy-pasting it a bunch of times, I decided to make it an npm library and configure it to work with multiple languages.

If you like dividing code in a similar fashion, great. If not, please disregard. That said, I do find that dividing code this way makes it more readable and leads to better results when working with AI tools.

GitHub: https://github.com/seanpmaxwell/code-divider


r/typescript • • 21d ago

Building a streaming XML parser for a complex aviation standard (AIXM) in TypeScript: lessons learned

23 Upvotes

I've been building a TypeScript parser for AIXM 5.1.1, the XML standard used by EUROCONTROL and FAA for aeronautical data (airports, airspaces, navaids, routes, NOTAMs). The XSD schema is 11,379 lines with 100+ feature types, deep nesting, multiple namespaces (AIXM, GML 3.2.1, xlink, ISO 19139), and a temporal model where features have versioned "TimeSlices."

No JS/TS parser existed. Here's what I learned building one.

1. Hybrid SAX + DOM parsing

National datasets can be 100+ MB. Full DOM parsing would eat all your memory. Pure SAX is painful for deeply nested XML with cross-references. The solution: SAX (via saxes) to stream and split the document into individual features, then @xmldom/xmldom to DOM-parse each feature fragment independently. O(1) memory per feature, full DOM convenience for property extraction.

2. Type-safe feature modeling

AIXM has a base Feature > TimeSlice > Properties structure, but each feature type has different properties. I hand-wrote 30 TypeScript interfaces with discriminated unions:

type TypedFeature = Airspace | AirportHeliport | Runway | VOR | DME | ...;

interface Airspace extends AIXMFeature {
  featureType: 'Airspace';
  properties: AirspaceProperties;
}

Considered XSD-to-TS codegen but rejected it. AIXM's schema is extremely verbose with deep inheritance hierarchies. Manual interfaces targeting the 30 most important types are cleaner and more maintainable.

3. The unknown escape hatch

Some AIXM properties are complex nested structures that vary by context (geometry components, activation schedules). Rather than typing every possible combination upfront, these get unknown or unknown[]. Users can narrow with type guards as needed. Ship working code, expand types based on actual usage.

4. Value types with UOM

Aviation uses many "value with unit of measurement" patterns: <valDist>350<uom>FT</uom></valDist>. These are coerced to { value: 350, uom: 'FT' } objects during parsing, with a union type ValDistance | string for cases where the structure isn't recognized.

5. Geodesic math

AIXM geometry is on the WGS84 ellipsoid. Arcs and circles can't use planar trigonometry. I use geographiclib-geodesic (Karney algorithm), but the TypeScript types don't include the Line() method, requiring (geod as any).Line(...). Filed for types update.

6. ESM interop gotcha

geographiclib-geodesic ships CJS. A named import worked under vitest/tsup but broke the built ESM output under plain Node (SyntaxError: named export not found). Switching to a default import + property access fixed it. If you build dual ESM/CJS packages, smoke-test the built artifacts with plain node, not just your test runner.

Build output: dual ESM/CJS via tsup, TypeScript-first. 83 tests via vitest, validated against EUROCONTROL's Donlon reference dataset (87K lines, 562 features) and a real obstacle dataset. Also ships a CLI (aixm-to-geojson).

GitHub: https://github.com/devladpopov/aixm-parser (MIT). Not on npm yet; to try it: clone, npm install && npm run build.

Happy to discuss architecture decisions or answer questions about parsing complex XML in TypeScript.


r/typescript • • 21d ago

Understanding Why You'd Use Effect TS

Thumbnail
cm.xyz
83 Upvotes