diff --git a/package-lock.json b/package-lock.json
index bdf963e8f412e..4df9fa4af3e85 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10139,6 +10139,15 @@
"@types/react": "*"
}
},
+ "@types/react-dom": {
+ "version": "16.9.5",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.5.tgz",
+ "integrity": "sha512-BX6RQ8s9D+2/gDhxrj8OW+YD4R+8hj7FEM/OJHGNR0KipE1h1mSsf39YeyC81qafkq+N3rU3h3RFbLSwE5VqUg==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
"@types/react-syntax-highlighter": {
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.2.tgz",
diff --git a/package.json b/package.json
index 9f9c0443ef296..1430c67a12d54 100644
--- a/package.json
+++ b/package.json
@@ -91,6 +91,7 @@
"@types/lodash": "4.14.149",
"@types/prettier": "1.19.0",
"@types/qs": "6.9.1",
+ "@types/react-dom": "16.9.5",
"@types/requestidlecallback": "0.3.1",
"@types/sprintf-js": "1.1.2",
"@wordpress/babel-plugin-import-jsx-pragma": "file:packages/babel-plugin-import-jsx-pragma",
diff --git a/packages/element/CHANGELOG.md b/packages/element/CHANGELOG.md
index 6fa9e71bbb959..2db200bde4fb9 100644
--- a/packages/element/CHANGELOG.md
+++ b/packages/element/CHANGELOG.md
@@ -1,5 +1,6 @@
## Master
+- Include TypeScript type declarations ([#21248](https://github.com/WordPress/gutenberg/pull/21248))
- Graduated `__experimentalCreateInterpolateElement` function to stable api: `createInterpolateElement` (see [20699](https://github.com/WordPress/gutenberg/pull/20699))
## 2.10.0 (2019-12-19)
diff --git a/packages/element/README.md b/packages/element/README.md
index 1a51dea4046de..c1f819f6116f5 100755
--- a/packages/element/README.md
+++ b/packages/element/README.md
@@ -180,7 +180,7 @@ _Related_
_Parameters_
-- _child_ `WPElement`: Any renderable child, such as an element, string, or fragment.
+- _child_ (unknown type): Any renderable child, such as an element, string, or fragment.
- _container_ `HTMLElement`: DOM node into which element should be rendered.
# **createRef**
@@ -199,7 +199,7 @@ Finds the dom node of a React component.
_Parameters_
-- _component_ `WPComponent`: Component's instance.
+- _component_ (unknown type): Component's instance.
# **forwardRef**
@@ -289,13 +289,11 @@ aside from `children` are passed.
_Parameters_
-- _props_ `Object`:
-- _props.children_ `string`: HTML to render.
-- _props.props_ `Object`: Any additonal props to be set on the containing div.
+- _props_ `RawHTMLProps`: Children should be a string of HTML. Other props will be passed through to div wrapper.
_Returns_
-- `WPComponent`: Dangerously-rendering component.
+- `JSX.Element`: Dangerously-rendering component.
# **render**
@@ -303,7 +301,7 @@ Renders a given element into the target DOM node.
_Parameters_
-- _element_ `WPElement`: Element to render.
+- _element_ (unknown type): Element to render.
- _target_ `HTMLElement`: DOM node into which element should be rendered.
# **renderToString**
@@ -312,9 +310,9 @@ Serializes a React element to string.
_Parameters_
-- _element_ `WPElement`: Element to serialize.
-- _context_ `?Object`: Context object.
-- _legacyContext_ `?Object`: Legacy context object.
+- _element_ (unknown type): Element to serialize.
+- _context_ `[Object]`: Context object.
+- _legacyContext_ `[Object]`: Legacy context object.
_Returns_
diff --git a/packages/element/package.json b/packages/element/package.json
index 0b54d5538af07..28cf5b051987a 100644
--- a/packages/element/package.json
+++ b/packages/element/package.json
@@ -21,6 +21,7 @@
"main": "build/index.js",
"module": "build-module/index.js",
"react-native": "src/index",
+ "types": "build-types",
"sideEffects": false,
"dependencies": {
"@babel/runtime": "^7.8.3",
diff --git a/packages/element/src/create-interpolate-element.js b/packages/element/src/create-interpolate-element.js
index e6d0eefdd3bfa..d3da32354bb11 100644
--- a/packages/element/src/create-interpolate-element.js
+++ b/packages/element/src/create-interpolate-element.js
@@ -3,6 +3,8 @@
*/
import { createElement, cloneElement, Fragment, isValidElement } from 'react';
+/** @typedef {import('./react').WPElement} WPElement */
+
let indoc, offset, output, stack;
/**
@@ -22,6 +24,24 @@ let indoc, offset, output, stack;
*/
const tokenizer = /<(\/)?(\w+)\s*(\/)?>/g;
+/**
+ * The stack frame tracking parse progress.
+ *
+ * @typedef Frame
+ *
+ * @property {WPElement} element A parent element which may still have
+ * @property {number} tokenStart Offset at which parent element first
+ * appears.
+ * @property {number} tokenLength Length of string marking start of parent
+ * element.
+ * @property {number} [prevOffset] Running offset at which parsing should
+ * continue.
+ * @property {number} [leadingTextStart] Offset at which last closing element
+ * finished, used for finding text between
+ * elements.
+ * @property {WPElement[]} children Children.
+ */
+
/**
* Tracks recursive-descent parse state.
*
@@ -29,21 +49,21 @@ const tokenizer = /<(\/)?(\w+)\s*(\/)?>/g;
* parsed.
*
* @private
- * @param {WPElement} element A parent element which may still have
- * nested children not yet parsed.
- * @param {number} tokenStart Offset at which parent element first
- * appears.
- * @param {number} tokenLength Length of string marking start of parent
- * element.
- * @param {number} prevOffset Running offset at which parsing should
- * continue.
- * @param {number} leadingTextStart Offset at which last closing element
- * finished, used for finding text between
- * elements
+ * @param {WPElement} element A parent element which may still have
+ * nested children not yet parsed.
+ * @param {number} tokenStart Offset at which parent element first
+ * appears.
+ * @param {number} tokenLength Length of string marking start of parent
+ * element.
+ * @param {number} [prevOffset] Running offset at which parsing should
+ * continue.
+ * @param {number} [leadingTextStart] Offset at which last closing element
+ * finished, used for finding text between
+ * elements.
*
* @return {Frame} The stack frame tracking parse progress.
*/
-function Frame(
+function createFrame(
element,
tokenStart,
tokenLength,
@@ -175,14 +195,14 @@ function proceed( conversionMap ) {
// otherwise we found an inner element
addChild(
- new Frame( conversionMap[ name ], startOffset, tokenLength )
+ createFrame( conversionMap[ name ], startOffset, tokenLength )
);
offset = startOffset + tokenLength;
return true;
case 'opener':
stack.push(
- new Frame(
+ createFrame(
conversionMap[ name ],
startOffset,
tokenLength,
@@ -210,7 +230,7 @@ function proceed( conversionMap ) {
);
stackTop.children.push( text );
stackTop.prevOffset = startOffset + tokenLength;
- const frame = new Frame(
+ const frame = createFrame(
stackTop.element,
stackTop.tokenStart,
stackTop.tokenLength,
diff --git a/packages/element/src/raw-html.js b/packages/element/src/raw-html.js
index 0f56060e3057b..f75a9ff66e4fc 100644
--- a/packages/element/src/raw-html.js
+++ b/packages/element/src/raw-html.js
@@ -3,17 +3,21 @@
*/
import { createElement } from './react';
+// Disable reason: JSDoc linter doesn't seem to parse the union (`&`) correctly.
+/* eslint-disable jsdoc/valid-types */
+/** @typedef {{children: string} & import('react').ComponentPropsWithoutRef<'div'>} RawHTMLProps */
+/* eslint-enable jsdoc/valid-types */
+
/**
* Component used as equivalent of Fragment with unescaped HTML, in cases where
* it is desirable to render dangerous HTML without needing a wrapper element.
* To preserve additional props, a `div` wrapper _will_ be created if any props
* aside from `children` are passed.
*
- * @param {Object} props
- * @param {string} props.children HTML to render.
- * @param {Object} props.props Any additonal props to be set on the containing div.
+ * @param {RawHTMLProps} props Children should be a string of HTML. Other props
+ * will be passed through to div wrapper.
*
- * @return {WPComponent} Dangerously-rendering component.
+ * @return {JSX.Element} Dangerously-rendering component.
*/
export default function RawHTML( { children, ...props } ) {
// The DIV wrapper will be stripped by serializer, unless there are
diff --git a/packages/element/src/react-platform.js b/packages/element/src/react-platform.js
index 44a3422ed7977..fe3d3e94a9994 100644
--- a/packages/element/src/react-platform.js
+++ b/packages/element/src/react-platform.js
@@ -13,7 +13,7 @@ import {
*
* @see https://github.com/facebook/react/issues/10309#issuecomment-318433235
*
- * @param {WPElement} child Any renderable child, such as an element,
+ * @param {import('./react').WPElement} child Any renderable child, such as an element,
* string, or fragment.
* @param {HTMLElement} container DOM node into which element should be rendered.
*/
@@ -22,14 +22,14 @@ export { createPortal };
/**
* Finds the dom node of a React component.
*
- * @param {WPComponent} component Component's instance.
+ * @param {import('./react').WPComponent} component Component's instance.
*/
export { findDOMNode };
/**
* Renders a given element into the target DOM node.
*
- * @param {WPElement} element Element to render.
+ * @param {import('./react').WPElement} element Element to render.
* @param {HTMLElement} target DOM node into which element should be rendered.
*/
export { render };
diff --git a/packages/element/src/react.js b/packages/element/src/react.js
index 449b799ef74f8..78752a1b5a179 100644
--- a/packages/element/src/react.js
+++ b/packages/element/src/react.js
@@ -37,7 +37,7 @@ import { isString } from 'lodash';
/**
* Object containing a React component.
*
- * @typedef {import('react').Component} WPComponent
+ * @typedef {import('react').ComponentType} WPComponent
*/
/**
diff --git a/packages/element/src/serialize.js b/packages/element/src/serialize.js
index d5ef4e02a7758..172657812533e 100644
--- a/packages/element/src/serialize.js
+++ b/packages/element/src/serialize.js
@@ -52,7 +52,9 @@ import {
import { createContext, Fragment, StrictMode, forwardRef } from './react';
import RawHTML from './raw-html';
-const { Provider, Consumer } = createContext();
+/** @typedef {import('./react').WPElement} WPElement */
+
+const { Provider, Consumer } = createContext( undefined );
const ForwardRef = forwardRef( () => {
return null;
} );
@@ -60,14 +62,14 @@ const ForwardRef = forwardRef( () => {
/**
* Valid attribute types.
*
- * @type {Set}
+ * @type {Set}
*/
const ATTRIBUTES_TYPES = new Set( [ 'string', 'boolean', 'number' ] );
/**
* Element tags which can be self-closing.
*
- * @type {Set}
+ * @type {Set}
*/
const SELF_CLOSING_TAGS = new Set( [
'area',
@@ -101,7 +103,7 @@ const SELF_CLOSING_TAGS = new Set( [
* [ tr.firstChild.textContent.trim() ]: true
* } ), {} ) ).sort();
*
- * @type {Set}
+ * @type {Set}
*/
const BOOLEAN_ATTRIBUTES = new Set( [
'allowfullscreen',
@@ -152,7 +154,7 @@ const BOOLEAN_ATTRIBUTES = new Set( [
*
* - `alt`: https://blog.whatwg.org/omit-alt
*
- * @type {Set}
+ * @type {Set}
*/
const ENUMERATED_ATTRIBUTES = new Set( [
'autocapitalize',
@@ -195,7 +197,7 @@ const ENUMERATED_ATTRIBUTES = new Set( [
* .map( ( [ key ] ) => key )
* .sort();
*
- * @type {Set}
+ * @type {Set}
*/
const CSS_PROPERTIES_SUPPORTS_UNITLESS = new Set( [
'animation',
@@ -269,7 +271,7 @@ function isInternalAttribute( attribute ) {
* @param {string} attribute Attribute name.
* @param {*} value Non-normalized attribute value.
*
- * @return {string} Normalized attribute value.
+ * @return {*} Normalized attribute value.
*/
function getNormalAttributeValue( attribute, value ) {
switch ( attribute ) {
@@ -346,9 +348,9 @@ function getNormalStylePropertyValue( property, value ) {
/**
* Serializes a React element to string.
*
- * @param {WPElement} element Element to serialize.
- * @param {?Object} context Context object.
- * @param {?Object} legacyContext Legacy context object.
+ * @param {import('react').ReactNode} element Element to serialize.
+ * @param {Object} [context] Context object.
+ * @param {Object} [legacyContext] Legacy context object.
*
* @return {string} Serialized element.
*/
@@ -369,7 +371,10 @@ export function renderElement( element, context, legacyContext = {} ) {
return element.toString();
}
- const { type, props } = element;
+ const {
+ type,
+ props,
+ } = /** @type {{type?: any, props?: any}} */ ( element );
switch ( type ) {
case StrictMode:
@@ -434,11 +439,11 @@ export function renderElement( element, context, legacyContext = {} ) {
/**
* Serializes a native component type to string.
*
- * @param {?string} type Native component type to serialize, or null if
- * rendering as fragment of children content.
- * @param {Object} props Props object.
- * @param {?Object} context Context object.
- * @param {?Object} legacyContext Legacy context object.
+ * @param {?string} type Native component type to serialize, or null if
+ * rendering as fragment of children content.
+ * @param {Object} props Props object.
+ * @param {Object} [context] Context object.
+ * @param {Object} [legacyContext] Legacy context object.
*
* @return {string} Serialized element.
*/
@@ -478,13 +483,15 @@ export function renderNativeComponent(
return '<' + type + attributes + '>' + content + '' + type + '>';
}
+/** @typedef {import('./react').WPComponent} WPComponent */
+
/**
* Serializes a non-native component type to string.
*
- * @param {Function} Component Component type to serialize.
- * @param {Object} props Props object.
- * @param {?Object} context Context object.
- * @param {?Object} legacyContext Legacy context object.
+ * @param {WPComponent} Component Component type to serialize.
+ * @param {Object} props Props object.
+ * @param {Object} [context] Context object.
+ * @param {Object} [legacyContext] Legacy context object.
*
* @return {string} Serialized element
*/
@@ -494,10 +501,22 @@ export function renderComponent(
context,
legacyContext = {}
) {
- const instance = new Component( props, legacyContext );
+ const instance = new /** @type {import('react').ComponentClass} */ ( Component )(
+ props,
+ legacyContext
+ );
- if ( typeof instance.getChildContext === 'function' ) {
- Object.assign( legacyContext, instance.getChildContext() );
+ if (
+ typeof (
+ // Ignore reason: Current prettier reformats parens and mangles type assertion
+ // prettier-ignore
+ /** @type {{getChildContext?: () => unknown}} */ ( instance ).getChildContext
+ ) === 'function'
+ ) {
+ Object.assign(
+ legacyContext,
+ /** @type {{getChildContext?: () => unknown}} */ ( instance ).getChildContext()
+ );
}
const html = renderElement( instance.render(), context, legacyContext );
@@ -508,9 +527,9 @@ export function renderComponent(
/**
* Serializes an array of children to string.
*
- * @param {Array} children Children to serialize.
- * @param {?Object} context Context object.
- * @param {?Object} legacyContext Legacy context object.
+ * @param {import('react').ReactNodeArray} children Children to serialize.
+ * @param {Object} [context] Context object.
+ * @param {Object} [legacyContext] Legacy context object.
*
* @return {string} Serialized children.
*/
diff --git a/packages/element/tsconfig.json b/packages/element/tsconfig.json
new file mode 100644
index 0000000000000..1f90b9c762ae6
--- /dev/null
+++ b/packages/element/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "declarationDir": "build-types",
+
+ "noImplicitAny": false,
+ "strictNullChecks": false
+ },
+ "references": [ { "path": "../escape-html" } ],
+ "include": [ "src/**/*" ]
+}
diff --git a/packages/eslint-plugin/configs/jsdoc.js b/packages/eslint-plugin/configs/jsdoc.js
index 8bff4a36b7d32..71a97abda2fd0 100644
--- a/packages/eslint-plugin/configs/jsdoc.js
+++ b/packages/eslint-plugin/configs/jsdoc.js
@@ -59,6 +59,8 @@ const typescriptUtilityTypes = [
'Required',
'ReturnType',
'ThisType',
+ 'unknown',
+ 'never',
];
module.exports = {
diff --git a/tsconfig.json b/tsconfig.json
index 7cdae4ac9a188..f2d19a1874a85 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -5,6 +5,7 @@
{ "path": "packages/autop" },
{ "path": "packages/blob" },
{ "path": "packages/block-editor" },
+ { "path": "packages/element" },
{ "path": "packages/dom-ready" },
{ "path": "packages/escape-html" },
{ "path": "packages/html-entities" },