Skip to content

orm: support | undefined in addition to ? on optional fields #243

@terrablue

Description

@terrablue

Currently this example shows a compile error:

import User from "#store/User";
import route from "primate/route";

route.get(async () => {
    const { id: _id, ...donald } = await User.insert({
        age: 30,
        name: "Donald",
        lastname: undefined, // <- TS error; removing this line expresses the same idea, but doesn't result in error
    });

    return donald;
});

Diagnostics:

1. Argument of type '{ age: number; name: string; lastname: undefined; }' is not assignable to parameter of type 'Insertable<{ id: PrimaryType; name: StringType; age: UintType<"u8">; lastname: OptionalType<StringType>; }>' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
     Type '{ age: number; name: string; lastname: undefined; }' is not assignable to type 'Omit<{ id?: string; lastname?: string; name: string; age: number; }, "id">' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
       Types of property 'lastname' are incompatible.
         Type 'undefined' is not assignable to type 'string'. [2379]

User is defined as:

import p from "pema";
import store from "primate/store";

export default store({
  id: p.primary,
  name: p.string,
  age: p.u8.range(0, 120),
  lastname: p.string.optional(),
});

The insertable record is defined as:

 Promise<{
    name: string;
    age: number;
    lastname?: string;
    id: string;
}>

This should become:

 Promise<{
    name: string;
    age: number;
    lastname?: string | undefined;
    id: string;
}>

Which is a bit more verbose, but coalesces JS' unwieldy differentiation between undefined vs not defined.

Idea: change @rcompat/type's UndefinedToOptional from:

import type Unpack from "#Unpack";
type Undef = undefined;
type UndefinedToOptional<T> = Unpack<{
  [K in keyof T as Undef extends T[K] ? K : never]?: Exclude<T[K], Undef>;
} & {
  [K in keyof T as Undef extends T[K] ? never : K]: T[K];
}>;
export type { UndefinedToOptional as default };

to

import type Unpack from "#Unpack";
type Undef = undefined;
type UndefinedToOptional<T> = Unpack<{
  [K in keyof T as Undef extends T[K] ? K : never]?: Exclude<T[K], Undef> | undefined; // support explicit `undefined`
} & {
  [K in keyof T as Undef extends T[K] ? never : K]: T[K];
}>;
export type { UndefinedToOptional as default };

Possibly this type would be then no longer appropriate or a new type in @rcompat/type would be required, as this could affect other uses of this type.

Related: https://www.typescriptlang.org/tsconfig/#exactOptionalPropertyTypes

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions