While working on @general-dexterity/cube-records
, I needed a way to define[1] my domain-specific Cube models in a way that would provide type-safe autocompletion. The solution was elegantly simple: global interface augmentation.
The library defines an empty global interface:
export interface CubeRecordMap {
__empty: {
measures: {};
dimensions: {};
joins: [];
};
}
That __empty
property isn't arbitrary - it's critical. Without it, TypeScript and build tools like tsup
would optimize the empty interface into a type alias, which can't be augmented:
// What we write
export interface CubeRecordMap {}
// What tsup might output
export type CubeRecordMap = {}
// Now augmentation fails
declare global {
interface CubeRecordMap { // Error: can't augment a type alias
users: { /* ... */ }
}
}
The __empty
placeholder prevents this optimization, ensuring the interface remains augmentable through the entire build pipeline.
I can now extend it in my project, and have a codegen tool generate the full interface based on my Cube models and views:
declare global {
interface CubeRecordMap {
users: {
measures: { count: { type: number } };
dimensions: {
id: { type: string };
email: { type: string };
};
joins: readonly ['orders'];
};
// ... other models and views
}
}
Now the library can extract type-safe cube names and fields:
type CubeRecordName = keyof CubeRecordMap;
type CubeRecordMeasure<T extends CubeRecordName> =
keyof CubeRecordMap[T]['measures'] & string;
What makes this pattern powerful is that it provides domain-specific autocompletion with zero runtime overhead. The type information exists only at compile time. I can add cube definitions incrementally, and my IDE immediately knows about every field in my data model.
This isn't just for fun[2], it simplifies how users of that library (probably mainly me to be honest) make requests to a Cube instance. Instead of searching through Cube models or views for field names, you get instant feedback as you type and you let TypeScript and your editor help you out.
Footnotes
Or use codegen to do so with another library. ↩︎
Even though it was fun to try and figure this out. ↩︎