diff --git a/xml/README.md b/xml/README.md index 41def9f..ac3375b 100644 --- a/xml/README.md +++ b/xml/README.md @@ -248,6 +248,59 @@ Note that while you can _theoretically_ use internal API properties, currently, Supporting `~children` might be added in the future ([#57](https://github.com/lowlighter/libs/issues/57)) for mixed content, but its behavior is not yet well defined. Setting `~name` manually might lead to unexpected behaviors, especially if it differs from the parent key. +## Common pitfalls + +- You may run into type mismatches with string literal types, such as follows: + ```ts + import { stringify } from "./stringify.ts" + + const ast = { + "@version": "1.0", + "@encoding": "UTF-8", + } + // @ts-expect-error + const result = stringify(ast) + ``` + ```console + Argument of type '{ "@version": string; "@encoding": string; ... + }' is not assignable to parameter of type 'Partial'. + Types of property '"@version"' are incompatible. + Type 'string' is not assignable to type '`1.${number}`'. + ``` + This is usually caused by assigning your parsed AST to a variable before passing to `stringify`. + While that's perfectly acceptable, you'll need to tell TypeScript what you plan on doing with it, otherwise it will assign overly broad types. + To solve this issue, annotate the variable with `satisfies xml_document`. + For example: + ```ts + import { stringify, type xml_document } from "./stringify.ts" + + const ast = { + "@version": "1.0", + "@encoding": "UTF-8", + } satisfies Partial + const result = stringify(ast) + ``` + This tells TypeScript that you're planning on using `ast` as an `xml_document`, so that it doesn't overly generalize the type of `"1.0"` from `` `1.${number}` `` to `string`. + > Why does TypeScript do this? + > + > It knows that `"1.0"` is mutable, despite `ast` being `const` because JavaScript allows mutating properties of constants. + > Therefore, it's unsafe to assume that `1.0` won't be changed. + > Even if there's nothing in-between the declaration and only use of the `ast` variable, the `stringify` call could mutate it, which prevents even simple cases from being safe to type literally. + > The best way to fix this is to append `as const` to tell TypeScript that it won't be changed. + > However, it still doesn't tell TypeScript what `ast` will be used for, so we feel that it's safer to use the `satisfies` operator. + > There's nothing stopping you from using both if you choose, though! + + If you don't want to import the `xml_document` type, or don't mind an extra level of nesting, you can also inline the `ast` variable into the stringify call. + This provides TypeScript with the needed context. + ```ts + import { stringify, type xml_document } from "./stringify.ts" + + const result = stringify({ + "@version": "1.0", + "@encoding": "UTF-8", + }) + ``` + ## 📜 License and credits ```plaintext