milesj / babel-plugin-typescript-to-proptypes Goto Github PK
View Code? Open in Web Editor NEWGenerate React PropTypes from TypeScript interfaces or type aliases.
License: MIT License
Generate React PropTypes from TypeScript interfaces or type aliases.
License: MIT License
It seems like only React.SFC
components are outputting propTypes and not React.FunctionComponent
components. The reason I'm using FunctionComponent
is because the TS types say that React.SFC
is deprecated and instructs you to use FunctionComponent
:
Input:
interface Props {
test: string;
}
export const FunctionComponent: React.FunctionComponent<Props> = () => <div />;
export const SFCComponent: React.SFC<Props> = () => <div />;
Output:
var FunctionComponent = function FunctionComponent() {
return React.createElement("div", null);
};
exports.FunctionComponent = FunctionComponent;
var SFCComponent = function SFCComponent() {
return React.createElement("div", null);
};
exports.SFCComponent = SFCComponent;
SFCComponent.propTypes = {
test: _propTypes.default.string.isRequired
};
I expect using both React.SFC
and React.FunctionComponent
to output propTypes.
if I define a string literal in a separate file from the react component, and use that string literal in the props of the react component, the resulting proptypes dont contain that key.
i.e.
heading.constants.ts
export type LEVEL = '1' | '2' | '3';
heading.ts
import { LEVEL } from './heading.constants.ts';
interface Props {
level: LEVEL,
}
export const Header = (props: Props) => {
<div>{props.level}</div>
}
dist/index.ts
...
Header.propTypes = {};
The example instructions specify using @babel/preset-react
. To produce the transform you are showing in the readme, I think it should be suggesting use of @babel/plugin-syntax-jsx
instead.. Using preset react transforms your JSX into JS.
Perhaps I am wrong but, but i had much more luck with the below.
module.exports= {
presets: ['@babel/preset-typescript'],
plugins: ['@babel/plugin-syntax-jsx',['babel-plugin-typescript-to-proptypes', { typeCheck: true }]]
}
Thanks for your work on this. I just have a question for which I didn't see an answer in the README or open/closed issues, apologies if I missed it. Would the following inline type definitions in the following example be picked up and converted to prop types?
const MyComponent = ({
title: string = 'Untitled',
count: number = 0
}) => {
return null
}
If not, would you be open to supporting it?
When I have a following prop type definition:
type onChange = {
(k: 'subject', v: string): void;
(k: 'description', v: string): void;
(k: 'canContact', v: boolean): void;
(k: 'attachments', v: $File[]): void;
}
โฆ I get an object PropType
on the component, but the correct one would be a function.
Warning: Failed prop type: Invalid prop `onChange` of type `function` supplied to `Feedback`, expected `object`.
Would you consider catching this case and generating function PropType
?
https://www.typescriptlang.org/docs/handbook/2/functions.html#call-signatures
Pretty promising lib, thanks for making it.
I'm doing my best to follow the snapshot format; happy to edit if anything is confusing or inaccurate.
Add the following fixture
import React from 'react';
interface SubProps {
b: 1 | 2 | 3;
}
export interface Props {
a: SubProps['b'];
}
export default class TypeIndexedAccess extends React.Component<Props> {
render() {
return null;
}
}
import _pt from 'prop-types';
import React from 'react';
interface SubProps {
b: 1 | 2 | 3;
}
export interface Props {
a: SubProps['b'];
}
export default class TypeIndexedAccess extends React.Component<Props> {
static propTypes = {
a: _pt.oneOf([1,2,3]).isRequired
};
render() {
return null;
}
}
import React from 'react';
interface SubProps {
b: 1 | 2 | 3;
}
export interface Props {
a: SubProps['b'];
}
export default class TypeIndexedAccess extends React.Component<Props> {
render() {
return null;
}
}
Hello!
Thank you for this great plugin!
However, is it possible to use this without Babel? I'm compiling my code with TypeScript compiler directly, and I was looking for a solution that will generate prop types from type definitions.
Thanks!
Hey there, I'm using this with yarn 2 which supports pnp. When running, I get the following error
Error: babel-plugin-typescript-to-proptypes tried to access @babel/types, but it isn't declared in its dependencies;
this makes the require call ambiguous and unsound.
Required package: @babel/types (via "@babel/types")
Required by: babel-plugin-typescript-to-proptypes@virtual
The problems is reference here: https://next.yarnpkg.com/advanced/migration#a-package-is-trying-to-access-another-package-
I believe @babel/types
should be listed in this project's dependencies.
Thanks for your work!
I've found issue with enums
. They all transformed to PropTypes.any
. I've forked the repo and added a fixture:
import React, { FC } from 'react';
export enum Color {
Green = 'green',
Red = 'red',
}
export interface Props {
color: Color,
}
const TypeAsEnum: FC<Props> = () => {
return null;
};
export default TypeAsEnum;
and the resulting snapshot is:
exports[`babel-plugin-typescript-to-proptypes transforms ./fixtures/type-as-enum.ts 1`] = `
"import _pt from 'prop-types';
import React, { FC } from 'react';
export enum Color {
Green = 'green',
Red = 'red',
}
export interface Props {
color: Color;
}
const TypeAsEnum: FC<Props> = () => {
return null;
};
TypeAsEnum.propTypes = TypeAsEnum{
color: _pt.any.isRequired
};
export default TypeAsEnum;"
`;
Do we have some workaround for this?
It would be cool if we have PropTypes.oneOf
for this.
Given this usage of Mapped Types (meaning an index signature where index strings can be members of a union of literal strings):
export type Color = "red" | "green";
export type Props {
colors: {
[key in Color]: boolean;
};
}
Then, there are no resulting prop types associated to the prop color
.
You can see it from the following failing spec:
I want some help on how to set this up with the `create-react-app --template typescript, I am not sure I am doing something wrong or it's some issue from the package itself
It looks like this plugin adds default React import, even if there are no propTypes should be added
This is an issue with Rollup when declaring React as an external module
I've created a repl example to demonstrate the issue
Could you please do something with this?
When using React.forwardRef, the proptypes are not generated.
When modules: false
is used in Babel, the prop types arent injected at all.
Example: tagName: keyof JSX.IntrinsicElements;
I'm trying to use this plugin, to get the proptypes visible in Storybook. But unfortunately, I cannot seem to start storybook with this plugin installed:
ERROR in ./src/context/withContext.tsx
Module build failed (from ./node_modules/babel-loader/lib/index.js):
TypeError: Cannot read property 'name' of null
at FunctionDeclaration (/home/smeijer/dev/myproject/node_modules/babel-plugin-typescript-to-proptypes/lib/index.js:164:57)
I'm not sure what info you need to get a better understanding of the error, but this is the webpack
config I use:
config.module.rules.push({
test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'),
options: {
presets: [[ 'react-app', { flow:false, typescript:true }]],
plugins: [['babel-plugin-typescript-to-proptypes', { comments: true }]],
},
});
config.resolve.extensions.push('.ts', '.tsx');
I'm using babel-loader: 8.0.6
in storybook: 5.3.17
At work we use create-react-app --typescript
to set up new projects, is it possible to integrate this without ejecting?
Hi, I could not figure out how to use your plugin to add propTypes to my current Typescript React components.
I followed your current description, but could not see propTypes usage in my built code.
Could you provide more details how to do that? Thanks in advance!
The following fixture receives no PropTypes
.
import React from 'react';
export default class OutOfOrder extends React.Component<Props> {
render() {
return null;
}
}
export interface Props {
b: string;
}
Moving the Props
to the top of the file does yield PropTypes
.
Here's the fixture + expected snapshot; thought that might be easier than posting in the comment.
I see in this plugin test fixtures that it supports generating propTypes from interfaces extending other interface, but only if the extended interface is in the same file.
Is it possible to do the same when the type/interface extends external imported interfaces? Or does this depend on the WIP feature typeCheck
?
import React from 'react';
import { AProps, BProps } from './types';
export interface Props extends AProps, BProps {
name: string;
}
export default function FuncExtendedInterfaces(props: Props) {
return null;
}
I react component library with configuration:
storybook, rollup, typescript, react.
components use React.forwardRef
I tried to generate propTypes for components but it did nothing. It builded without error, but component.propTypes is not generated.
I tried with React.FC instead of React.forwardRef and it works normally.
Given a union like type C = A | B
, where A
and B
are both interfaces, babel-plugin-typescript-to-prototype generates code requiring that C
contain all values present in A
and B
, rather than values present in either A
or B
. For example:
import React from 'react';
interface A {
foo: string;
}
interface B {
bar: string;
}
type C = A | B;
export const Component = (_props: C) => {
return <React.Fragment></React.Fragment>;
}
This will generate the following code:
var Component = function Component(_props) {
return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null);
};
exports.Component = Component;
Component.propTypes = {
foo: _propTypes["default"].string.isRequired,
bar: _propTypes["default"].string.isRequired
};
For a full repro, see https://github.com/ianhoffman/union-bug-repro
Given the above example, we should generate something like the following:
var Component = function Component(_props) {
return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null);
};
exports.Component = Component;
Component.propTypes = _propTypes["default"].oneOfType([
_propTypes["default"].shape({
foo: _propTypes["default"].string.isRequired,
}),
_propTypes["default"].shape({
bar: _propTypes["default"].string.isRequired
}),
]).isRequired;
Hi, thanks for all your work on this - it's an awesome plugin!
I'm running into an issue where if I have a type import from react separate from the default import like so
import React from 'react';
import type { HTMLAttributes, MouseEventHandler } from 'react';
The plugin will not output any propTypes for the component. Switching the order of the imports will fix the issue:
import type { HTMLAttributes, MouseEventHandler } from 'react';
import React from 'react';
Did a little bit of digging and it looks like the second import (the type import here) overrides the default import found from the first import here:
babel-plugin-typescript-to-proptypes/src/index.ts
Lines 133 to 137 in 25b74f3
Which ends up short circuiting the plugin execution:
babel-plugin-typescript-to-proptypes/src/index.ts
Lines 163 to 166 in 25b74f3
My first thought for a fix would be to preserve that default import from react by only assigning on L136 if state.reactImportedName
is falsy. But obviously I'm not intimately familiar with the rest of the codebase so not sure if that causes any unwanted side effects somewhere else ๐
.
Please let me know if you have any pointers for a fix - I'd be happy to put up a PR ๐.
Ran into this myself today. This prop type scrollToGroup: _pt.oneOfType([_pt.oneOf([''])]),
was crashing our builds.
Source: https://github.com/milesj/interweave/blob/master/packages/emoji-picker/src/EmojiList.tsx#L25
Looks like I need to also filter out empty strings? Man, dealing with unknown refs is getting gross.
import {
number,
string,
oneOfType,
arrayOf,
node,
bool,
func,
shape,
InferProps
} from 'prop-types'
....
export const propTypes = {
id: string,
placeholder: string,
disabled: bool,
className: string,
โ Test suite failed to run
ReferenceError: string is not defined
29 |
30 | export const propTypes = {
> 31 | id: string,
| ^
32 | placeholder: string,
33 | disabled: bool,
34 | className: string,
Consider the following component:
const Foo: React.FC<{bar: boolean}> = (props) => (
props.bar != undefined ? <h1>Full!</h1> : <h2>Empty.</h2>
);
and its usage in another component's render function:
render() {
const barProp = null;
return <Foo bar={barProp} />;
}
Now the problem is with the handling of null/undefined values by TypeScript and propTypes. There is a discrepancy - in TS, the above won't throw an error, because the value for bar
is supplied (even though it's null
). But babel-plugin-typescript-to-proptypes
will generate propTypes with isRequired
here (as bar
is not marked as optional), and React shows a warning that bar
is required but it's null
.
I don't feel marking the field in the interface as bar?: boolean
is a good approach to solve such an issue, because then <Foo />
would be a valid use of the component, but that can lead to hard-to-spot bugs where a (potentially null-valued) property wasn't passed down the component tree.
On the other hand, having warnings pop up everywhere because of this issue is not great either. Out of a lack of a better idea, I'd suggest having a configuration switch to indicate whether isRequired
should be generated at all - this would work if someone wants to just check the types of props at runtime, but not enforce their presence.
I'm using this package to have a react js copy of my react typescript components. Is it possible to control the import name?
currently its
import _pt from 'prop-types';
I would like to have control of name instead of _pt
Hi! Thanks for the plugin - its been super useful in our test setup, but I've encountered the following issue and I thought I'd raise it just in case :)
If I define the following:
interface BasicProps {
foo:bar
}
interface ConditionA extends BasicProps {
A:string
B?:string
}
interface ConditionB extends BasicProps {
A?:string
B:string
}
type AorB = ConditionA | ConditionB
I run into an issue where property A must always be set, where I was expecting AorB to require either A, or B, or both...
Thanks!
I'm trying to get this to work but the only thing it does is add import _pt from "prop-types";
to the file. It never actually converts the types to prop-types...
I've tried with a very basic example, such as:
./packages/core/src/AppProvider/index.tsx
import React from 'react';
import { Provider as StateProvider, ProviderProps as StateProviderProps } from 'react-redux';
import { ThemeProvider, ThemeProviderProps } from '@material-ui/core/styles';
import { SnackbarProvider } from 'notistack';
import CssBaseline from '@material-ui/core/CssBaseline';
interface AppProviderProps {
store: StateProviderProps['store'];
theme: ThemeProviderProps['theme'];
}
const AppProvider: React.FC<AppProviderProps> = (props) => {
const { children, store, theme } = props;
return (
<StateProvider store={store}>
<ThemeProvider theme={theme}>
<SnackbarProvider>
<React.Fragment>
<CssBaseline />
{children}
</React.Fragment>
</SnackbarProvider>
</ThemeProvider>
</StateProvider>
);
};
export default AppProvider;
export { default as createTheme } from './createTheme';
export { default as createStore } from './createStore';
export { AppProviderProps };
./babel.config.js
// checking if NODE_ENV === 'production' removed to ensure no issues with env variables
module.exports = {
presets: ['@babel/preset-typescript', '@babel/preset-react'],
plugins: ['@babel/plugin-proposal-class-properties', 'babel-plugin-typescript-to-proptypes']
};
./packages/core/package.json
{
// bunch of irrelevant package data
scripts: {
"build": "babel --config-file ../../babel.config.js ./src --out-dir ./build --extensions \".tsx,.ts\""
}
BUILD OUTPUT: ./packages/core/build/AppProvider/index.js
import _pt from "prop-types";
import React from 'react';
import { Provider as StateProvider } from 'react-redux';
import { ThemeProvider } from '@material-ui/core/styles';
import { SnackbarProvider } from 'notistack';
import CssBaseline from '@material-ui/core/CssBaseline';
const AppProvider = props => {
const {
children,
store,
theme
} = props;
return /*#__PURE__*/React.createElement(StateProvider, {
store: store
}, /*#__PURE__*/React.createElement(ThemeProvider, {
theme: theme
}, /*#__PURE__*/React.createElement(SnackbarProvider, null, /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(CssBaseline, null), children))));
};
export default AppProvider;
export { default as createTheme } from './createTheme';
export { default as createStore } from './createStore';
Hello, with latest typescript compiler versions is not needed to import react. I detected that when not adding the import the plugin does not generate the types. It makes sense as checking the code we ultimatelly rely on react being imported to move on. If not we just stop execution:
https://github.com/milesj/babel-plugin-typescript-to-proptypes/blob/master/src/index.ts#L133
I'm more than open to open a PR but I'm curious about what would be the prefered approach? I'm thinking on adding a config option like "usesReactLessFiles" and in that case we rely on other thing to perform the transpilation?
Hi there,
I've been trying to implement this in our app, and so far so good, but I noticed that the library doesn't support void function components. It's mostly a semantic/type difference.
I saw the REACT_FC_NAMES
const in the code, which made it seem as easy as adding VFC to make this functionality work.
I'd make a PR, but I was wondering if this all that needs to be done to add support for VFCs, or is there something more to consider (regarding children
, for example)?
import React from 'react';
type Props = {
foo: string,
};
// this produces proptypes
function Example(props: Props) {
return <div />
}
// and this produces proptypes
const Example: React.FC<Props> = (props) => {
return <div />
};
// but this does not produce proptypes
const Example = (props: Props) => {
return <div />
};
In my experience, the last example is the most common/intuitive way of writing a function component with types. Is there a technical reason why that case is handled differently than the other two?
The README documents this pretty well, but from a naive user's perspective it seems like an arbitrary distinction. I think it's worthwhile to support this pattern if possible.
Hi !
First of all, thank you for that promissing package.
I spotted an issue when using arrays.
You can reproduce it with the following code :
import React from 'react';
interface KeyMapProps {
stringArray: string[];
stringArray2: Array<string>;
}
function Foo({ stringArray, stringArray2 }: KeyMapProps) {
}
The output will be:
function Foo({
stringArray,
stringArray2
}) {
Foo.propTypes = {
stringArray: _propTypes.default.arrayOf(_propTypes.default.string).isRequired,
stringArray2: _propTypes.default.any.isRequired
};
stringArray2 output should be _propTypes.default.arrayOf(_propTypes.default.string).isRequired
too
The issue is with all types in arrays (not only string).
According to the typescript documentation, both writings are OK.
I saw that you rely on isTSArrayType
from @babel/core
package but I don't know if it's a babel issue or an issue in your package.
Thanks
Due to #8 and the plugin not being able to read/parse external types:
type FooProps = {
value: ExternalType["foo"] | null;
};
const Foo: FC<FooProps> = props => (<div>{props.value}</div>);
gets generated as something like:
const Foo = props => (<div>{props.value}</div>);
Foo.propTypes = {
value: PropTypes.oneOf(null),
};
It would be less problematic if the whole prop was skipped rather than attempting to strip external references. Generating an invalid validation rule based on a subset of the union is almost always going to be invalid and unhelpful.
This is a great plugin, thanks for making it!
I have found it doesn't work with Pick<>
. I have forked the repo and added a fixture
import React from 'react';
interface SomeType {
someString: string;
someNumber: number;
}
type PropData = Pick<SomeType, 'someString'>;
type Props = PropData & {additionalString: string};
export const PickFromTypeFunctionComponent: React.FunctionComponent<
Props
> = () => {
return null;
};
and the resulting snapshot is
exports[`babel-plugin-typescript-to-proptypes transforms ./fixtures/pick-from-type.ts 1`] = `
"import _pt from 'prop-types';
import React from 'react';
interface SomeType {
someString: string;
someNumber: number;
}
type PropData = Pick<SomeType, 'someString'>;
type Props = PropData & {
additionalString: string;
};
export const PickFromTypeFunctionComponent: React.FunctionComponent<Props> = () => {
return null;
};
PickFromTypeFunctionComponent.propTypes = {
additionalString: _pt.string.isRequired
};"
`;
I am currently investigating and seeing if I can add support for this. If I succeed, I can send a PR your way.
When trying to convert a component with many props (over 25) the plugin only converts the first ~25 props and omits the rest.
I created a demo repo.
npm i && npm test
The result file (lib/ManyProps.js
) should contain all 40 props that are in the source file (src/ManyProps.tsx
)
The result file contains only 25 props.
Node: v8.16.0
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.4",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.3.3",
"@types/react": "^16.8.17",
"babel-plugin-typescript-to-proptypes": "^0.17.1",
"typescript": "^3.4.5"
I don't have experience with babel plugins but I'd be happy to help with a fix if you can give some directions to where the problem is.
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.