Skip to content

Support setting React PropTypes using TypeScript types #4833

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

Closed
Vinnl opened this issue Sep 17, 2015 · 24 comments
Closed

Support setting React PropTypes using TypeScript types #4833

Vinnl opened this issue Sep 17, 2015 · 24 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@Vinnl
Copy link

Vinnl commented Sep 17, 2015

I'm not sure how practical this would be, but since TypeScript is now supporting React's JSX syntax, it could perhaps provide more syntactic sugar for a common React pattern? Namely, that of setting PropTypes, requirements for the structure of property arguments for React components. Sounds like TypeScript's syntax should be able to remove a lot of the boilerplate there.

@Lenne231
Copy link

PropTypes is used for type checking in developement mode. TypeScript already does type checking at compile time, i don't think that you need PropTypes if you use TypeScript.

@Vinnl
Copy link
Author

Vinnl commented Sep 17, 2015

Hmm, that's true, I guess :)

@Vinnl Vinnl closed this as completed Sep 17, 2015
@Vinnl
Copy link
Author

Vinnl commented Sep 17, 2015

Although, then again (sorry for the change-of-mind), props aren't passed as an argument to the component, right? Thus, there isn't really a place in the component to define what its given properties should look like...

@Vinnl Vinnl reopened this Sep 17, 2015
@mhegazy
Copy link
Contributor

mhegazy commented Sep 17, 2015

//cc: @RyanCavanaugh

@adidahiya
Copy link
Contributor

Your props should be defined in an interface which is passed as a type parameter to React.Component<P, S> when you define a component. I don't see any real use for React's runtime type checking of props when using TypeScript.

@mhegazy mhegazy added the Question An issue which isn't directly actionable in code label Sep 17, 2015
@Vinnl
Copy link
Author

Vinnl commented Sep 17, 2015

Ah, that sounds good - sorry, I only have experience with React's non-ES6 syntax since I'm using mixins, so if you can define the interface for your props using the class syntax, that should cover it :)

@Vinnl Vinnl closed this as completed Sep 17, 2015
@adidahiya
Copy link
Contributor

no worries :)

as some additional advice, you'll want your props to extend React.Props to automatically get things like this.props.children:

    interface IFooProps extends React.Props<Foo> {
        // ...
    }

    interface IFooState {
        // ...
    }

    class Foo extends React.Component<IFooProps, IFooState> {
        // ...
    }

@CtrlZvi
Copy link

CtrlZvi commented Oct 7, 2015

I might be missing something, but without proptypes support, how do I write the equivalent of

var MyComponent = React.createClass({
  propTypes: {
    children: React.PropTypes.element.isRequired
  },

  render: function() {
    return (
      <div>
        {this.props.children} // This must be exactly one element or it will throw.
      </div>
    );
  }

});
interface IMyComponentProps extends React.Props<MyComponent> {
}

interface IFooState {
}

class MyComponent extends React.Component<IMyComponentProps, IMyComponentState> {
    render() {
        return (
            <div>
                {this.props.children}
            </div>
        );
    }
}

React.render(
    <MyComponent>Body</MyComponent>,
    body
);

makes the children optional, but

interface IMyComponentProps extends React.Props<MyComponent> {
    children: React.ReactChild | {} | any[] | boolean;
}

yields the compilation error error TS2324: Property 'children' is missing in type 'IMyComponentProps'.

@RyanCavanaugh
Copy link
Member

There's currently no type system enforcement of child elements.

We looked into this but a) there doesn't seem to be a lot of code that has meaningful constraints on its children and b) we would have had to encode a ton of very React-specific semantics into the type system, which we weren't comfortable doing at this point.

@TheSharpieOne
Copy link

What about the case when building a react library to be used by others. Others may not be using typescript. It would be nice to have a way to generate PropTypes when creating the distributed lib so other developers will be able to get PropType validation during development when using the library. React then strips theses away when building production.

@TobiasBales
Copy link

as the @TheSharpieOne said, this is stopping us from porting main libraries to typescript since we have multiple consumers which in turn stops us from migrating the consumers to typescript (since it feels to be rather error prone to manually add both and keep them in sync)

@wilomgfx
Copy link

what @TheSharpieOne and @TobiasBales said. We kinda need support for it.

@gaoxiaoliangz
Copy link

I came up with an idea, that satisfies both typescript and react runtime prop validation, but it has flaws.

Say we have a component named Icon, it has two props name and size.

const iconPropTypes = {
  name: pt.string.isRequired as any as string,
  size: pt.number as any as number
}

type TProps = typeof iconPropTypes

class Icon extends Component<TProps, {}> {
  ...component content
}

Icon['propTypes'] = iconPropTypes

The problem is obvious in typescript size is required when it's not supposed to, and I have not found a solution to work this around.

@stevecooperorg
Copy link

The effective difference between React PropTypes and TypeScript types is that, while TypeScript type annotations can describe what JSON schema the server should deliver, React detects what is actually delivered. If the wrong data is delivered to the client, React PropTypes will give us good error messages while the transpired TypeScript will die without explanation.

While the two systems clearly overlap, this feature can be justified in serving to automatically check server data at runtime.

Also, note that React PropTypes are fairly limited ("this property must be an object") while TypeScript could generate a more complete custom validator ("this property must be an object with an integer id and a string name")

@TobiasBales
Copy link

React PropTypes can describe objects in more detail, e.g. PropTypes.shape

@stevecooperorg
Copy link

@TobiasBales - thanks for that; you're right. PropTypes.shape is darn close, conceptually, to a straightfoward bit of runtime typechecking.

I wonder whether it is possible to write a PropTypes object for each type using the TypeScript compiler API and type checker. Notice how the example lets you find type and and member info, build a complex object describing the type, and output structured content. You could do something like;

  1. use the compiler API to get type and member info
  2. use it to generate a validation routine per type, and the PropTypes object for each component,
  3. call it from your main TypeScript files.

Not as easy as full language support, and it's just an idea, but possibly something you could get working with a bit of hacking about.

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Feb 25, 2017

Hey all, would prop-types-ts get you close to what you want? Instead of creating proptypes out of TypeScript types, it allows you to make TypeScript types from proptypes.

@cpunion
Copy link

cpunion commented Mar 6, 2017

Some libraries written by TS, compiled to JavaScript and published on npm registry, like https://github.com/ant-design/ant-design-mobile, is there a way to use its propTypes in JavaScript?

@wilomgfx
Copy link

wilomgfx commented Sep 9, 2017

@omril1 what ?

@jac1013
Copy link

jac1013 commented Nov 11, 2017

This might help someone, if you want to make a props to be required and force TypeScript to throw an error about it you can accomplish it by using an interface for the Generic Props of the React.Component (I know this is already explain in the thread, but no one pointed out the optional operator that It's explained later in this comment).

interface Props {
  name: string
}

export class Person extends React.Component<Props, {}> 

If you later try <Person/> without providing the name attribute it will throw a compile error, the counterpart of this would be to use the optional operator ?

interface Props {
  name?: string
}

With the above interface TypeScript won't complain whenever we do <Person/>, because the name is optional according to the use of the optional operator in the interface attribute definition.

@grncdr
Copy link

grncdr commented Nov 27, 2017

For anybody still interested in the original suggestion, I've put together a proof-of-concept webpack loader here https://github.com/grncdr/ts-react-loader#what-it-does that will inject static propTypes and contextTypes into React components.

@jessedvrs
Copy link

Maybe re-open this issue @Vinnl ? My use case is when using ts and non-ts files at the same time (e.g. when I incrementally rewrite my React components to TypeScript). My non-ts files don't warn me when I pass the wrong props, and I have no runtime checks either.

@Vinnl
Copy link
Author

Vinnl commented Mar 21, 2018

@jessedvrs Now that I understand TypeScript better, I've become convinced that React's PropTypes and TypeScript types serve different purposes, namely runtime vs. compile-time checks - and TypeScript typically does not try to influence run-time behaviour.

That said, I think your use case points to a bigger problem with TypeScript, not even specific to prop-types, namely: dealing with less-strict environments (such as TypeScript with looser configuration, or plain Javascript). If you use TypeScript in strict mode, you can write a function that receives a string as argument. However, you might still want to add a check for whether that argument is null (as would be allowed by someone using that function that runs TypeScript in non-strict mode) or something completely different (as plain Javascript allows). So technically, the code can deal with non-string values - should that be reflected in the types?

I am currently inclined to say no, and to add those checks as such:

function someFunction(someArgument: string) {
    if ((someArgument as any) === null) {
        // Do something
    }
}

(although I'm not sure if that would work as expected, as I have no libraries in TypeScript)

An alternative is to not do the prop-type checking at all - there's already a lot of assumptions built-in in regular Javascript. If you need the extra guarantees in a file, that file should be converted to TypeScript.

In any case, I happen to have reported the more general case here: #12615

@jessedvrs
Copy link

Thanks for your quick response @Vinnl :)

You could indeed state that the strictness of the library user should match the strictness of the library itself, so users could just upgrade to TypeScript and the problem would be solved. TypeScript and prop-types do indeed serve different technical purposes. So that is why this is not a TypeScript issue and this issue should stay closed, I agree.

But we do use a TypeScript interface in replacement of React's PropTypes, so in the end it does serve the same purpose regardless of the time difference (runtime, compile-time). And that is property validation when implementing React components.

That being said, your code example is indeed my current solution. Which is to manually throw or log errors when the value doesn't match the type, but this is exactly what React's PropTypes already do. So I'm looking to find a solution to add React's PropTypes based on the TypeScript interface. I personally think this should be something explicit, for example with decorators or something.

@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests