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

Support patternProperties #1859

Open
ELigoP opened this issue Jan 31, 2024 · 11 comments
Open

Support patternProperties #1859

ELigoP opened this issue Jan 31, 2024 · 11 comments

Comments

@ELigoP
Copy link

ELigoP commented Jan 31, 2024

Typescript-restricted key (fail)

// test_str.ts
export type A = `wifi${number}`;
// actual_str.json
{
  "$ref": "#/definitions/A",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "A": {
      "type": "string"
    }
  }
}
// expected_str.json
{
  "$ref": "#/definitions/A",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "A": {
      "type": "string",
      "pattern": "^wifi[0-9]+$"
    }
  }
}

Annotated string (pass)

// test_annotated_str.ts
/**
 * @pattern ^wifi[0-9]+$
 */
export type A = `wifi${number}`;
// actual_annotated_str.json
// expected_annotated_str.json
{
  "$ref": "#/definitions/A",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "A": {
      "pattern": "^wifi[0-9]+$",
      "type": "string"
    }
  }
}

Annotated key (kinda pass)

// test_annotated_key.ts
/**
 * @pattern ^wifi[0-9]+$
 */
type WifiDevName = string;
export type A = Record<WifiDevName, number>;
// actual_annotated_key.json
{
  "$ref": "#/definitions/A",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "A": {
      "additionalProperties": {
        "type": "number"
      },
      "propertyNames": {
        "pattern": "^wifi[0-9]+$"
      },
      "type": "object"
    }
  }
}
// expected_annotated_key.json
{
  "$ref": "#/definitions/A",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "A": {
      "patternProperties": {
        "^wifi[0-9]+$": {
          "type": "number"
        }
      },
      "type": "object"
    }
  }
}

Real life object (fail)

// test_real_life.ts
/**
 * @pattern ^lan[0-9]+$
 */
type LedLanKey = `lan${number}`;
/**
 * @pattern ^radio[0-9]+$
 */
type LedRadioKey = `radio${number}`;
type LedsLan = Record<LedLanKey, object>;
type LedsRadio = Record<LedRadioKey, object>;
export type A = LedsLan &
    LedsRadio & {
        power?: object;
    };
// actual_real_life.json
{
  "$ref": "#/definitions/A",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "A": {
      "additionalProperties": {
        "type": "object"
      },
      "properties": {
        "power": {
          "type": "object"
        }
      },
      "type": "object"
    }
  }
}
// expected_real_life.json
{
  "$ref": "#/definitions/A",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "A": {
      "properties": {
        "power": {
          "type": "object"
        }
      },
      "patternProperties": {
        "^lan[0-9]+$": {
          "type": "object"
        },
        "^radio[0-9]+$": {
          "type": "object"
        }
      },
      "type": "object"
    }
  }
}
@ELigoP
Copy link
Author

ELigoP commented Jan 31, 2024

I tried to read the code to contribute fix but didn't understand where to start.

@se-panfilov
Copy link

Honestly, this was my question - how to use pattern for regexp. Would be glad to see this working

@arthurfiorette
Copy link
Collaborator

Thanks for the report. Can you provide a minimal reproducible example that demonstrates the issue?

@ELigoP
Copy link
Author

ELigoP commented May 28, 2024

mre.sh:

#!/bin/bash

# Create input file
echo 'export type A = `wifi${number}`;' > test_str.ts


# Expected output
cat <<EOF > expected_str.json
{
  "\$ref": "#/definitions/A",
  "\$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "A": {
      "type": "string",
      "pattern": "^wifi[0-9]+$"
    }
  }
}
EOF

# Actual output
# Generate schema using ts-json-schema-generator
npx ts-json-schema-generator --path test_str.ts --out actual_str.json

# Compare actual and expected output
diff -u1 --color expected_str.json actual_str.json

Output:

bash mre.sh
--- expected_str.json	2024-05-28 09:46:21.504403302 +0300
+++ actual_str.json	2024-05-28 09:46:22.776424172 +0300
@@ -5,6 +5,5 @@
     "A": {
-      "type": "string",
-      "pattern": "^wifi[0-9]+$"
+      "type": "string"
     }
   }
-}
+}

@ELigoP
Copy link
Author

ELigoP commented May 28, 2024

My current solution is to use annotations, which works in some (not all) cases, but seems a redundant.

@se-panfilov
Copy link

@ELigoP Can you show a small example, pls?

@jakearchibald
Copy link

@se-panfilov I don't see how the problem could be reduced further. In case it's the way it's formatted:

The input:

export type A = `wifi${string}`;

The output:

{
  "$ref": "#/definitions/A",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "A": {
      "type": "string"
    }
  }
}

The expected output, something like:

{
  "$ref": "#/definitions/A",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {
    "A": {
      "type": "string",
      "pattern": "^wifi.*$"
    }
  }
}

Alternatively, use comments to preserve the typescript def.

@domoritz
Copy link
Member

domoritz commented Nov 3, 2024

I changed the issue to be a feature request then. The request is to generate a pattern for template strings. Note that string without a pattern is a totally valid answer (albeit not as strict as possible). Happy to review a pull request for this.

@jakearchibald
Copy link

Note that string without a pattern is a totally valid answer (albeit not as strict as possible).

Yeahhhh but by that logic you could simplify this project a lot by returning any for all the types, and claiming it's 'totally valid'.

Are there other cases where the type conversion is lossy? It'd be nice if there was an option to fail the conversion in these cases, or to include a comment with the typescript type that couldn't be losslessly converted.

@domoritz
Copy link
Member

domoritz commented Nov 4, 2024

Yes, that would be valid but a lot less useful. I'm just saying it to justify making this an enhancement rather than a bug.

I'm still completely in favor of fixing this issue!

I'm sure there are other cases where json schema is not as expressive as typescript.

@air2
Copy link

air2 commented Dec 19, 2024

Just some note: I guess the expected output should also include "additionalProperties": false else the patternProperties are not really useful

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

No branches or pull requests

6 participants