Skip to content
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

Fix IdSchema and PathSchema types #4196

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

solimant
Copy link

@solimant solimant commented May 19, 2024

Reasons for making this change

The way IdSchema and PathSchema are defined results in reaching a conflicting type when the type argument passed to those types is of a primitive type, such as string.

For example, in the snippet below (TS playground link), when the type argument for IdSchema is string (i.e. IdSchema<string>), the effective resolved type would be an intersection of FieldId and string, which is impossible:

type FieldId = {
  $id: string;
};

type IdSchema<T = any> = FieldId & {
  [key in keyof T]?: IdSchema<T[key]>;
};

type FieldPath = {
  $name: string;
};

type PathSchema<T = any> = FieldPath & {
  [key in keyof T]?: PathSchema<T[key]>;
};

type StringIdSchema = IdSchema<string>; // i.e. FieldId & string

type StringPathSchema = PathSchema<string>; // i.e. FieldPath & string

// TypeScript error:
// Type '{ $id: string; }' is not assignable to type 'StringIdSchema'.
//  Type '{ $id: string; }' is not assignable to type 'string'.
const idSchema: StringIdSchema = {$id: 'some_id'};

// TypeScript error:
// Type '{ $name: string; }' is not assignable to type 'StringPathSchema'.
//  Type '{ $name: string; }' is not assignable to type 'string'.
const pathSchema: StringPathSchema = {$name: 'some_path'};

When the type argument is string, keyof string in TypeScript actually results in keyof String, which includes index signatures like length, toString, etc. However, because IdSchema and PathSchema map over the keys of the string primitive type, we need to consider that string itself is not treated as an object with properties that can be indexed. Thus, IdSchema and PathSchema will effectively ignore any keys and result in string.

This PR addresses this contradiction by limiting the resolved type of IdSchema and PathSchema to just FieldId and FieldPath, respectively, when the type argument is a non-primitive type, in which it leverages the existing type GenericObjectType. This would let TypeScript accept the types as intended (TS playground link):

type GenericObjectType = {
    [name: string]: any;
};

type FieldId = {
  $id: string;
};

type IdSchema<T = any> = T extends GenericObjectType
  ? FieldId & {
      [key in keyof T]?: IdSchema<T[key]>;
    }
  : FieldId;

type FieldPath = {
  $name: string;
};

type PathSchema<T = any> = T extends GenericObjectType
  ? FieldPath & {
      [key in keyof T]?: PathSchema<T[key]>;
    }
  : FieldPath;

type StringIdSchema = IdSchema<string>; // i.e. FieldId

type StringPathSchema = PathSchema<string>; // i.e. FieldPath

type FooType = {foo: string};

type FooIdSchema = IdSchema<FooType>; // i.e. FieldId & { foo?: FieldId | undefined };

// No TypeScript error
const idSchema: StringIdSchema = {$id: 'some_id'};

// No TypeScript error
const pathSchema: StringPathSchema = {$name: 'some_path'};

// No TypeScript error
const fooIdSchema: FooIdSchema = {$id: 'some_id', foo: {$id: 'some_foo_id'}};

If this is related to existing tickets, include links to them as well. Use the syntax fixes #[issue number] (ex: fixes #123).

If your PR is non-trivial and you'd like to schedule a synchronous review, please add it to the weekly meeting agenda: https://docs.google.com/document/d/12PjTvv21k6LIky6bNQVnsplMLLnmEuypTLQF8a-8Wss/edit

Checklist

  • I'm updating documentation
  • I'm adding or updating code
    • I've added and/or updated tests. I've run npm run test:update to update snapshots, if needed.
    • I've updated docs if needed
    • I've updated the changelog with a description of the PR
  • I'm adding a new feature
    • I've updated the playground with an example use of the feature

@heath-freenome
Copy link
Member

@solimant It looks like your type updates broke the building of utils. Can you look into this?

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

Successfully merging this pull request may close these issues.

None yet

2 participants