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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions