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

Convert some patternProperties to template literal typescript #518

Open
leximarie opened this issue Mar 14, 2023 · 1 comment
Open

Convert some patternProperties to template literal typescript #518

leximarie opened this issue Mar 14, 2023 · 1 comment

Comments

@leximarie
Copy link

I have an schema like:

{
  "title": "Person",
  "type": "object",
    "conditions": {
      "type": "object",
      "patternProperties": {
        "applies_to_(foo|bar)": {
          "type": "string"
        }
      }
    }
  }

This can be transferred into a template literal structure in typescript like:

export interface Person {
  conditions?: {
    [k in `applies_to_${"foo" | "bar"}`]: string;
  };
}

I was playing around with the parser.ts and came up with something like this in the parseSchema function:

  let singlePatternProperty = false
  if (schema.patternProperties) {
    // partially support patternProperties. in the case that
    // additionalProperties is not set, and there is only a single
    // value definition, we can validate against that.
    singlePatternProperty = !schema.additionalProperties && Object.keys(schema.patternProperties).length === 1

    asts = asts.concat(
      map(schema.patternProperties, (value, key: string) => {
        const ast = parse(value, options, key, processed, usedNames)
        const comment = `This interface was referenced by \`${parentSchemaName}\`'s JSON-Schema definition
via the \`patternProperty\` "${key}".`
        ast.comment = ast.comment ? `${ast.comment}\n\n${comment}` : comment
        let keyName = singlePatternProperty ? '[k: string]' : key

        // find the literals that can be used to make up the "or"
        const literalsMatch = key.match(/(?<keyPart>.*)\((?<literals>(\w+\|?)*)\)/)
        if (literalsMatch && literalsMatch.groups?.literals && singlePatternProperty) {
          const literals = literalsMatch.groups.literals.split('|')
          keyName = `[k in \`${literalsMatch.groups.keyPart}\$\{${literals.map(k => `"${k}"`).join('|')}\}\`]`
        }

        return {
          ast,
          isPatternProperty: !singlePatternProperty,
          isRequired: singlePatternProperty || includes(schema.required || [], key),
          isUnreachableDefinition: false,
          keyName: keyName
        }
      })
    )
  }

This is working (after changing the generator's escapeKeyName function a bit), but I'm sure I'm missing some obvious use cases where this approach can be improved. While this would work for my specific case I would like to get it working for some other applicable cases and prevent it from breaking current cases.

Any feedback/suggestions would be appreciated :)

@leximarie
Copy link
Author

UPDATE: A major key that I'm missing is how the library determines if the type should be generated as a type or interface. The implementation that I have works for nested properties, but not standalone properties because they need to be exported as type. @bcherny could you give me an idea of how I could tell the generator to make these properties their own types and join them by unions?

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

No branches or pull requests

1 participant