Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Support for PocketBase 0.23 with Improved expand Property Optionality Based on Texpand #107

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

0xd8d
Copy link

@0xd8d 0xd8d commented Nov 30, 2024

  • This pull request adds support for Pocketbase 0.23.
  • Enhanced expand Property Type Handling with Texpand Generic Parameter
    This update refines the type definitions for the expand property in BaseSystemFields and related types to make it optional when the generic parameter Texpand is not specified (defaults to unknown), and required when Texpand is explicitly provided. Previously, the expand property’s optionality did not depend on whether Texpand was specified, which could lead to less precise type safety.

Changes Made:

  • Defaulted TExpand to unknown:
    • In BaseSystemFields<Texpand = unknown>, the default type for Texpand is now unknown instead of never. This aligns with common TypeScript practices and ensures that when Texpand is not provided, it defaults to unknown.
  • Introduced IsExactlyUnknown Utility Type:
    • Added a new utility type IsExactlyUnknown<T> to accurately determine if a type T is exactly unknown. This utility is crucial because simply checking T extends unknown isn’t sufficient (as all types extend unknown). The utility uses advanced conditional types to make this determination.
      // Utility type to check if T is exactly unknown
      type IsExactlyUnknown<T> =
        unknown extends T
          ? T extends unknown
            ? keyof T extends never
              ? true
              : false
            : false
          : false;
  • Adjusted BaseSystemFields Conditional Logic:
    • Modified BaseSystemFields to use the IsExactlyUnknown utility type in a conditional type that determines the optionality of the expand property.
      export type BaseSystemFields<Texpand = unknown> = IsExactlyUnknown<Texpand> extends true
      ? {
          id: RecordIdString;
          collectionId: string;
          collectionName: Collections;
          expand?: unknown; // 'expand' is optional when Texpand is unknown
        }
      : {
          id: RecordIdString;
          collectionId: string;
          collectionName: Collections;
          expand: Texpand; // 'expand' is required when Texpand is specified
        };
  • Updated Dependent Types:
    • Adjusted types like AuthSystemFields and UsersResponse to align with the new BaseSystemFields definition by defaulting Texpand to unknown.
      export type AuthSystemFields<Texpand = unknown> = {
        email: string;
        emailVisibility: boolean;
        username: string;
        verified: boolean;
      } & BaseSystemFields<Texpand>;

Reasons for Changes:

  • Accurate Optionality of expand:
    • The primary goal is to make the expand property optional when no specific expand type is provided, reflecting that no expanded data is expected. When an expand type is specified, it indicates that expanded data is expected, so the expand property becomes required.
  • Enhanced Type Safety:
    • By accurately reflecting the presence or absence of expanded data in the types, we improve type safety and help prevent runtime errors related to undefined properties.

Impact of Changes:

  • Developer Experience:
    • Developers will now get more accurate IntelliSense suggestions and type checks in their IDEs, improving productivity and reducing bugs.

Examples:

  • When Texpand is omitted (defaults to unknown):
    // 'expand' is optional and of type unknown
    type UserResponse = BaseSystemFields;
    
    const user: UserResponse = {
      id: "123",
      collectionId: "users",
      collectionName: Collections.Users,
      // 'expand' can be omitted
    };
  • When Texpand is specified:
    // 'expand' is required and has the specified type
    type UserResponseWithExpand = BaseSystemFields<{ profile: Profile }>;
    
    const user: UserResponseWithExpand = {
      id: "123",
      collectionId: "users",
      collectionName: Collections.Users,
      expand: {
        profile: {
          // profile fields
        },
      },
    };

@mirus-ua
Copy link

mirus-ua commented Dec 1, 2024

Today, I've been thinking that the package needs better expand field handling, and here we go, a PR that implements this.
Thank you!

@patmood
Copy link
Owner

patmood commented Dec 2, 2024

Thanks for this! Looks like a good improvement. Give me some time to go through and test it out.

If you can check the integration tests too, that would be useful (npm run integration:local)

Copy link
Owner

@patmood patmood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again for the PR! Nice work, just a few questions/comments.

Comment on lines +21 to +25
? T extends unknown
? keyof T extends never
? true
: false
: false
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these nested conditions necessary? I would keep it simple since it's really just checking for the default param

export const BASE_SYSTEM_FIELDS_DEFINITION = `// System fields
export type BaseSystemFields<T = never> = {
\tid: ${RECORD_ID_STRING_NAME}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep the /t white space style, otherwise the output is inconsistent

@@ -16,7 +16,7 @@ export async function fromDatabase(
const result = await db.all("SELECT * FROM _collections")
return result.map((collection) => ({
...collection,
fields: JSON.parse(collection.fields),
fields: JSON.parse(collection.schema),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does this need to change?

Comment on lines +30 to +38
// Utility type to check if T is exactly unknown
type IsExactlyUnknown<T> =
unknown extends T
? T extends unknown
? keyof T extends never
? true
: false
: false
: false
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just return the expected type and be joined with BaseSystemFields? This would prevent some repetition and be more explicit about what it's doing.

Suggested change
// Utility type to check if T is exactly unknown
type IsExactlyUnknown<T> =
unknown extends T
? T extends unknown
? keyof T extends never
? true
: false
: false
: false
// Utility type to get expand field. If T is provided, expand is no longer optional
type ExpandType<T> =
unknown extends T
? T extends unknown
? { expand?: unknown }
: { expand: T }
: { expand: T }

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants