WebReflection / uhtml

A micro HTML/SVG render
MIT License
917 stars 37 forks source link

Typescript issues #93

Closed greentore closed 11 months ago

greentore commented 11 months ago
  1. render is typed to only allow () => uhtml.Hole when the documentation hints pasing holes directly is allowed too.
  2. html is typed to only allow primitive values or holes as values, which means inline event listeners like onclick=${handler} error.
WebReflection commented 11 months ago

what are you suggesting? I don't TS much so I might not fix this best

greentore commented 11 months ago

I took a closer look and realized 1 is a simple precedence error. Replacing () => Hole | Hole with (() => Hole) | Hole should fix it. As for 2, I don't really understand the code well enough to suggest typings. The simplest solution would be to just use any / unknown in place of Value type (I believe that's how it was done in 3.x.x) to avoid false positives in user code. Anyway, I managed to fix errors in my code by typing Value this way:

/**
 * @template T
 * @typedef {T | T[]} MaybeArray<T>
 */

/** @typedef {MaybeArray<null | undefined | string | number | boolean | Hole | ((...args: unknown[]) => unknown)>} Value */

but it's likely not bulletproof.

WebReflection commented 11 months ago

do you mind filing a PR? :D

greentore commented 11 months ago

Sure, I don't mind. But first I want to know which approach you prefer: bailing out with unknown or trying to patch up Value type? Personally I'm leaning towards the former.

WebReflection commented 11 months ago

I don't care :partying_face: I don't use TS and in this case it feels more like troubles than a feature, as the context is in the string surrounding the interpolation, and I am sure TS can't understand any of that, so it's OK to have any, actually that's probably for the best in general!

WebReflection commented 11 months ago

so, I went ahead and filed this MR https://github.com/WebReflection/uhtml/pull/95

I think the function case is solved (even if for listeners is actually more like (event:Event) => void to me) but I am not sure what is the issue with Value ... it's currently specified as ...values:Value[] all over the place and it's never a maybe array of values, it's an always because of how tags work.

WebReflection commented 11 months ago

OK, I've published that MR so please let me know if anything is still problematic now or if it's "good enough" ... and feel free to re-open this if that's the case, thank you!

greentore commented 11 months ago

@WebReflection with current types it's not legal to return an array from an expression.

import { render, html } from 'uhtml';

render(document.querySelector('#todos'), html`
  <ul>
    ${databaseResults.map(value => html`<li>${value}</li>`)}
  </ul>
`);

For example this piece of code from the documentation is going to error because Hole[] isn't a valid Value. MaybeArray<T> utility type marks an array with elements of type T as valid too.

WebReflection commented 11 months ago

would Hole[] as extra type work? Maybe you are right that unknown is a better type ...

WebReflection commented 11 months ago

the thing is ... I think lists are accepted in two cases:

how about a Value like this?

/**
 * @template T
 * @typedef {T | T[]} Value
 */

that solves most gotchas to me ... but maybe that's also unnecessary when this would do too?

/** @typedef {unknown | unknown[]} Value */

I might just go for the later kind, and eventually find a "bullet proof" solution in the future ... what do you think?

WebReflection commented 11 months ago

dho! ... unknown | unknown[] feels like an overengineered unknown ... I might just stick with unknown as Value for now and I will think further how to narrow down helping with types there.

WebReflection commented 11 months ago

let me know if unknown now works as expected or with less issues, thank you!

greentore commented 11 months ago

Yeah, it works correctly now. I will let you know if I find any other type issues.