31

I'm using React with TypeScript and I've created stateless function. I've removed useless code from the example for readability.

interface CenterBoxProps extends React.Props<CenterBoxProps> {
    minHeight?: number;
}

export const CenterBox = (props: CenterBoxProps) => {
    const minHeight = props.minHeight || 250;
    const style = {
        minHeight: minHeight
    };
    return <div style={style}>Example div</div>;
};

Everything is great and this code is working correctly. But there's my question: how can I define defaultProps for CenterBox component?

As it is mentioned in react docs:

(...) They are pure functional transforms of their input, with zero boilerplate. However, you may still specify .propTypes and .defaultProps by setting them as properties on the function, just as you would set them on an ES6 class. (...)

it should be easy as:

CenterBox.defaultProps = {
    minHeight: 250
}

But this code generates TSLint error: error TS2339: Property 'defaultProps' does not exist on type '(props: CenterBoxProps) => Element'.

So again: how can I correctly define defaultProps in my above stack (React + TypeScript)?

7 Answers 7

44

After 2 hours of looking for solution... it's working.

If you want to define defaultProps, your arrow function should look like:

export const CenterBox: React.SFC<CenterBoxProps> = props => {
    (...)
};

Then you can define props like:

CenterBox.defaultProps = { someProp: true }

Note that React.SFC is alias for React.StatelessComponent.

I hope that this question (and answer) help somebody. Make sure that you have installed newest React typings.

Sign up to request clarification or add additional context in comments.

2 Comments

could you add a more specific example on how you got defaultProps working? thanks :D
This actually works. Thanks @Jurosh would you mind marking this (your) answer as correct?
28

I believe a better way than described in the React docs is simply to use Javascript / Typescript default arguments.

There's an answer here: https://stackoverflow.com/a/54569933/484190 but for convenience, here's an example:

import React, { FC } from "react";

interface CompProps {
  x?: number;
  y?: number;
}

const Comp: FC<CompProps> = ({ x = 10, y = 20 }) => {
  return <div>{x}, {y}</div>;
}

export default Comp;

This will allow Typescript to know that you don't have to provide the prop, and also that it will never be "undefined" inside your component

4 Comments

This is perfect!
Unfortunately you won't see those default props in react dev tools..
@pete otaqui Have you tried this with InferProps and PropTypes? I've looked everywhere and I'm not seeing a way to tell TS that the value isn't undefined
@OrBachar do you know why it's not possible to see the defaultProps in dev tools? Is there a way to get dev tools to show them?
13

And here's how it works for stateful functions in case others stumble on this. The key is declaring defaultProps as a static variable.

interface IBoxProps extends React.Props<IBoxProps> {
    x?: number;
    y?: number;
    height?: number;
    width?: number;
}

interface IBoxState {
    visible?: boolean;
}

export default class DrawBox extends React.Component<IBoxProps, IBoxState> {
    static defaultProps: IBoxProps;

    constructor(props: IBoxProps) {
        super(props);
    }
    ...
}

DrawBox.defaultProps = {
    x=0;
    y=0;
    height=10;
    weight=10;
};

4 Comments

I know this is a bit late. When I try to inherit the DrawBox class and create a sub class using: "class SubDrawBox extends DrawBox", I get the error DrawBox is not generic. How can I fix this?
Could you please try answering my question - stackoverflow.com/questions/50068412
This answer does not relate to the question. It is a class component, not a stateless functional component.
This helped me because I was using Class component. Thank you. As a side note, you can declare defaultProps like this: static defaultProps: Partial<IBoxProps> in case you don't want to provide a default for every property.
6

For Functional Components as of React 16.7.0 the 'React.SFC' type is being deprecated in favour of 'React.FC'.

Example

type TFuncComp = React.FC<{ text: string }>

const FuncComp: TFuncComp = props => <strong>{props.text}</strong>

FuncComp.defaultProps = { text: 'Empty Text' }

Deprecation Warning in Source

FC (FunctionalComponent) Type in Source

Comments

3

Typing default props in function component with React.FC can result in false type error:

   type Props = {
     required: string,
   } & typeof defaultProps;

   const defaultProps = {
     optDefault: 'optDefault'
   };

   const MyComponent: React.FC<Props> = (props: Props) => (
     <ul>
       <li>required: {props.required}</li>
       <li>optDefault: {props.optDefault}</li>
     </ul>
   )
   MyComponent.defaultProps = defaultProps;


   ReactDOM.render(
     <div>
       <MyComponent
         required='required'
         optDefault='over written'
       />
       <MyComponent   /* type error  <---- false type error */
         required='required'
       />
     </div>,
     document.getElementById('app')
   );

error:

[tsserver 2741] Property 'optDefault' is missing in type '{ required: string; }' but required in type '{ optDefault: string; }'. [E]

Another solution proposed is to use Javascript's own default function parameters:

type Props = {
  required: string,
  optDefault?: string
}

const MyComponent:  React.FC<Props> = ({
  required,
  optDefault='default'
}: Props) => (
  <ul>
    <li>required: {required}</li>
    <li>optDefault: {optDefault}</li>
  </ul>
)

ReactDOM.render(
  <div>
    <MyComponent
      required='required'
      optDefault='over written'
    />
    <MyComponent
      required='required'
    />
  </div>,
  document.getElementById('app')
);

But the problem with this solution is that if you forgot to provide default, a run time bug will result:

const MyComponent: React.FC<Props> = ({
  required,
  optDefault //='optDefault' //<--- if you forgot to provide default
}: Props) => (
  <ul>
    <li>required: {required}</li>
    <li>optDefault: {optDefault}</li> {/* <-- result in bug */}
  </ul>
)

A better solution is not using React.FC at all, simply rely on Typescript type inference:

type Props = {
  required: string,
} & typeof defaultProps;

const defaultProps = {
  optDefault: 'optDefault'
};

const MyComponent = (props: Props) => (
  <ul>
    <li>required: {props.required}</li>
    <li>optDefault: {props.optDefault}</li>
  </ul>
)
MyComponent.defaultProps = defaultProps


ReactDOM.render(
  <div>
    <MyComponent
      required='required'
      optDefault='over written'
    />
    <MyComponent
      required='required'
    />
  </div>,
  document.getElementById('app')
);

2 Comments

Not using React.FC at all indeed worked better for me, thanks!
Yup, iam agree with this answer, another problem apeared when trying to used styled-component module to wrap JSX.Element like default Typescript type interface
-1

You can put this inside your component

static defaultProps: any;

Comments

-1

Simplest for me is to define (partial) defaults in the defaultProps directly:

export default class TSError extends React.Component<ITSErrorProps, {}> {
  private static defaultProps: Partial<ITSErrorProps> = {
    fullPage: true,
    controlClick: true,
    doubleClick: true
  };

...

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.