Using JSDoc to enable intellisense for render props in vscode
strongly typed render props in vscode intellisense without typescript
Setup
We have a react codebase where we have many React components with the render props pattern. In this codebase we haven’t enabled typescript (yet), and we generally use prop-types for dynamic type checking. We use vscode for development and take advantage of its intellisense / autocompletion in our dev workflow.
About vscode intellisense
vscode uses typescript internally, and in most cases, it can automatically infer types from type-definitions, JSDoc comments or proptypes. For function components, vscode can offer code completion suggestions by default, and if you have proptypes for the function component, it can infer the type of the props as well.
Case 1: Function component without prop destructuring, and without prop types
- vscode shows the whole props object as
any
on hovering over the usage
- vscode does not provide any autocompletion when adding props in the usage
Case 2: Function component without prop destructuring, with prop types
- vscode shows the whole props object as
any
on hovering over the usage
- vscode provides autocompletion hints, as well as types while adding props in the usage
Case 3: Function component with prop destructuruing and prop types
- vscode shows the destructured props object when hovering over the usage (though the object properties are still shown as
any
)
- vscode provides autocompletion hints, as well as types while adding props in the usage
Case 4: Class component
- vscode is not able to infer props for Class components, with or without prop types
NOTE:
- vscode still shows prop types as
any
on hover in all the cases - vscode shows all props as optional while hovdering as well as while autocomplete.
The problem
We will take an example of a toggle component with the render props pattern. This component calls the children function passing the open
value, so that the parent can render whatever they want the toggle is open.
Although vscode can infer types from prop types in basic cases, it can’t do that effectively when the prop is a function.
This is because:
- it is not easy to specify a strongly typed function prop using prop types. You can’t specify the function params or return type in prop types
import React, { useState } from "react";
import PropTypes from "prop-types";// This does not work with autocomplete,
// as there is no easy way for vscode to infer
// the type of the children functionfunction Toggle(props) {
const [open, setOpen] = useState(false);
return (
<div>
<button type="button" onClick={() => setOpen(!open)}>
{" "}
Toggle
{" "}
</button>
{props.children({ open })}
</div>
);
}Toggle.propTypes = {
children: PropTypes.func.isRequired
};export default Toggle;
- when using custom validators in prop types for making sure that the prop adheres to a certain interface, vscode can’t correctly infer the function params and return types.
import React, { useState } from "react";
import PropTypes from "prop-types";// This does not work with autocomplete,
// as there is no easy way for vscode to infer
// the type of the children functionfunction Toggle(props) {
const [open, setOpen] = useState(false);
return (
<div>
<button type="button" onClick={() => setOpen(!open)}>
{" "}
Toggle
{" "}
</button>
{props.children({ open })}
</div>
);
}
Toggle.propTypes = {
children: (props, propName, componentName) => {
let error;
const prop = props[propName];
if (typeof prop !== "function") {
error = new Error("expected children to be a function");
} if (prop.length === 0) {
error = new Error("expected children to be a function with 1 argument");
} // ...don't know how to verify further
return error;
}
};export default Toggle;
As you would know, children as function is the basis of the render props patten. Since we are not able to specify the prop type of the children function correctly using prop types, vscode can’t guess it and can not suggest correct types at the point of usage. This leads to a lot of to-and-fro in the usage vs definition, just to figure out what to expect from the render props children function.
The solution
Enter JSDoc. As mentioned earlier, vscode includes JSDoc comments, if available, in its type inference. So, although we can’t specify the exact type of the children function using prop types, we can still annotate it using JSDoc, and vscode would be able to suggest the types at the point of usage.
import React, { useState } from "react";
import PropTypes from "prop-types";// This works with autocomplete,
// as vscode can infer
// the type of the children function
// based on the jsdocs/**
* @callback ChildrenFn
* @param {{ open: boolean }} props - whether the toggle is open or not
* @returns {React.ElementType}
*//**
* @param {Object} props
* @param {ChildrenFn} props.children
*/function Toggle(props) {
const [open, setOpen] = useState(false);
return (
<div>
<button type="button" onClick={() => setOpen(!open)}>
{" "}
Toggle
{" "}
</button>
{props.children({ open })}
</div>
);
}Toggle.propTypes = {
children: PropTypes.func.isRequired
};export default Toggle;