Skip to content
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

report(tsc): infer createElement type from tag name #6637

Merged
merged 2 commits into from
Nov 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions lighthouse-core/report/html/renderer/details-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class DetailsRenderer {
displayedPath = url;
}

const element = /** @type {HTMLElement} */ (this._dom.createElement('div', 'lh-text__url'));
const element = this._dom.createElement('div', 'lh-text__url');
element.appendChild(this._renderText({
value: displayedPath,
}));
Expand Down Expand Up @@ -161,7 +161,7 @@ class DetailsRenderer {
});
}

const a = /** @type {HTMLAnchorElement} */ (this._dom.createElement('a'));
const a = this._dom.createElement('a');
a.rel = 'noopener';
a.target = '_blank';
a.textContent = details.text;
Expand Down Expand Up @@ -197,7 +197,7 @@ class DetailsRenderer {
* @return {Element}
*/
_renderThumbnail(details) {
const element = /** @type {HTMLImageElement}*/ (this._dom.createElement('img', 'lh-thumbnail'));
const element = this._dom.createElement('img', 'lh-thumbnail');
const strValue = details.value;
element.src = strValue;
element.title = strValue;
Expand Down Expand Up @@ -346,7 +346,7 @@ class DetailsRenderer {
* @protected
*/
renderNode(item) {
const element = /** @type {HTMLSpanElement} */ (this._dom.createElement('span', 'lh-node'));
const element = this._dom.createElement('span', 'lh-node');
if (item.snippet) {
element.textContent = item.snippet;
}
Expand Down
17 changes: 10 additions & 7 deletions lighthouse-core/report/html/renderer/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

/* globals URL self */

/** @typedef {HTMLElementTagNameMap & {[id: string]: HTMLElement}} HTMLElmentByTagName */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTMLElementTagNameMap is the built-in you're talking about?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTMLElementTagNameMap is the built-in you're talking about?

yes


class DOM {
/**
* @param {Document} document
Expand All @@ -27,14 +29,14 @@ class DOM {
this._document = document;
}

// TODO(bckenny): can pass along `createElement`'s inferred type
/**
* @param {string} name
* @template {string} T
* @param {T} name
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we type this as the keys of the HTMLElementTagNameMap?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we type this as the keys of the HTMLElementTagNameMap?

We could, though it would eliminate the fallback to the generic HTMLElement when a tag name isn't recognized. Maybe we're ok with that in exchange for better completion/stricter checking?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would we still get helpful completion if it were strict options | string? :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strict options | string

sadly no, it's smart about it and coalesces to just string.

Copy link
Member Author

@brendankenny brendankenny Nov 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I tried to use just HTMLElementTagNameMap, and we have one place that's unrecognized: 'summary' in

const summaryEl = this.dom.createChildOf(groupEl, expandable ? 'summary' : 'div');

When Edge gets <details> support I imagine that they'll also support <summary> and their WebIDL will get updated and so the tsc types will get updated (I'm surprised it already has 'details' support, honestly), but until then we probably just want to go with how this PR is now.

* @param {string=} className
* @param {Object<string, (string|undefined)>=} attrs Attribute key/val pairs.
* Note: if an attribute key has an undefined value, this method does not
* set the attribute on the node.
* @return {Element}
* @return {HTMLElmentByTagName[T]}
*/
createElement(name, className, attrs = {}) {
const element = this._document.createElement(name);
Expand All @@ -58,13 +60,14 @@ class DOM {
}

/**
* @template {string} T
* @param {Element} parentElem
* @param {string} elementName
* @param {T} elementName
* @param {string=} className
* @param {Object<string, (string|undefined)>=} attrs Attribute key/val pairs.
* Note: if an attribute key has an undefined value, this method does not
* set the attribute on the node.
* @return {Element}
* @return {HTMLElmentByTagName[T]}
*/
createChildOf(parentElem, elementName, className, attrs) {
const element = this.createElement(elementName, className, attrs);
Expand Down Expand Up @@ -122,7 +125,7 @@ class DOM {

// Append link if there are any.
if (linkText && linkHref) {
const a = /** @type {HTMLAnchorElement} */ (this.createElement('a'));
const a = this.createElement('a');
a.rel = 'noopener';
a.target = '_blank';
a.textContent = linkText;
Expand All @@ -147,7 +150,7 @@ class DOM {
const [preambleText, codeText] = parts.splice(0, 2);
element.appendChild(this._document.createTextNode(preambleText));
if (codeText) {
const pre = /** @type {HTMLPreElement} */ (this.createElement('code'));
const pre = this.createElement('code');
pre.textContent = codeText;
element.appendChild(pre);
}
Expand Down
3 changes: 2 additions & 1 deletion lighthouse-core/report/html/renderer/report-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class ReportRenderer {
/**
* @param {LH.Result} result
* @param {Element} container Parent element to render the report into.
* @return {Element}
*/
renderReport(result, container) {
// Mutate the UIStrings if necessary (while saving originals)
Expand All @@ -55,7 +56,7 @@ class ReportRenderer {
// put the UIStrings back into original state
Util.updateAllUIStrings(originalUIStrings);

return /** @type {Element} **/ (container);
return container;
}

/**
Expand Down
5 changes: 2 additions & 3 deletions lighthouse-core/report/html/renderer/report-ui-features.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,7 @@ class ReportUIFeatures {
// load event, however it is cross-domain and won't fire. Instead, listen
// for a message from the target app saying "I'm open".
const json = reportJson;
window.addEventListener('message', function msgHandler(/** @type {Event} */ e) {
const messageEvent = /** @type {MessageEvent} */ (e);
Copy link
Member Author

@brendankenny brendankenny Nov 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no idea what was going on here. Maybe fixed on the compiler/built-in defs side so it was not only inferred, but inferred as a MessageEvent.

window.addEventListener('message', function msgHandler(messageEvent) {
if (messageEvent.origin !== VIEWER_ORIGIN) {
return;
}
Expand Down Expand Up @@ -469,7 +468,7 @@ class ReportUIFeatures {
const ext = blob.type.match('json') ? '.json' : '.html';
const href = URL.createObjectURL(blob);

const a = /** @type {HTMLAnchorElement} */ (this._dom.createElement('a'));
const a = this._dom.createElement('a');
a.download = `${filename}${ext}`;
a.href = href;
this._document.body.appendChild(a); // Firefox requires anchor to be in the DOM.
Expand Down