8000 Default props not working in functional component · Issue #31247 · microsoft/TypeScript · GitHub
[go: up one dir, main page]

Skip to content

Default props not working in functional component #31247

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
AzureMarker opened this issue May 4, 2019 · 20 comments
Closed

Default props not working in functional component #31247

AzureMarker opened this issue May 4, 2019 · 20 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@AzureMarker
Copy link

TypeScript Version: 3.4.5

Search Terms:
react default props defaultProps functional component stateless

Code

import React from "react";

interface Props {
  name: string;
  optional: string;
}

const Component = ({ name, optional = "default" }: Props) => (
  <p>{name + " " + optional}</p>
);

const Test = () => <Component name="test" />;

Expected behavior:
According to the TypeScript 3.0 release notes, the optional prop should not be required in Test as it has been defined with a default using the ES2015 default initializers feature in Component.

Actual behavior:
The following compile error is thrown:

Property 'optional' is missing in type '{ name: string; }' but required in type 'Props'.

Playground Link:
Link
It doesn't look like the playground supports TSX.

Related Issues:
#27425

@ilogico
Copy link
ilogico commented May 4, 2019

The documentation may not be clear on this, but you have to mark the property as optional in your Props interface:

interface Props {
    name: string;
    optional?: string;
}

@AzureMarker
Copy link
Author

That doesn't seem to match what the release notes say:

export interface Props {
    name: string;
}

// ...

function Greet({ name = "world" }: Props) {
    return <div>Hello {name.toUpperCase()}!</div>;
}

@AzureMarker
Copy link
Author
AzureMarker commented May 5, 2019

Making the prop optional also means that when you access the props outside of the component (in a test), the type system does not guarantee that the prop exists even though it will always exist.

Example:

const wrapper = shallow(<Test/>);

// optional is string | undefined instead of string
const optional = wrapper
        .find(Component)
        .props()
        .optional;

If optional was a function, you would have to check it exists before you execute it. An example is if you provide a default onClick prop which just calls preventDefault.

@tbassetto
Copy link
tbassetto commented May 7, 2019

With TypeScript 3.1 or above you should be able to write:

export interface Props {
    name: string;
}

function Greet(props: Props) {
    return <div>Hello {props.name.toUpperCase()}!</div>;
}
Greet.defaultProps = {
  name: "world",
} as Partial<Props>; // Good practice, without it you could use a number as default prop and TypeScript wouldn't complain…

const component = <Greet />; // no errors

@weswigham weswigham added the Question An issue which isn't directly actionable in code label May 8, 2019
@AzureMarker
Copy link
Author

Is it a bug that using the default initializer feature does not work for default props? It seems like a regression given that the example given in the release notes does not work on the latest TypeScript version.

@G-Rath
Copy link
G-Rath commented May 13, 2019

@tbassetto The problem with that is that TS doesn't force you to actually provide a default value:

import React from 'react';

interface Props {
  myProp: string;
}

const Comp = (props: Props) => {
  return (
    <div>
      {props.myProp.toUpperCase()}
    </div>
  );
};

Comp.defaultProps = {

} as Pick<Props, 'myProp'>;

const comp = <Comp />;

The other annoying thing is the fact that you have to do this in the first place: If I type Comp as React.FC<Props>, then defaultProps is typed nicely, but other problems arise.

All in all, TypeScripts JSX/React support right now seems to be in a weird not-quite-perfect" state, but running as if everything is fine (at least in my eyes - Mainly, I can't seem to find any solid notes on what currently is the "Official" way to do stateless React components in a type safe manner for 3.4.5).

8000

@tomspeak
Copy link
tomspeak commented Jun 14, 2019

Although I see everyone referencing work arounds, I am unable to get any of them to work.

I am on version 3.3.3

export interface Props {
  shouldTruncateContent: boolean;
}

const Alert: React.FunctionComponent<Props> = ({
  children,
  shouldTruncateContent = false
}) => {
  return (
        <S.Content shouldTruncateContent={shouldTruncateContent} size="fill">
          {children}
        </S.Content>
  );
};

Alert.defaultProps = {
  shouldTruncateContent: false
} as Pick<Props, 'shouldTruncateContent'>;

export default Alert;

Does anyone have any insight into why this still throws the error

TS2741: Property 'shouldTruncateContent' is missing in type '{ children: string; }' but required in type 'Props'.

I have tried all the fixes proposed in #27425 and #519 too.

@dragomirtitian
Copy link
Contributor

@tomspeak Either my workaround or @Hotell's woraround (although this one makes all props optional). Tested on 3.5 with your sample code.

@jamesBennett
Copy link

Although I see everyone referencing work arounds, I am unable to get any of them to work.

I am on version 3.3.3

export interface Props {
  shouldTruncateContent: boolean;
}

const Alert: React.FunctionComponent<Props> = ({
  children,
  shouldTruncateContent = false
}) => {
  return (
        <S.Content shouldTruncateContent={shouldTruncateContent} size="fill">
          {children}
        </S.Content>
  );
};

Alert.defaultProps = {
  shouldTruncateContent: false
} as Pick<Props, 'shouldTruncateContent'>;

export default Alert;

Does anyone have any insight into why this still throws the error

TS2741: Property 'shouldTruncateContent' is missing in type '{ children: string; }' but required in type 'Props'.

I have tried all the fixes proposed in #27425 and #519 too.

You just need to make shouldTruncateContent: boolean; optional shouldTruncateContent?: boolean;

@ralexhassle
Copy link

The documentation may not be clear on this, but you have to mark the property as optional in your Props interface:

interface Props {
    name: string;
    optional?: string;
}

I am using this optional type everywhere. Why the down vote ? Could someone explain ?

@ilogico
Copy link
ilogico commented May 27, 2020

@ralexhassle, I can't explain the down votes, but I can explain that is certainly a correct way of typing a component's optional property.
You might prefer using defaultProps, which the example of the question isn't using, but as far as I'm concerned, marking optional properties as optional is probably perfectly fine...

@AzureMarker
Copy link
Author

The purpose of the issue is that the props are not optional, but have a default value (therefore can be eluded when using the component). If you mark the prop optional with ? then TS assumes it could be undefined and forces you to handle that case. However, it will never be undefined because there is a default value. The downvotes are because the commentator did not seem to understand the issue and proposed something that contradicted the 3.0 release notes.

@flo-sch
Copy link
flo-sch commented Oct 8, 2020

@RyanCavanaugh why has the bot closed this issue?

Perhaps I missed it but I do not see an actual answer, despite lot of workarounds?

PS: using TypeScript v4