pgtyped
pgtyped copied to clipboard
Feature Request: Support branded types and other type conversions
We use a lot of branded types in our typescript code, which helps us catch more bugs at compile time:
type Brand<K, T> = K & { __brand: T };
export type CustomerId = Brand<number, 'CustomerId'>;
export type Email = Brand<string, 'Email'>;
Of course, postgres doesn't support anything like this, so these are just stored as integers and strings in the database. So, it would be amazing if I could map certain fields in the code that pgtyped generates to the branded type that I really want.
Also, I have a few cases where I'm pulling bigints out of postgres but I can guarantee that they are valid integers in Typescript. It would be nice to be able to specify a function that is called to convert each value from the database to a new value that has the right type (and the conversion could include an assertion or other sanity check).
I'm not sure if you could parse these out 100% of the time before generating the real query, but one approach might be to specify some kind of mapping function in the SQL, since the @params are harder to keep in sync.
select
customer_id as customerId via toCustomerId,
email via toEmail
from customers
toCustomerId
and toEmail
would both be functions that are exported from a file that the pgtyped config knows about. For branded types, they would be very simple, but for things like string -> number conversion they would do a little more work
export function toEmail(s: string): Email {
return s as Email;
}
export function toSafeInteger(s: string): Number {
const val = Number.parseInt(s);
if (!Number.isSafeInteger(val)) throw ...
return val;
}
Anyway, just some suggestions. I love the library, so thanks for your work on it!
Unfortunately given how pgTyped works, extending SQL like that won't be possible because we rely on Postgres for parsing and analyzing the queries.
One way to achieve this might be to use some TS type magic by building a generic type that iterates through the returned result type interface and maps certain key-value combinations to branded values.
Ah, I thought you might be doing some SQL parsing before feeding it into Postgres.
Doing with an @param to suggest the type would be pretty good too.
Agree, I have been thinking about providing a way to override the inferred types, maybe with something like an @override
flag.
Not sure yet about how dynamic will such an override mapping be, but it definitely be useful either way.
@twk you can transform pgtyped types to something another with tools like https://github.com/millsp/ts-toolbelt
For now branded types are a non-priority due to poor TS and Postgres support.