226

I can't figure out how to set default property values for my components using Typescript.

This is the source code:

class PageState
{
}

export class PageProps
{
    foo: string = "bar";
}

export class PageComponent extends React.Component<PageProps, PageState>
{
    public render(): JSX.Element
    {
        return (
            <span>Hello, world</span>
        );
    }
}

And when I try to use the component like this:

ReactDOM.render(<PageComponent />, document.getElementById("page"));

I get an error saying property foo is missing. I want to use the default value. I've also tried to use static defaultProps = ... inside the component, but it had no effect as I suspected.

src/typescript/main.tsx(8,17): error TS2324: Property 'foo' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<PageComponent> & PageProps & { children?: ReactEle...'.

How can I use default property values? Many JS components my company uses rely on them and not using them is not a choice.

Tom
  • 2,355
  • 3
  • 11
  • 10

12 Answers12

420

Default props with class component

Using static defaultProps is correct. You should also be using interfaces, not classes, for the props and state.

Update 2018/12/1: TypeScript has improved the type-checking related to defaultProps over time. Read on for latest and greatest usage down to older usages and issues.

For TypeScript 3.0 and up

TypeScript specifically added support for defaultProps to make type-checking work how you'd expect. Example:

interface PageProps {
  foo: string;
  bar: string;
}

export class PageComponent extends React.Component<PageProps, {}> {
    public static defaultProps = {
        foo: "default"
    };

    public render(): JSX.Element {
        return (
            <span>Hello, { this.props.foo.toUpperCase() }</span>
        );
    }
}

Which can be rendered and compile without passing a foo attribute:

<PageComponent bar={ "hello" } />

Note that:

  • foo is not marked optional (ie foo?: string) even though it's not required as a JSX attribute. Marking as optional would mean that it could be undefined, but in fact it never will be undefined because defaultProps provides a default value. Think of it similar to how you can mark a function parameter optional, or with a default value, but not both, yet both mean the call doesn't need to specify a value. TypeScript 3.0+ treats defaultProps in a similar way, which is really cool for React users!
  • The defaultProps has no explicit type annotation. Its type is inferred and used by the compiler to determine which JSX attributes are required. You could use defaultProps: Pick<PageProps, "foo"> to ensure defaultProps matches a sub-set of PageProps. More on this caveat is explained here.
  • This requires @types/react version 16.4.11 to work properly.

For TypeScript 2.1 until 3.0

Before TypeScript 3.0 implemented compiler support for defaultProps you could still make use of it, and it worked 100% with React at runtime, but since TypeScript only considered props when checking for JSX attributes you'd have to mark props that have defaults as optional with ?. Example:

interface PageProps {
    foo?: string;
    bar: number;
}

export class PageComponent extends React.Component<PageProps, {}> {
    public static defaultProps: Partial<PageProps> = {
        foo: "default"
    };

    public render(): JSX.Element {
        return (
            <span>Hello, world</span>
        );
    }
}

Note that:

  • It's a good idea to annotate defaultProps with Partial<> so that it type-checks against your props, but you don't have to supply every required property with a default value, which makes no sense since required properties should never need a default.
  • When using strictNullChecks the value of this.props.foo will be possibly undefined and require a non-null assertion (ie this.props.foo!) or type-guard (ie if (this.props.foo) ...) to remove undefined. This is annoying since the default prop value means it actually will never be undefined, but TS didn't understand this flow. That's one of the main reasons TS 3.0 added explicit support for defaultProps.

Before TypeScript 2.1

This works the same but you don't have Partial types, so just omit Partial<> and either supply default values for all required props (even though those defaults will never be used) or omit the explicit type annotation completely.

Default props with Functional Components

You can use defaultProps on function components as well, but you have to type your function to the FunctionComponent (StatelessComponent in @types/react before version 16.7.2) interface so that TypeScript knows about defaultProps on the function:

interface PageProps {
  foo?: string;
  bar: number;
}

const PageComponent: FunctionComponent<PageProps> = (props) => {
  return (
    <span>Hello, {props.foo}, {props.bar}</span>
  );
};

PageComponent.defaultProps = {
  foo: "default"
};

Note that you don't have to use Partial<PageProps> anywhere because FunctionComponent.defaultProps is already specified as a partial in TS 2.1+.

Another nice alternative (this is what I use) is to destructure your props parameters and assign default values directly:

const PageComponent: FunctionComponent<PageProps> = ({foo = "default", bar}) => {
  return (
    <span>Hello, {foo}, {bar}</span>
  );
};

Then you don't need the defaultProps at all! Be aware that if you do provide defaultProps on a function component it will take precedence over default parameter values, because React will always explicitly pass the defaultProps values (so the parameters are never undefined, thus the default parameter is never used.) So you'd use one or the other, not both.

Aaron Beall
  • 49,769
  • 26
  • 85
  • 103
  • I've ctrl+c/ctr+v'd your code (and fixed the semicolon that shouldn't be there) and it's giving me the same error: src/typescript/main.tsx(8,17): error TS2324: Property 'foo' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes & PageProps & { children?: ReactEle...'. – Tom May 17 '16 at 17:01
  • 2
    The error sounds like you are using `` somewhere without passing the `foo` prop. You can make it optional using `foo?: string` in your interface. – Aaron Beall May 17 '16 at 17:02
  • Yes, exactly, because I need it to use the default value specified in defaultProps - when I pass the prop in the place where I use it, it gets overwritten, doesn't it? – Tom May 17 '16 at 17:04
  • You're right, I wasn't thinking, obviously you want it optional. :) Updated my example. – Aaron Beall May 17 '16 at 17:06
  • What if I want to have two properties: one required and one optional with default value? `public static defaultProps` cannot have the type of `PageProps` then, as I'm not going to specify all props required by interface. – ptkoz Jun 25 '16 at 10:45
  • @pamelus You can mix optional and not optional props in a single interface, just mark the optional ones with a `?` and leave the required props as is. – Aaron Beall Aug 15 '16 at 16:52
  • 1
    @Aaron But tsc will throw compilation error then, as defaultProps does not implement PageProps interface. You either have to make all interface properties optional (bad) or specify default value also for all required fields (unnecessary boilerplate) or avoid specifying type on defaultProps. – ptkoz Aug 15 '16 at 17:12
  • Can typescript partials be used with a stateless functional component? – Adrian Moisa Mar 19 '17 at 09:42
  • 1
    @adrianmoisa You mean default props? Yes it works but the syntax is different... I'll add an example to my answer when I'm back at my computer... – Aaron Beall Mar 19 '17 at 15:26
  • 2
    @AdrianMoisa Updated with s function component example – Aaron Beall Mar 20 '17 at 15:15
  • @Aaron I have a very similar question, would you mind help me on [it](https://stackoverflow.com/questions/47032289/default-function-value-in-react-component-using-typescript)? Thanks in advanced. – Charlie Oct 31 '17 at 09:50
  • Hi, using this way, inside the component it will complain `"foo" is possibly undefined`, even though we provided default value. How to fix that? – Boyang Mar 24 '18 at 18:49
  • @Boyang use the "Definite Assignment Assertions" to silence the compiler. https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html – cquezel Mar 25 '18 at 21:38
  • @Boyang Yeah, that's an annoying issue when you have `strictNullChecks` enabled, AFAIK there's no way to make TS understand this correctly. You can always use a non-null assertion: `this.props.foo!` or a type-guard `this.props.foo && /*do something with foo */`. FWIW Flowtype seems to understand `defaultProps` better. – Aaron Beall Mar 26 '18 at 14:25
  • @Boyang Definite Assignment Assertions are close, those work on instance properties and variable declarations, but component props aren't either of those. – Aaron Beall Mar 26 '18 at 14:29
  • I logged [an issue](https://github.com/Microsoft/TypeScript/issues/22881) about the case with `strictNullChecks` and there's some workarounds described [here](https://levelup.gitconnected.com/ultimate-react-component-patterns-with-typescript-2-8-82990c516935) (scroll to "Default Props") – Aaron Beall Mar 28 '18 at 13:27
  • I know this is an older question, but does `defaultProps` *need* to be a `public static` field or could it be a `private` field? – Jared Nov 06 '18 at 21:18
  • 1
    @Jared Yes, it needs to to be `public static defaultProps` or `static defaultProps` (`public` is default) for everything (compiler and React runtime) to work properly. It may work at runtime with `private static defaultProps` only because `private` and `public` don't exist at runtime, but the compiler wouldn't work correctly. – Aaron Beall Nov 12 '18 at 14:42
  • Just the perfect answer! – Srijani Ghosh Dec 05 '19 at 10:00
  • 1
    The Function Component example appears to not work? https://www.typescriptlang.org/play/#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wG4AocmATzCTgAUcwBnOAXjgG9y44A7FCHoB+AFxwWMKMH4BzUnF5wAbigA2AVyQSpM+YvIBfSuTQR+UuADEIECcnQwAdNc38MwCwGFckfkj8MAA8TBCsAHwccAAUXILCADSqGtpGAJQcUTzKCUjOMBAAytKycjHpFHxEMJpQ-HDBACbAKgD0ERQm5LYQzk1ImCia6jBhrNE8fHkS+AAWSOrqEPjGpEA – dwjohnston May 14 '20 at 08:51
  • August, 2023 - https://bobbyhadz.com/blog/react-optional-props-typescript – Prateek Kumar Dalbehera Aug 30 '23 at 06:52
23

With Typescript 2.1+, use Partial < T > instead of making your interface properties optional.

export interface Props {
    obj: Model,
    a: boolean
    b: boolean
}

public static defaultProps: Partial<Props> = {
    a: true
};
Learner
  • 2,459
  • 4
  • 23
  • 39
19

Functional Component

Actually, for functional component the best practice is like below, I create a sample Spinner component:

import React from 'react';
import { ActivityIndicator } from 'react-native';
import { colors } from 'helpers/theme';

export interface SpinnerProps {
  color?: string;
  size?: 'small' | 'large' | 1 | 0;
  animating?: boolean;
  hidesWhenStopped?: boolean;
}

const Spinner = ({
  color = colors.primary,
  size = 'small',
  animating = true,
  hidesWhenStopped = true,
}: SpinnerProps): JSX.Element => (
  <ActivityIndicator
    color={color}
    size={size}
    animating={animating}
    hidesWhenStopped={hidesWhenStopped}
  />
);

export default Spinner;

If your component has children it would be better to use React.FC, like following:

export interface TypographyProps {
  color?: string;
}

const Typography: React.FC<TypographyProps> = ({
  children,
  color,
}) => (
  <span style={{ color }}>
    {children}
  </span>
);

export default Typography;
AmerllicA
  • 29,059
  • 15
  • 130
  • 154
11

You can use the spread operator to re-assign props with a standard functional component. The thing I like about this approach is that you can mix required props with optional ones that have a default value.

interface MyProps {
   text: string;
   optionalText?: string;
}

const defaultProps = {
   optionalText = "foo";
}

const MyComponent = (props: MyProps) => {
   props = { ...defaultProps, ...props }
}
Christopher
  • 1,395
  • 2
  • 18
  • 32
  • In my opinion, this is the cleanest, easiest to read solution. – MikeyT Jul 05 '21 at 18:44
  • Might be better to use a temp variable to merge instead of overwriting the parameter `props` – jfunk Sep 23 '21 at 20:07
  • @jfunk why????? – Christopher Sep 24 '21 at 02:03
  • 2
    "Assignment to variables declared as function parameters can be misleading and lead to confusing behavior, as modifying function parameters will also mutate the arguments object. Often, assignment to function parameters is unintended and indicative of a mistake or programmer error." https://eslint.org/docs/rules/no-param-reassign – jfunk Sep 24 '21 at 13:31
10

With Typescript 3.0 there is a new solution to this issue:

export interface Props {
    name: string;
}

export class Greet extends React.Component<Props> {
    render() {
        const { name } = this.props;
        return <div>Hello ${name.toUpperCase()}!</div>;
    }
    static defaultProps = { name: "world"};
}

// Type-checks! No type assertions needed!
let el = <Greet />

Note that for this to work you need a newer version of @types/react than 16.4.6. It works with 16.4.11.

CorayThan
  • 17,174
  • 28
  • 113
  • 161
  • Great answer! How might one handle: `export interface Props { name?: string;}` where name is an _optional_ prop? I keep getting `TS2532 Object is possibly 'undefined'` – Fydo Sep 11 '18 at 18:13
  • @Fydo I haven't needed to have a default value for an optional prop, since `undefined` is a sort of automatic default value for those props. You want to be able to pass in `undefined` as the explicit value sometimes, but have another default value? Have you tried `export interface Props {name: string | undefined;}` instead? Haven't tried that, just an idea. – CorayThan Sep 11 '18 at 20:24
  • Adding `?` is the same as adding `|undefined`. I want to optionally pass in the prop, and let `defaultProps` handle that case. Looks like this isn't possible in TS3 quite yet. I'll just use the dreaded `name!` syntax since I know it's never `undefined` when `defaultProps` are set. Thanks anyways! – Fydo Sep 12 '18 at 14:56
  • 2
    Upvoted because this is the right answer now! Also updated my accepted answer (which is starting to become a history book) with this new feature, and a little more explanation. :) – Aaron Beall Dec 03 '18 at 19:41
5

From a comment by @pamelus on the accepted answer:

You either have to make all interface properties optional (bad) or specify default value also for all required fields (unnecessary boilerplate) or avoid specifying type on defaultProps.

Actually you can use Typescript's interface inheritance. The resulting code is only a little bit more verbose.

interface OptionalGoogleAdsProps {
    format?: string;
    className?: string;
    style?: any;
    scriptSrc?: string
}

interface GoogleAdsProps extends OptionalGoogleAdsProps {
    client: string;
    slot: string;
}


/**
 * Inspired by https://github.com/wonism/react-google-ads/blob/master/src/google-ads.js
 */
export default class GoogleAds extends React.Component<GoogleAdsProps, void> {
    public static defaultProps: OptionalGoogleAdsProps = {
        format: "auto",
        style: { display: 'block' },
        scriptSrc: "//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"
    };
5

For those having optional props that need default values. Credit here :)

interface Props {
  firstName: string;
  lastName?: string;
}

interface DefaultProps {
  lastName: string;
}

type PropsWithDefaults = Props & DefaultProps;

export class User extends React.Component<Props> {
  public static defaultProps: DefaultProps = {
    lastName: 'None',
  }

  public render () {
    const { firstName, lastName } = this.props as PropsWithDefaults;

    return (
      <div>{firstName} {lastName}</div>
    )
  }
}
Morlo Mbakop
  • 3,518
  • 20
  • 21
4

For the functional component, I would rather keep the props argument, so here is my solution:

interface Props {
  foo: string;
  bar?: number; 
}

// IMPORTANT!, defaultProps is of type {bar: number} rather than Partial<Props>!
const defaultProps = {
  bar: 1
}


// externalProps is of type Props
const FooComponent = exposedProps => {
  // props works like type Required<Props> now!
  const props = Object.assign(defaultProps, exposedProps);

  return ...
}

FooComponent.defaultProps = defaultProps;
xiechao06
  • 410
  • 6
  • 9
  • defining `FooComponent.defaultProps` makes no sense, because it isn't used. you are **manually** merging the prop directly with the `defaultProps` variable... – vsync Jan 01 '22 at 10:48
2

With optional and default props for functional component (Typescript 4.4+):

export const LoadingSpinner = ({
  size = "lg",
  children,
}: {
  size?: "sm" | "base" | "lg";
  children?: any;
}) => {
console.log(size);
return <div>{children}</div>
};

use it like this:

 <LoadingSpinner size="sm"><p>hello</p></LoadingSpinner>
 <LoadingSpinner><p>hello</p></LoadingSpinner>

GorvGoyl
  • 42,508
  • 29
  • 229
  • 225
0

Check my solution:

interface Props {
  foo?: string | undefined;
  bar: string;
  other?: string;
}

export const Component = ({foo, bar, other = 'default text'}:Props) => {
    console.log(foo, bar, other);
    return(
        ...//some cool stuff your component should do
    )
}

<Component bar='obligatory text'/>
0

As we know to do one task, react have many ways, one is I am demonstrating.

Component

<Greet name="Dupinder"/>

Component Type

type greetProps = {
    name: string
    number ?: number
}

Assigining default or passed values from component.

export const Greet = (props: greetProps) => {
    const {number = 0} = props
    return ( 
        <div>
            <h2>Welcome to the react practice: {props.name} here {number}</h2>
        </div>
    )
}

Creating a const with default value which will be filled from props coming from component attributes.

enter image description here

Component with value

Component

<Greet name="Dupinder" number="10"/>

Output

enter image description here

Dupinder Singh
  • 7,175
  • 6
  • 37
  • 61
-1

Hooks (with Typescript)

export interface ApprovalRejectModalProps{
 singleFileApprove:boolean;
}

ApproveRejectModal.defaultProps={
 singleFileApprove:false --> default value
}

export const ApproveRejectModal:React.FC<ApprovalRejectModalProps>=(props)=>{
return (
        <div>
            ....
        </div>
       )
}
Aayush Bhattacharya
  • 1,628
  • 18
  • 17