@solidjs/h 2.0.0-experimental.9 → 2.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -17
- package/dist/h.cjs +64 -24
- package/dist/h.js +65 -25
- package/jsx-runtime/types/index.d.ts +7 -0
- package/jsx-runtime/types/jsx-properties.d.ts +93 -0
- package/jsx-runtime/types/jsx.d.ts +4156 -0
- package/jsx-runtime/types-cjs/index.d.cts +7 -0
- package/jsx-runtime/types-cjs/jsx-properties.d.cts +93 -0
- package/jsx-runtime/types-cjs/jsx.d.cts +4156 -0
- package/jsx-runtime/types-cjs/package.json +3 -0
- package/package.json +42 -22
- package/types/hyperscript.d.ts +28 -0
- package/types/index.d.ts +3 -0
- package/types-cjs/hyperscript.d.cts +28 -0
- package/types-cjs/index.d.cts +3 -0
- package/types-cjs/package.json +3 -0
package/README.md
CHANGED
|
@@ -6,31 +6,37 @@ HyperScript function takes a few forms. The 2nd props argument is optional. Chil
|
|
|
6
6
|
|
|
7
7
|
```js
|
|
8
8
|
// create an element with a title attribute
|
|
9
|
-
h("button", { title: "My button" }, "Click Me")
|
|
9
|
+
h("button", { title: "My button" }, "Click Me");
|
|
10
10
|
|
|
11
11
|
// create a component with a title prop
|
|
12
|
-
h(Button, { title: "My button" }, "Click Me")
|
|
12
|
+
h(Button, { title: "My button" }, "Click Me");
|
|
13
13
|
|
|
14
14
|
// create an element with many children
|
|
15
|
-
h("div", { title: "My button" }, h("span", "1"), h("span", "2"), h("span", "3"))
|
|
15
|
+
h("div", { title: "My button" }, h("span", "1"), h("span", "2"), h("span", "3"));
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
This is the least efficient way to use Solid as it requires a slightly larger runtime that isn't treeshakeable, and cannot leverage anything in the way of analysis, so it requires manual wrapping of expressions and has a few other caveats (see below).
|
|
19
19
|
|
|
20
|
+
> `h(...)` returns a tagged zero-arity thunk rather than a DOM node directly.
|
|
21
|
+
> Pass a function reference (or `() => h(App)`) to `render(...)` so the thunk
|
|
22
|
+
> is invoked inside the root. Nested `h(...)` thunks auto-invoke when consumed,
|
|
23
|
+
> so composition with control-flow components like `<For>` and `<Show>` works
|
|
24
|
+
> without extra wrapping.
|
|
25
|
+
|
|
20
26
|
## Example
|
|
21
27
|
|
|
22
28
|
```js
|
|
23
|
-
import { render } from "
|
|
24
|
-
import h from "
|
|
29
|
+
import { render } from "@solidjs/web";
|
|
30
|
+
import h from "@solidjs/h";
|
|
25
31
|
import { createSignal } from "solid-js";
|
|
26
32
|
|
|
27
33
|
function Button(props) {
|
|
28
|
-
return h("button.btn-primary", props)
|
|
34
|
+
return h("button.btn-primary", props);
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
function Counter() {
|
|
32
38
|
const [count, setCount] = createSignal(0);
|
|
33
|
-
const increment =
|
|
39
|
+
const increment = e => setCount(c => c + 1);
|
|
34
40
|
|
|
35
41
|
return h(Button, { type: "button", onClick: increment }, count);
|
|
36
42
|
}
|
|
@@ -38,6 +44,21 @@ function Counter() {
|
|
|
38
44
|
render(Counter, document.getElementById("app"));
|
|
39
45
|
```
|
|
40
46
|
|
|
47
|
+
## TypeScript JSX
|
|
48
|
+
|
|
49
|
+
When using TypeScript's automatic JSX runtime with hyperscript, point `jsxImportSource` at `@solidjs/h`:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"compilerOptions": {
|
|
54
|
+
"jsx": "react-jsx",
|
|
55
|
+
"jsxImportSource": "@solidjs/h"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`solid-js` does not provide JSX runtime types in 2.0. Web JSX should use `@solidjs/web`; hyperscript JSX should use `@solidjs/h`.
|
|
61
|
+
|
|
41
62
|
## Differences from JSX
|
|
42
63
|
|
|
43
64
|
There are a few differences from Solid's JSX that are important to note. And also apply when attempting use any transformation that would compile to HyperScript.
|
|
@@ -46,22 +67,22 @@ There are a few differences from Solid's JSX that are important to note. And als
|
|
|
46
67
|
|
|
47
68
|
```js
|
|
48
69
|
// jsx
|
|
49
|
-
<div id={props.id}>{firstName() + lastName()}</div
|
|
70
|
+
<div id={props.id}>{firstName() + lastName()}</div>;
|
|
50
71
|
|
|
51
72
|
// hyperscript
|
|
52
|
-
h("div", { id: () => props.id }, () => firstName() + lastName())
|
|
73
|
+
h("div", { id: () => props.id }, () => firstName() + lastName());
|
|
53
74
|
```
|
|
54
75
|
|
|
55
76
|
2. Merging spreads requires using the merge props helper to keep reactivity
|
|
56
77
|
|
|
57
78
|
```js
|
|
58
79
|
// jsx
|
|
59
|
-
<div class={selectedClass()} {...props}
|
|
80
|
+
<div class={selectedClass()} {...props} />;
|
|
60
81
|
|
|
61
82
|
// hyperscript
|
|
62
|
-
import {
|
|
83
|
+
import { merge } from "solid-js";
|
|
63
84
|
|
|
64
|
-
h("div",
|
|
85
|
+
h("div", merge({ class: selectedClass }, props));
|
|
65
86
|
```
|
|
66
87
|
|
|
67
88
|
3. Events on components require explicit event in the arguments
|
|
@@ -70,10 +91,10 @@ Solid's HyperScript automatically wraps functions passed to props of components
|
|
|
70
91
|
|
|
71
92
|
```js
|
|
72
93
|
// good
|
|
73
|
-
h(Button, { onClick:
|
|
94
|
+
h(Button, { onClick: e => console.log("Hi") });
|
|
74
95
|
|
|
75
96
|
// bad
|
|
76
|
-
h(Button, { onClick: () => console.log("Hi")})
|
|
97
|
+
h(Button, { onClick: () => console.log("Hi") });
|
|
77
98
|
```
|
|
78
99
|
|
|
79
100
|
4. All refs are callback form
|
|
@@ -83,17 +104,17 @@ We can't do the compiled assignment trick so only the callback form is supported
|
|
|
83
104
|
```js
|
|
84
105
|
let myEl;
|
|
85
106
|
|
|
86
|
-
h(div, { ref:
|
|
107
|
+
h(div, { ref: el => (myEl = el) });
|
|
87
108
|
```
|
|
88
109
|
|
|
89
110
|
5. There is a shorthand for static id and classes
|
|
90
111
|
|
|
91
112
|
```js
|
|
92
|
-
h("div#some-id.my-class")
|
|
113
|
+
h("div#some-id.my-class");
|
|
93
114
|
```
|
|
94
115
|
|
|
95
116
|
6. Fragments are just arrays
|
|
96
117
|
|
|
97
118
|
```js
|
|
98
|
-
[h("span", "1"), h("span", "2")]
|
|
119
|
+
[h("span", "1"), h("span", "2")];
|
|
99
120
|
```
|
package/dist/h.cjs
CHANGED
|
@@ -2,15 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
var web = require('@solidjs/web');
|
|
4
4
|
|
|
5
|
+
const $ELEMENT = Symbol("hyper-element");
|
|
6
|
+
const $WRAPPED = Symbol("hyper-wrapped");
|
|
7
|
+
function resolveThunks(value) {
|
|
8
|
+
if (typeof value === "function" && value[$ELEMENT]) return resolveThunks(value());
|
|
9
|
+
if (Array.isArray(value)) {
|
|
10
|
+
const out = new Array(value.length);
|
|
11
|
+
for (let i = 0; i < value.length; i++) out[i] = resolveThunks(value[i]);
|
|
12
|
+
return out;
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function wrapCallback(orig) {
|
|
17
|
+
let w;
|
|
18
|
+
if (orig.length === 1) {
|
|
19
|
+
w = function (a) {
|
|
20
|
+
return resolveThunks(orig.call(this, a));
|
|
21
|
+
};
|
|
22
|
+
} else if (orig.length === 2) {
|
|
23
|
+
w = function (a, b) {
|
|
24
|
+
return resolveThunks(orig.call(this, a, b));
|
|
25
|
+
};
|
|
26
|
+
} else {
|
|
27
|
+
w = function (...args) {
|
|
28
|
+
return resolveThunks(orig.apply(this, args));
|
|
29
|
+
};
|
|
30
|
+
Object.defineProperty(w, "length", {
|
|
31
|
+
value: orig.length
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
w[$WRAPPED] = true;
|
|
35
|
+
return w;
|
|
36
|
+
}
|
|
5
37
|
function createHyperScript(r) {
|
|
6
|
-
function h() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
38
|
+
function h(...rawArgs) {
|
|
39
|
+
if (rawArgs.length === 1 && Array.isArray(rawArgs[0])) return rawArgs[0];
|
|
40
|
+
const thunk = () => r.untrack(() => materialize(rawArgs));
|
|
41
|
+
thunk[$ELEMENT] = true;
|
|
42
|
+
return thunk;
|
|
43
|
+
}
|
|
44
|
+
function materialize(args) {
|
|
45
|
+
let e;
|
|
46
|
+
let classes = [];
|
|
47
|
+
let multiExpression = false;
|
|
48
|
+
args = args.slice();
|
|
11
49
|
function item(l) {
|
|
50
|
+
if (l == null) return;
|
|
12
51
|
const type = typeof l;
|
|
13
|
-
if (
|
|
52
|
+
if ("string" === type) {
|
|
14
53
|
if (!e) parseClass(l);else e.appendChild(document.createTextNode(l));
|
|
15
54
|
} else if ("number" === type || "boolean" === type || "bigint" === type || "symbol" === type || l instanceof Date || l instanceof RegExp) {
|
|
16
55
|
e.appendChild(document.createTextNode(l.toString()));
|
|
@@ -35,29 +74,27 @@ function createHyperScript(r) {
|
|
|
35
74
|
dynamic = true;
|
|
36
75
|
} else if (d[k].get) dynamic = true;
|
|
37
76
|
}
|
|
38
|
-
dynamic ? r.spread(e, l,
|
|
77
|
+
dynamic ? r.spread(e, l, !!args.length) : r.assign(e, l, !!args.length);
|
|
39
78
|
} else if ("function" === type) {
|
|
40
79
|
if (!e) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
80
|
+
const first = args[0];
|
|
81
|
+
const props = first == null || typeof first === "object" && !Array.isArray(first) && !(first instanceof Element) ? args.shift() || {} : {};
|
|
82
|
+
if (args.length) props.children = args.length > 1 ? args : args[0];
|
|
83
|
+
for (const k in props) {
|
|
84
|
+
const v = props[k];
|
|
85
|
+
if (typeof v === "function") {
|
|
86
|
+
if (!v.length) r.dynamicProperty(props, k);else if (!v[$ELEMENT] && !v[$WRAPPED]) {
|
|
87
|
+
props[k] = wrapCallback(v);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
51
90
|
}
|
|
52
91
|
e = r.createComponent(l, props);
|
|
92
|
+
while (typeof e === "function" && e[$ELEMENT]) e = e();
|
|
53
93
|
args = [];
|
|
54
|
-
} else
|
|
55
|
-
r.insert(e, l, multiExpression ? null : undefined);
|
|
56
|
-
}
|
|
94
|
+
} else if (l[$ELEMENT]) item(l());else r.insert(e, l, multiExpression ? null : undefined);
|
|
57
95
|
}
|
|
58
96
|
}
|
|
59
|
-
if (args
|
|
60
|
-
typeof args[0] === "string" && detectMultiExpression(args);
|
|
97
|
+
if (typeof args[0] === "string") detectMultiExpression(args);
|
|
61
98
|
while (args.length) item(args.shift());
|
|
62
99
|
if (e instanceof Element && classes.length) e.classList.add(...classes);
|
|
63
100
|
return e;
|
|
@@ -68,7 +105,7 @@ function createHyperScript(r) {
|
|
|
68
105
|
const v = m[i],
|
|
69
106
|
s = v.substring(1, v.length);
|
|
70
107
|
if (!v) continue;
|
|
71
|
-
if (!e) e = r.SVGElements.has(v) ? document.createElementNS(
|
|
108
|
+
if (!e) e = r.SVGElements.has(v) ? document.createElementNS(r.Namespaces.svg, v) : r.MathMLElements.has(v) ? document.createElementNS(r.Namespaces.mathml, v) : document.createElement(v);else if (v[0] === ".") classes.push(s);else if (v[0] === "#") e.setAttribute("id", s);
|
|
72
109
|
}
|
|
73
110
|
}
|
|
74
111
|
function detectMultiExpression(list) {
|
|
@@ -92,7 +129,10 @@ const h = createHyperScript({
|
|
|
92
129
|
insert: web.insert,
|
|
93
130
|
createComponent: web.createComponent,
|
|
94
131
|
dynamicProperty: web.dynamicProperty,
|
|
95
|
-
|
|
132
|
+
untrack: web.untrack,
|
|
133
|
+
SVGElements: web.SVGElements,
|
|
134
|
+
MathMLElements: web.MathMLElements,
|
|
135
|
+
Namespaces: web.Namespaces
|
|
96
136
|
});
|
|
97
137
|
|
|
98
138
|
module.exports = h;
|
package/dist/h.js
CHANGED
|
@@ -1,14 +1,53 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Namespaces, MathMLElements, SVGElements, untrack, dynamicProperty, createComponent, insert, assign, spread } from '@solidjs/web';
|
|
2
2
|
|
|
3
|
+
const $ELEMENT = Symbol("hyper-element");
|
|
4
|
+
const $WRAPPED = Symbol("hyper-wrapped");
|
|
5
|
+
function resolveThunks(value) {
|
|
6
|
+
if (typeof value === "function" && value[$ELEMENT]) return resolveThunks(value());
|
|
7
|
+
if (Array.isArray(value)) {
|
|
8
|
+
const out = new Array(value.length);
|
|
9
|
+
for (let i = 0; i < value.length; i++) out[i] = resolveThunks(value[i]);
|
|
10
|
+
return out;
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function wrapCallback(orig) {
|
|
15
|
+
let w;
|
|
16
|
+
if (orig.length === 1) {
|
|
17
|
+
w = function (a) {
|
|
18
|
+
return resolveThunks(orig.call(this, a));
|
|
19
|
+
};
|
|
20
|
+
} else if (orig.length === 2) {
|
|
21
|
+
w = function (a, b) {
|
|
22
|
+
return resolveThunks(orig.call(this, a, b));
|
|
23
|
+
};
|
|
24
|
+
} else {
|
|
25
|
+
w = function (...args) {
|
|
26
|
+
return resolveThunks(orig.apply(this, args));
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(w, "length", {
|
|
29
|
+
value: orig.length
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
w[$WRAPPED] = true;
|
|
33
|
+
return w;
|
|
34
|
+
}
|
|
3
35
|
function createHyperScript(r) {
|
|
4
|
-
function h() {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
36
|
+
function h(...rawArgs) {
|
|
37
|
+
if (rawArgs.length === 1 && Array.isArray(rawArgs[0])) return rawArgs[0];
|
|
38
|
+
const thunk = () => r.untrack(() => materialize(rawArgs));
|
|
39
|
+
thunk[$ELEMENT] = true;
|
|
40
|
+
return thunk;
|
|
41
|
+
}
|
|
42
|
+
function materialize(args) {
|
|
43
|
+
let e;
|
|
44
|
+
let classes = [];
|
|
45
|
+
let multiExpression = false;
|
|
46
|
+
args = args.slice();
|
|
9
47
|
function item(l) {
|
|
48
|
+
if (l == null) return;
|
|
10
49
|
const type = typeof l;
|
|
11
|
-
if (
|
|
50
|
+
if ("string" === type) {
|
|
12
51
|
if (!e) parseClass(l);else e.appendChild(document.createTextNode(l));
|
|
13
52
|
} else if ("number" === type || "boolean" === type || "bigint" === type || "symbol" === type || l instanceof Date || l instanceof RegExp) {
|
|
14
53
|
e.appendChild(document.createTextNode(l.toString()));
|
|
@@ -33,29 +72,27 @@ function createHyperScript(r) {
|
|
|
33
72
|
dynamic = true;
|
|
34
73
|
} else if (d[k].get) dynamic = true;
|
|
35
74
|
}
|
|
36
|
-
dynamic ? r.spread(e, l,
|
|
75
|
+
dynamic ? r.spread(e, l, !!args.length) : r.assign(e, l, !!args.length);
|
|
37
76
|
} else if ("function" === type) {
|
|
38
77
|
if (!e) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
78
|
+
const first = args[0];
|
|
79
|
+
const props = first == null || typeof first === "object" && !Array.isArray(first) && !(first instanceof Element) ? args.shift() || {} : {};
|
|
80
|
+
if (args.length) props.children = args.length > 1 ? args : args[0];
|
|
81
|
+
for (const k in props) {
|
|
82
|
+
const v = props[k];
|
|
83
|
+
if (typeof v === "function") {
|
|
84
|
+
if (!v.length) r.dynamicProperty(props, k);else if (!v[$ELEMENT] && !v[$WRAPPED]) {
|
|
85
|
+
props[k] = wrapCallback(v);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
49
88
|
}
|
|
50
89
|
e = r.createComponent(l, props);
|
|
90
|
+
while (typeof e === "function" && e[$ELEMENT]) e = e();
|
|
51
91
|
args = [];
|
|
52
|
-
} else
|
|
53
|
-
r.insert(e, l, multiExpression ? null : undefined);
|
|
54
|
-
}
|
|
92
|
+
} else if (l[$ELEMENT]) item(l());else r.insert(e, l, multiExpression ? null : undefined);
|
|
55
93
|
}
|
|
56
94
|
}
|
|
57
|
-
if (args
|
|
58
|
-
typeof args[0] === "string" && detectMultiExpression(args);
|
|
95
|
+
if (typeof args[0] === "string") detectMultiExpression(args);
|
|
59
96
|
while (args.length) item(args.shift());
|
|
60
97
|
if (e instanceof Element && classes.length) e.classList.add(...classes);
|
|
61
98
|
return e;
|
|
@@ -66,7 +103,7 @@ function createHyperScript(r) {
|
|
|
66
103
|
const v = m[i],
|
|
67
104
|
s = v.substring(1, v.length);
|
|
68
105
|
if (!v) continue;
|
|
69
|
-
if (!e) e = r.SVGElements.has(v) ? document.createElementNS(
|
|
106
|
+
if (!e) e = r.SVGElements.has(v) ? document.createElementNS(r.Namespaces.svg, v) : r.MathMLElements.has(v) ? document.createElementNS(r.Namespaces.mathml, v) : document.createElement(v);else if (v[0] === ".") classes.push(s);else if (v[0] === "#") e.setAttribute("id", s);
|
|
70
107
|
}
|
|
71
108
|
}
|
|
72
109
|
function detectMultiExpression(list) {
|
|
@@ -90,7 +127,10 @@ const h = createHyperScript({
|
|
|
90
127
|
insert,
|
|
91
128
|
createComponent,
|
|
92
129
|
dynamicProperty,
|
|
93
|
-
|
|
130
|
+
untrack,
|
|
131
|
+
SVGElements,
|
|
132
|
+
MathMLElements,
|
|
133
|
+
Namespaces
|
|
94
134
|
});
|
|
95
135
|
|
|
96
136
|
export { h as default };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { JSX } from "./jsx.d.ts";
|
|
2
|
+
import type { JSX } from "./jsx.d.ts";
|
|
3
|
+
declare function Fragment(props: {
|
|
4
|
+
children: JSX.Element;
|
|
5
|
+
}): JSX.Element;
|
|
6
|
+
declare function jsx(type: any, props: any): JSX.Element;
|
|
7
|
+
export { jsx, jsx as jsxs, jsx as jsxDEV, Fragment };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared type-level helpers used to derive `prop:*` attribute typings from
|
|
3
|
+
* DOM element interfaces (e.g. `HTMLInputElement`, `HTMLButtonElement`).
|
|
4
|
+
*
|
|
5
|
+
* The wrapping of each value (`FunctionMaybe<T>` in `jsx-h.d.ts` vs. the
|
|
6
|
+
* raw value in `jsx.d.ts`) is applied by each consumer when composing its
|
|
7
|
+
* own `Properties<T>` mapped type. That way this file stays identical in
|
|
8
|
+
* both reactive and non-reactive contexts and only needs to exist once.
|
|
9
|
+
*
|
|
10
|
+
* originally from
|
|
11
|
+
* @url https://github.com/potahtml/pota
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Base-class properties shared by all elements — skipped from `prop:*`. */
|
|
15
|
+
export type SkipPropsFrom = HTMLUnknownElement & HTMLElement & Element & Node;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Value types allowed on a `prop:*`. Primitives plus the writable
|
|
19
|
+
* non-primitive DOM-object props worth exposing:
|
|
20
|
+
*
|
|
21
|
+
* - `HTMLMediaElement.srcObject`
|
|
22
|
+
* - `HTMLButtonElement.popoverTargetElement` / `commandForElement` (and the same via
|
|
23
|
+
* `PopoverTargetAttributes` mixin on `HTMLInputElement`)
|
|
24
|
+
*/
|
|
25
|
+
export type PropValue =
|
|
26
|
+
| string
|
|
27
|
+
| number
|
|
28
|
+
| boolean
|
|
29
|
+
| null
|
|
30
|
+
| MediaStream
|
|
31
|
+
| MediaSource
|
|
32
|
+
| Blob
|
|
33
|
+
| File
|
|
34
|
+
| Date
|
|
35
|
+
| Element;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Ergonomics widening for emitted `prop:*` value types:
|
|
39
|
+
*
|
|
40
|
+
* - general `string` → `string | number` (HTML coerces numbers)
|
|
41
|
+
* - string literal unions (`'on' | 'off'`) stay exact, so users still get autocomplete /
|
|
42
|
+
* narrowing
|
|
43
|
+
* - other types pass through unchanged
|
|
44
|
+
*/
|
|
45
|
+
type WidenString<V> = string extends V ? string | number : V;
|
|
46
|
+
export type WidenPropValue<V> = [V] extends [string] ? WidenString<V> : V;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Structurally identical → `Y`; distinct → `N`. Used by `IsReadonlyKey` to detect
|
|
50
|
+
* readonly keys by comparing `Pick<T, K>` with `Readonly<Pick<T, K>>`.
|
|
51
|
+
*/
|
|
52
|
+
export type IfEquals<A, B, Y = unknown, N = never> =
|
|
53
|
+
(<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? Y : N;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* True when `K` is readonly on `T`. Singleton-constant properties (e.g.
|
|
57
|
+
* `tagName: "INPUT"`, `nodeType: 1`) are always `readonly` in `lib.dom.d.ts`, so this
|
|
58
|
+
* single check covers both readonly and singleton-literal cases.
|
|
59
|
+
*/
|
|
60
|
+
export type IsReadonlyKey<T, K extends keyof T> = IfEquals<
|
|
61
|
+
Pick<T, K>,
|
|
62
|
+
Readonly<Pick<T, K>>,
|
|
63
|
+
true,
|
|
64
|
+
false
|
|
65
|
+
>;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Resolves to the `prop:K` string literal when `K` is a writable, element-specific
|
|
69
|
+
* property suitable for a `prop:*` attribute; otherwise resolves to `never` so the
|
|
70
|
+
* key is filtered out of the resulting mapped type.
|
|
71
|
+
*
|
|
72
|
+
* Filters out:
|
|
73
|
+
*
|
|
74
|
+
* - base-class keys (via `SkipPropsFrom`)
|
|
75
|
+
* - aria-* keys (already typed via `AriaAttributes`)
|
|
76
|
+
* - readonly keys
|
|
77
|
+
* - keys whose value types fall outside `PropValue`
|
|
78
|
+
* - the generic `string` index signature (e.g. `HTMLFormElement[name: string]: any`),
|
|
79
|
+
* which would otherwise shadow every key with an `any`-typed `prop:*`
|
|
80
|
+
*/
|
|
81
|
+
export type PropKey<T, K extends keyof T> = K extends keyof SkipPropsFrom
|
|
82
|
+
? never
|
|
83
|
+
: K extends string
|
|
84
|
+
? string extends K
|
|
85
|
+
? never
|
|
86
|
+
: K extends `aria${string}`
|
|
87
|
+
? never
|
|
88
|
+
: T[K] extends PropValue
|
|
89
|
+
? IsReadonlyKey<T, K> extends true
|
|
90
|
+
? never
|
|
91
|
+
: `prop:${K}`
|
|
92
|
+
: never
|
|
93
|
+
: never;
|