-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
undefined
vs null
is inconsistent for nullable columns
#183
Comments
It would be nice to have the column field be optional when I'm inserting into the table, but maybe the query result types should be explicitly |
So, regarding the type of the column in the select row, you are right. Mammoth defaults to Regarding the insert in your later comment: when inserting you should be able to ignore the nullable columns e.g. |
I personally treat
or
I think this makes sense because I can see this being a bigger problem down the road if I'm working with a larger team and we're passing mammoth row objects around functions.
This code would be wrong and can't be type-checked, and the dev who writes the db code might not necessarily be the same dev who writes the I'm currently working around this by explicitly setting all Re: the insert part in my later comment: |
In your opinion, what should the type be of a nullable column's value in a row? |
[@echentw and I are working together on a project that uses Mammoth.] I think the most important thing is for the TS types to be correct. That way I'll get a compile error if I mess something up. Beyond that, I have a preference for using JS So for the type PersonSelect = {
name: string;
age: number | null;
};
const results: Array<PersonSelect> = await db.select(db.person.name, db.person.age).from(db.person);
type PersonInsert = {
name: string;
age?: number | null
}
const newValue: PersonInsert = { ... };
await db.insertInto(db.person).value(newValue); |
That's interesting. I've also seen codebases where null is avoided and undefined is used instead. There is even a specific case in eslint's eqeqeq rule to warn when doing strict equality on null values (so it matches for undefined as well). Also, some authors try to avoid null altogether, see sindresorhus/meta#7. Obviously, it all depends on what the query executor function will return. As this is user land I guess it makes sense to at least be able to configure whether you're using null or undefined or even both. |
Yeah, there are a few strategies to dealing with the
Configurability would be great! (A type parameter to |
To me, the problem isn't the non-configurability, but rather just that Mammoth is giving me a Oo, a possible solution is to change the type to |
BTW, here's an unsafe workaround to convert all undefined/optional fields to nullable fields: type OptionalToNullable<O> = {
[K in keyof O]-?: undefined extends O[K] ? NonNullable<O[K]> | null : O[K];
};
// Unsafe function, use carefully.
export function optionalToNullable<T>(x: T): OptionalToNullable<T> {
return x as OptionalToNullable<T>;
} Edit: Ended up expanding it a bit: type NonOptional<T> = T extends undefined ? never : T;
type OptionalToNullable<T> = {
[K in keyof T]-?: undefined extends T[K] ? NonOptional<T[K]> | null : T[K];
};
type NonNullable<T> = T extends null ? never : T;
type NullableToOptional<T> = {
[K in keyof T]: null extends T[K] ? NonNullable<T[K]> | undefined : T[K];
};
// Unsafe functions, use carefully. Mammoth thinks nullable columns are returned as optional
// fields, but they're actually nullable (https://github.com/Ff00ff/mammoth/issues/183).
// Use this to convert a record from optional fields to nullable fields.
export function optionalToNullable<T>(x: T): OptionalToNullable<T> {
return x as OptionalToNullable<T>;
}
export function nullableToOptional<T>(x: T): NullableToOptional<T> {
return x as NullableToOptional<T>;
} Still very tedious to actually use them on every query/response from the DB... |
@martijndeh: What is your opinion on using Tangent: I tried experimenting with changing it to |
Personally, I prefer never to use null and always use undefined. I make no semantic difference between undefined and null. This simplifies a lot of code in my opinion. But, because pg defaults to null, and the query executor is in user-land, it's more straightforward to accept null. In Mammoth I don't want to do anything with the resulting rows as it may negatively impact performance. So I think null should be the new default. Would be nice to be able to switch back to undefined but that's optional. Code wise I'm hoping most can be fixed by changing This will probably be a breaking change so version 2.0 is coming. Lastly, regarding the snapshots, yes so the snapshotting has difficulty with determining whether something is |
One thing that might become less confusing after switching from await db.update(db.t1).set({a: 'hello', b: undefined});
await db.update(db.t1).set({a: 'hello'}); I think the first statement sets (For |
I agree it's error prone. TypeScript 4.4 is addressing this through exact optional property types though. https://devblogs.microsoft.com/typescript/announcing-typescript-4-4-beta/#exact-optional-property-types. |
Yeah, that should help. However, JS/TS programmers are used to |
There seems to be a bug in mammoth where Mammoth typing is telling you that the type of a field in a row result is
undefined
, when in fact the value isnull
.Here's my minimal reproduction, let me know if I'm misunderstanding something or made a mistake!
So let's say I have a table defined:
And now I want to select some rows from this table:
My IDE is telling me the inferred type of
results
is{name: string, age: number | undefined}[]
. However, when Iconsole.log(results)
, I notice that I actually getnull
(rather thanundefined
) values for age.The text was updated successfully, but these errors were encountered: