-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved JSDoc comments for type correctness. #1161
Changes from 9 commits
adf8dd5
26ea308
e6573fa
7d21d23
b6eb3c9
1e4140a
64e8503
e95c386
d2b7141
c0ae1d4
05a7ddc
aca4d72
7f5735e
5ebff59
dd17907
0387927
3a189fa
1ed6764
37495b2
cc10b1c
6125010
972d6b3
976ac4a
78ea2ef
bce5afa
5ac7548
9f82f5b
0d2aea9
170109b
decf628
57a66e2
ab2359c
59c4695
67e5185
36f2a27
fb063cf
b479244
d4abe42
8cc588d
a2be058
bfd9743
c24eccf
09f9690
c90f388
ce26426
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
/coverage | ||
package-lock.json | ||
/test/ts/**/*.js | ||
.*.swp | ||
|
||
# Additional bundles | ||
/devtools.js | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,13 +33,13 @@ export function createComponent(Ctor, props, context) { | |
inst; | ||
|
||
if (Ctor.prototype && Ctor.prototype.render) { | ||
inst = new Ctor(props, context); | ||
inst = new /** @type {(props, context) => void} */(Ctor)(props, context); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems strange to me. @rbiggs Can you elaborate on the thinking behind it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The following change I proposed in rbiggs/preact#2 should make this read a bit better: --- a/src/vdom/component-recycler.js
+++ b/src/vdom/component-recycler.js
@@ -33,7 +33,9 @@ export function createComponent(Ctor, props, context) {
inst;
if (Ctor.prototype && Ctor.prototype.render) {
- inst = new /** @type {(props, context) => void} */(Ctor)(props, context);
+ const ComponentCtor =
+ /** @type {(props, context) => void} */(Ctor);
+ inst = new ComponentCtor (props, context);
Component.call(inst, props, context);
}
else { There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The variables are fine, it's the typings that I have an issue with. It seems wrong and more like an issue with typescript if one has to define a class constructor as a plain function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, it's the wrong type being defined in the parameter: /**
* Create a component. Normalizes differences between PFC's and classful
* Components.
* @param {function} Ctor The constructor of the component to create
* @param {object} props The initial props of the component
* @param {object} context The initial context of the component
* @returns {import('../component').Component}
*/ The Ctor type is described as a generic function, which can't be used with the /**
* Create a component. Normalizes differences between PFC's and classful
* Components.
* @param {(props, context) => void} Ctor The constructor of the component to create
* @param {object} props The initial props of the component
* @param {object} context The initial context of the component
* @returns {import('../component').Component}
*/ Then the type cast isn't necessary: if (Ctor.prototype && Ctor.prototype.render) {
inst = new Ctor(props, context);
Component.call(inst, props, context);
} |
||
Component.call(inst, props, context); | ||
} | ||
else { | ||
inst = new Component(props, context); | ||
inst.constructor = Ctor; | ||
inst.render = doRender; | ||
inst['render'] = doRender; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like an unnecessary change here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem here is that You can get around this in one of two ways: type casting the This is a very common problem with dealing with expando properties and type systems. TypeScript users prefer to escape them as I have done. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would reeeally like to see if we can avoid code changes for the doc annotations. This was something I had run into in another project when trying to use checkJs. Thought: is there a way for us to use a typedef to create a ficticious There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be resolved here and in other places by changes proposed in rbiggs/preact#4, according to the first recommended alternative:
I find it interesting to note his second alternative:
|
||
} | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,9 +6,15 @@ import { unmountComponent } from './component'; | |
import options from '../options'; | ||
import { removeNode } from '../dom/index'; | ||
|
||
/** | ||
* @typedef {import('../component').Component} Component | ||
* @typedef {import('../dom/index.js').PreactElement} PreactElement | ||
* @typedef {import('../vnode').VNode} VNode | ||
*/ | ||
|
||
/** | ||
* Queue of components that have been mounted and are awaiting componentDidMount | ||
* @type {Array<import('../component').Component>} | ||
* @type {Array<Component>} | ||
*/ | ||
export const mounts = []; | ||
|
||
|
@@ -33,21 +39,23 @@ export function flushMounts() { | |
|
||
/** | ||
* Apply differences in a given vnode (and it's deep children) to a real DOM Node. | ||
* @param {import('../dom').PreactElement} dom A DOM node to mutate into the shape of a `vnode` | ||
* @param {import('../vnode').VNode} vnode A VNode (with descendants forming a tree) representing | ||
* @param {PreactElement} dom A DOM node to mutate into the shape of a `vnode` | ||
* @param {VNode} vnode A VNode (with descendants forming a tree) representing | ||
* the desired DOM structure | ||
* @param {object} context The current context | ||
* @param {boolean} mountAll Whether or not to immediately mount all components | ||
* @param {Element} parent ? | ||
* @param {boolean} componentRoot ? | ||
* @returns {import('../dom').PreactElement} The created/mutated element | ||
* @returns {PreactElement} The created/mutated element | ||
* @private | ||
*/ | ||
export function diff(dom, vnode, context, mountAll, parent, componentRoot) { | ||
// diffLevel having been 0 here indicates initial entry into the diff (not a subdiff) | ||
if (!diffLevel++) { | ||
// when first starting the diff, check if we're diffing an SVG or within an SVG | ||
isSvgMode = parent!=null && parent.ownerSVGElement!==undefined; | ||
isSvgMode = | ||
parent != null && | ||
/** @type{SVGElement} */(parent).ownerSVGElement !== undefined; | ||
|
||
// hydration is indicated by the existing element to be diffed not having a prop cache | ||
hydrating = dom!=null && !(ATTR_KEY in dom); | ||
|
@@ -71,8 +79,8 @@ export function diff(dom, vnode, context, mountAll, parent, componentRoot) { | |
|
||
/** | ||
* Internals of `diff()`, separated to allow bypassing diffLevel / mount flushing. | ||
* @param {import('../dom').PreactElement} dom A DOM node to mutate into the shape of a `vnode` | ||
* @param {import('../vnode').VNode} vnode A VNode (with descendants forming a tree) representing the desired DOM structure | ||
* @param {PreactElement} dom A DOM node to mutate into the shape of a `vnode` | ||
* @param {VNode} vnode A VNode (with descendants forming a tree) representing the desired DOM structure | ||
* @param {object} context The current context | ||
* @param {boolean} mountAll Whether or not to immediately mount all components | ||
* @param {boolean} [componentRoot] ? | ||
|
@@ -83,22 +91,23 @@ function idiff(dom, vnode, context, mountAll, componentRoot) { | |
prevSvgMode = isSvgMode; | ||
|
||
// empty values (null, undefined, booleans) render as empty Text nodes | ||
if (vnode==null || typeof vnode==='boolean') vnode = ''; | ||
if (vnode == null || typeof vnode === 'boolean') | ||
/** @type {any} */(vnode) = ''; | ||
|
||
|
||
// Fast case: Strings & Numbers create/update Text nodes. | ||
if (typeof vnode==='string' || typeof vnode==='number') { | ||
|
||
// update if it's already a Text node: | ||
if (dom && dom.splitText!==undefined && dom.parentNode && (!dom._component || componentRoot)) { | ||
if (dom && dom['splitText']!==undefined && dom.parentNode && (!dom._component || componentRoot)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like an unnecessary change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the same problem as with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should also be resolved by changes proposed in rbiggs/preact#4 |
||
/* istanbul ignore if */ /* Browser quirk that can't be covered: https://github.com/developit/preact/commit/fd4f21f5c45dfd75151bd27b4c217d8003aa5eb9 */ | ||
if (dom.nodeValue!=vnode) { | ||
dom.nodeValue = vnode; | ||
} | ||
} | ||
else { | ||
// it wasn't a Text node: replace it with one and recycle the old Element | ||
out = document.createTextNode(vnode); | ||
/** @type {any} */(out) = document.createTextNode(vnode); | ||
if (dom) { | ||
if (dom.parentNode) dom.parentNode.replaceChild(out, dom); | ||
recollectNodeTree(dom, true); | ||
|
@@ -150,14 +159,17 @@ function idiff(dom, vnode, context, mountAll, componentRoot) { | |
} | ||
|
||
// Optimization: fast-path for elements containing a single TextNode: | ||
if (!hydrating && vchildren && vchildren.length===1 && typeof vchildren[0]==='string' && fc!=null && fc.splitText!==undefined && fc.nextSibling==null) { | ||
if (!hydrating && vchildren && vchildren.length===1 && typeof vchildren[0]==='string' && fc!=null && fc['splitText']!==undefined && fc.nextSibling==null) { | ||
if (fc.nodeValue!=vchildren[0]) { | ||
fc.nodeValue = vchildren[0]; | ||
fc.nodeValue = | ||
/** @type {string} */(vchildren[0]); | ||
} | ||
} | ||
// otherwise, if there are existing or new children, diff them: | ||
else if (vchildren && vchildren.length || fc!=null) { | ||
innerDiffNode(out, vchildren, context, mountAll, hydrating || props.dangerouslySetInnerHTML!=null); | ||
innerDiffNode(out, | ||
/** @type {VNode[]} */(vchildren), | ||
context, mountAll, hydrating || props.dangerouslySetInnerHTML!=null); | ||
} | ||
|
||
|
||
|
@@ -174,8 +186,8 @@ function idiff(dom, vnode, context, mountAll, componentRoot) { | |
|
||
/** | ||
* Apply child and attribute changes between a VNode and a DOM Node to the DOM. | ||
* @param {import('../dom').PreactElement} dom Element whose children should be compared & mutated | ||
* @param {Array<import('../vnode').VNode>} vchildren Array of VNodes to compare to `dom.childNodes` | ||
* @param {PreactElement} dom Element whose children should be compared & mutated | ||
* @param {Array<VNode>} vchildren Array of VNodes to compare to `dom.childNodes` | ||
* @param {object} context Implicitly descendant context object (from most | ||
* recent `getChildContext()`) | ||
* @param {boolean} mountAll Whether or not to immediately mount all components | ||
|
@@ -198,12 +210,12 @@ function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) { | |
for (let i=0; i<len; i++) { | ||
let child = originalChildren[i], | ||
props = child[ATTR_KEY], | ||
key = vlen && props ? child._component ? child._component.__key : props.key : null; | ||
key = vlen && props ? child['_component'] ? child['_component'].__key : props.key : null; | ||
if (key!=null) { | ||
keyedLen++; | ||
keyed[key] = child; | ||
} | ||
else if (props || (child.splitText!==undefined ? (isHydrating ? child.nodeValue.trim() : true) : isHydrating)) { | ||
else if (props || (child['splitText']!==undefined ? (isHydrating ? child.nodeValue.trim() : true) : isHydrating)) { | ||
children[childrenLen++] = child; | ||
} | ||
} | ||
|
@@ -226,7 +238,8 @@ function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) { | |
// attempt to pluck a node of the same type from the existing children | ||
else if (min<childrenLen) { | ||
for (j=min; j<childrenLen; j++) { | ||
if (children[j]!==undefined && isSameNodeType(c = children[j], vchild, isHydrating)) { | ||
if (children[j] !== undefined && | ||
isSameNodeType(c = /** @type {any[]} */(children)[j], vchild, isHydrating)) { | ||
child = c; | ||
children[j] = undefined; | ||
if (j===childrenLen-1) childrenLen--; | ||
|
@@ -262,15 +275,16 @@ function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) { | |
|
||
// remove orphaned unkeyed children: | ||
while (min<=childrenLen) { | ||
if ((child = children[childrenLen--])!==undefined) recollectNodeTree(child, false); | ||
if ((child = children[childrenLen--]) !== undefined) | ||
recollectNodeTree(/** @type { any } */(child), false); | ||
} | ||
} | ||
|
||
|
||
|
||
/** | ||
* Recursively recycle (or just unmount) a node and its descendants. | ||
* @param {import('../dom').PreactElement} node DOM node to start | ||
* @param {PreactElement} node DOM node to start | ||
* unmount/removal from | ||
* @param {boolean} [unmountOnly=false] If `true`, only triggers unmount | ||
* lifecycle, skips removal | ||
|
@@ -312,7 +326,7 @@ export function removeChildren(node) { | |
|
||
/** | ||
* Apply differences in attributes from a VNode to the given DOM Element. | ||
* @param {import('../dom').PreactElement} dom Element with attributes to diff `attrs` against | ||
* @param {PreactElement} dom Element with attributes to diff `attrs` against | ||
* @param {object} attrs The desired end-state key-value attribute pairs | ||
* @param {object} old Current/previous attributes (from previous VNode or | ||
* element's prop cache) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
// @ts-nocheck | ||
// Prevent warning of using VNode before declaration in defnition of children property. | ||
/** | ||
* Virtual DOM Node | ||
* @typedef VNode | ||
* @property {string | function} nodeName The string of the DOM node to create or Component constructor to render | ||
* @property {Array<VNode | string>} children The children of node | ||
* @property {string | number | undefined} key The key used to identify this VNode in a list | ||
* @property {object} attributes The properties of this VNode | ||
* @property {Object.<string, any>} attributes The properties of this VNode | ||
*/ | ||
export const VNode = function VNode() {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohh I like this one! Totally forgot about
typedef
👍