Skip to content

Commit

Permalink
docs(xml): write a section on common pitfalls
Browse files Browse the repository at this point in the history
It's considerably too long, but oh well.
More docs are better than less, I guess.
  • Loading branch information
lishaduck committed Jun 9, 2024
1 parent bac32c6 commit 67eb8d0
Showing 1 changed file with 53 additions and 0 deletions.
53 changes: 53 additions & 0 deletions xml/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<xml_document>'.
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<xml_document>
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?
> <!-- Comment to keep the line break. `deno fmt` is a bit buggy, it appears. -->
> 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
Expand Down

0 comments on commit 67eb8d0

Please sign in to comment.