Visitar URL original
Enhance type definition for XMLParser `isArray` option when using TypeScript · Issue #803 · NaturalIntelligence/fast-xml-parser · GitHub
Skip to content

Enhance type definition for XMLParser isArray option when using TypeScript #803

Open

Description

@plushie-cat
  • Are you running the latest version?
  • Have you included sample input, output, error, and expected output?
  • Have you checked if you are using correct configuration?
  • Did you try online tool?
  • Have you checked the docs for helpful APIs and examples?

Description

When creating an instance of XMLParser using the isArray option, the method arguments are currently defined as the following, with the jPathOrMatcher argument being either a string or Matcher type:

Image

The behaviour as documented is that if the jPath option is set to true (or undefined since the default is true), then the type of jPathOrMatcher will be a string, and if jPath is false then the type of jPathOrMatcher will be a Matcher.

However, since the type of jPathOrMatcher is currently string | Matcher, to satisfy TypeScript I either have to add unnecessary conditional logic to check whether the type is string or Matcher, or add in a type assertion like (jPathOrMatcher as string).includes("blah").

I would like to propose updating the types so that it can smartly detect whether the jPath option is true or false, and set the type of isArray based on that. I've found that this can be achieved using union types, and I have a minimal working example using the TypeScript Playground:

https://www.typescriptlang.org/play/?#code/C4TwDgpgBAsghsAxgCwgJygXigbwFBSFRpwB2AJgPYC2ACmpZGqAFxRkgDceAvt3qEhQAGgCYAVgHkwwAJaVSAZyy4CRWYoCCaEiAD8bABTA4AcwBycahDaLgaWaVMAaKONoJkt+45dQNmsA+AEYArsA2UMGUlAA2EGQAlFgAfFEx8WTcRG4ewMgGUPahENw8UAA+qjkBOnD6RiYWVpF2Dk6u1Ago6GzwSKhorgFBDmERbNFxCaTJmGlTmaTZRO6ebABmcLGKpbz8APQHUAAqqEXg0JQbRedr+exopqHWpMD+pP5adSBf7FBtXx4RAKOy5TwnNAlaRyUFsMRSGTyJQqfA1b66RpmSzWVz3ZDDLSjWTjCBzNJonJENAQYChNCfYp7HI8ZxqQj4thM3h4PBHU7nQRXG75aD4x7PV7vRxfbS6P5wAE+JzA0HvfEAVQoEA2jgg5BhyMU8IkhtBqPZsp+WOauPB+UJgRC4TJqWqVMINLpDKKUOZRB4PL5xzO0CFUGutzFeWQEpeEDeHyt8o0-36PTQqqU6pjADFtrszUoTYjYSjsJTCLVMVBjNiWp1uoNHcTSeT3R6vfTGX6VoRWZbOVAtjsILwgA

Image

Would you like to work on this issue?

  • Yes
  • No

Bookmark this repository for further updates. Visit SoloThought to know about recent features.

Activity

  1. github-actions commented on Mar 12, 2026

    @github-actions

    We're glad you find this project helpful. We'll try to address this issue ASAP. You can vist https://solothought.com to know recent features. Don't forget to star this repo.

  2. amitguptagwl commented on Mar 12, 2026

    @amitguptagwl
    Member

    Would it not the case with other parameters too? Eg string | Uint8Array, alidationOptions | boolean etc?

  3. plushie-cat commented on Mar 12, 2026

    @plushie-cat
    Author

    Oh I'm not sure but maybe that is the case - I'm only aware of this isArray case because I have code that is making use of it

    I guess it might be a trade off between having a simpler typescript definition vs more complicated typescript definition but having the type be smart enough to match the implementation logic exactly 🤔

  4. hajin-song commented on Mar 13, 2026

    @hajin-song

    I'm wondering if it will be beneficial to create custom type to represent jPathOrMatcher type? If my understanding is correct, consumers of the repo will now need to add path-expression-matcher to their dependencies to satisfy the new typing.

    For example,

    export type PathOrMatcher = string | Matcher
    
    ...
    
      /**
       * Determine whether a tag should be parsed as an array
       * 
       * @param tagName - The name of the tag
       * @param jPathOrMatcher - The jPath string (if jPath: true) or Matcher instance (if jPath: false)
       * @param isLeafNode - Whether the tag is a leaf node
       * @param isAttribute - Whether this is an attribute
       * @returns {boolean}
       * 
       * Defaults to `() => false`
       */
      isArray?: (tagName: string, jPathOrMatcher: PathOrMatcher, isLeafNode: boolean, isAttribute: boolean) => boolean;
  5. amitguptagwl commented on Mar 13, 2026

    @amitguptagwl
    Member

    I believe defining Matcher and Expression as unknown would be better.

  6. amitguptagwl commented on Mar 13, 2026

    @amitguptagwl
    Member

    I've published changes in v5.5.4. Now you'll not need to install path-expression-matcher until you want to use it

  7. amitguptagwl commented on Mar 13, 2026

    @amitguptagwl
    Member

    You can add you test case in following repository for understanding purpose if it is still not supported

    https://github.com/NaturalIntelligence/fxp-type-testing

  8. plushie-cat commented on Mar 15, 2026

    @plushie-cat
    Author

    @amitguptagwl sorry maybe I'm not quite understanding but would you be able to elaborate on why unknown is better? With unknown it relies on the consumer to know the behaviour of the implementation and an error caused by accidentally using the wrong type would not get caught until runtime.

    Apologies if I'm not understanding your point but I'd just like to know what is the benefit of using unknown as the type instead of the solution proposed by @hajin-song to have a custom PathOrMatcher type, or making the type of isArray aware of what the jPath boolean option is and setting the type based on that using the union type approach that I mentioned in my original comment

  9. amitguptagwl commented on Mar 16, 2026

    @amitguptagwl
    Member

    unknown for new library. So user is not forced to install new library just to ignore compilation error while they're not using that feature. Though not recommended.

    I've not done any change to previous typings. Because it'll make typings more complex and most of the patches of this library were to fix typings only :)

  10. KuSh commented on Mar 18, 2026

    @KuSh

    For the record, unknown still cause compilation errors :

    const options: X2jOptions = {
      isArray: (_tagName, jPath, _isLeafNode, _isAttribute) => ["mypath1", "mypath2"].includes(jPath), // error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'.
    }
  11. amitguptagwl commented on Mar 18, 2026

    @amitguptagwl
    Member

    Well the type of jPath in your case should be string | unknown. Not only unknown.

  12. KuSh commented on Mar 18, 2026

    @KuSh

    That's the case, but unknown can't be used as parameter of Array.includes() as it only accept string

  13. amitguptagwl commented on Mar 18, 2026

    @amitguptagwl
    Member

    I understand. The issue is I don't want to force the user to install path-expression-matcher just for the typings. BUT if I do that then this is the issue.

    Can you please type cast it until I find some better solution?

  14. added a commit that references this issue on Mar 18, 2026
    1a47d13
  15. KuSh commented on Mar 18, 2026

    @KuSh

    Can you please type cast it until I find some better solution?

    I've temporary added path-expression-matcher import in my app. The best way is to do what @plushie-cat suggested, a unionated type:

    type X2jOptions = {
       ... // common options
    } & (
      { jPath?: true; isArray?: (tagName: string, jPathOrMatcher: string, isLeafNode: boolean, isAttribute: boolean) => boolean; }
      | { jPath: false; isArray?: (tagName: string, jPathOrMatcher: Matcher, isLeafNode: boolean, isAttribute: boolean) => boolean; }
    )

    It should be possible to use a generic type to do it automatically

  16. amitguptagwl commented on Mar 19, 2026

    @amitguptagwl
    Member

    This will require to duplicate pattern for all remaining methods: tagValueProcessor, attributeValueProcessor, ignoreAttributes, tagFilter, updateTag etc.

    What if we declare Matcher and Expresson as object? So people who are using it can typecast and the one who are not using would require no change.

  17. KuSh commented on Mar 19, 2026

    @KuSh

    This will require to duplicate pattern for all remaining methods: tagValueProcessor, attributeValueProcessor, ignoreAttributes, tagFilter, updateTag etc.

    That's why I was talking about using a generic type to do the work for us

    What if we declare Matcher and Expresson as object? So people who are using it can typecast and the one who are not using would require no change.

    Anything else than string will generate typecheck errors for current users

  18. amitguptagwl commented on Mar 19, 2026

    @amitguptagwl
    Member

    I would probably copy paste the path-expression-matcher. typings and mark old properties deprecate.

  19. amitguptagwl commented on Mar 20, 2026

    @amitguptagwl
    Member

    Can you please check if v5.5.8 solves your issues? I would suggest you to use Expression instead of fixed string to improve performance. Additionally, use matcher in call backs for flexibility and performance. Migration is very easy. You just need to remember these 2 calls;

    new Expression("old or new expression");

    matcher.match("old or new expression");

  20. KuSh commented on Mar 20, 2026

    @KuSh

    I would suggest you to use Expression instead of fixed string to improve performance. ... Migration is very easy. You just need to remember these 2 calls;

    That's not really the problem, I was forced to update fxp because of CVE security alerts, and one of this upgrade failed because of type check errors on a patch version ... that was unexpected and blocked our automated deps security upgrades

  21. amitguptagwl commented on Mar 23, 2026

    @amitguptagwl
    Member

    Apologies for the inconvenience caused. Dozens of vulnerabilities were fixed in last 1 month. And this change were being pushed from more than an year. So they mixed up.
    I planned for a silent release so user will require no change but that could improve the performance partially. So I had to gp for this change with flag for backward compatibility but typings broke the experience.

  22. KuSh commented on Mar 23, 2026

    @KuSh

    Yeah, I wasn't blaming you at all ; I was just trying to explain why I chimed in. Users don't expect breaking changes from patch upgrades, even type check errors ;)

    For the record, version 5.5.8 doesn't fix the original problem:

          isArray: (_tagName, jPath, _isLeafNode, _isAttribute) => {
            return arrayPaths.includes(jPath); // error: Argument of type JPathOrMatcher is not assignable to parameter of type string. Type ReadonlyMatcher is not assignable to type string. (ts 2345)
          }

    As I've upgraded my code paths this won't be a problem anymore for us, but people who upgrade will still have those compilation errors

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

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