Giter Club home page Giter Club logo

babel-plugin-typescript-to-proptypes's People

Contributors

amannn avatar dchesterton avatar dependabot[bot] avatar milesj avatar redallen avatar sedenardi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

babel-plugin-typescript-to-proptypes's Issues

'FunctionComponent' components don't output propTypes

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:
image

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.

String literals defined in another file don't get translated to 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 = {};

example shows @babel/preset-react, should be '@babel/plugin-syntax-jsx'

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 }]]
}

Destructured Props in Functional Component

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?

Transform call-signature-only objects to `PropTypes.function`

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

IndexedAccessType not deeply traversed

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.

Steps to reproduce

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;
  }
}

Expected:

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;
  }

}

Actual:

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;
  }
}

Using the plugin without Babel?

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!

Usage with Yarn 2.0 Plug'n'Play

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.

enums transform to .any

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.

Using Mapped Types does not yield any prop type

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:

Screenshot 2019-09-11 at 11 36 05

TypeError: Cannot read property 'name' of null

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

Usage description

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!

Order of type declaration is significant

Steps to reproduce

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.

Extends external/imported interfaces/types

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;
}

Does not generate propTypes for forwardRef.

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.

Incorrect code generated for union types

Description

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

Expected Behavior

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;

Type import from react alongside default import causes the plugin to not output prop types

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:

if (node.source.value === 'react') {
const response = upsertImport(node);
state.reactImportedName = response.defaultImport;
}

Which ends up short circuiting the plugin execution:

// Abort early if we're definitely not in a file that needs conversion
if (!state.propTypes.defaultImport && !state.reactImportedName) {
return;
}

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 ๐Ÿ˜„.

Breaks existing prop-types deconstruction for jest tests

Code

import {
  number,
  string,
  oneOfType,
  arrayOf,
  node,
  bool,
  func,
  shape,
  InferProps
} from 'prop-types'

....


export const propTypes = {
  id: string,
  placeholder: string,
  disabled: bool,
  className: string,

OUTPUT:

โ— 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,

Difference between TypeScript optional fields and propTypes isRequired

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.

Rename import

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

Conditional types issue

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!

Not working, only imports prop-types

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';

Types not being generated if no react import present

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?

Support for Void Function Components

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)?

Support anonymous functions without React.FC

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.

T[] vs Array<T>

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

Generated propTypes are wrong when external types are stripped

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.

Does not work with Pick<>

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.

PropTypes are omitted for component with many props

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.

Steps to reproduce:

npm i && npm test

Expected result

The result file (lib/ManyProps.js) should contain all 40 props that are in the source file (src/ManyProps.tsx)

Actual result

The result file contains only 25 props.

Environment:

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!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.