使用TypeScript在React组件中的默认属性值

时间:2016-05-17 16:52:04

标签: reactjs typescript tsx

我无法弄清楚如何使用Typescript为我的组件设置默认属性值。

这是源代码:

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

当我尝试使用这样的组件时:

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

我收到错误消息,指出属性foo丢失了。我想使用默认值。我也尝试在组件中使用static defaultProps = ...,但它没有像我怀疑的那样有效。

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

如何使用默认属性值?我公司使用的许多JS组件都依赖于它们而不使用它们不是一种选择。

8 个答案:

答案 0 :(得分:231)

带有类组件的默认道具

使用static defaultProps是正确的。您还应该为props和state使用接口而不是类。

更新2018/12/1 :随着时间的推移,TypeScript改进了与defaultProps相关的类型检查。请继续阅读以了解最新和最常用的用法和问题。

对于TypeScript 3.0及更高版本

TypeScript added support for defaultProps专门用于进行类型检查工作的方式。例如:

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

可以在不传递foo属性的情况下呈现和编译:

<PageComponent bar={ "hello" } />

请注意:

  • foo 标记为可选(即foo?: string),即使它不是JSX属性所必需的。将其标记为可选意味着它可以是undefined,但实际上它永远不会是undefined,因为defaultProps提供了默认值。可以认为它类似于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+以类似的方式对待defaultProps,这对React用户来说真的很酷!
  • defaultProps没有明确的类型注释。它的类型是由推断器推断和使用的,用于确定需要哪些JSX属性。您可以使用defaultProps: Pick<PageProps, "foo">确保defaultPropsPageProps的子集匹配。有关此警告的更多信息是explained here
  • 这需要@types/react16.4.11才能正常使用。

对于TypeScript 2.1直到3.0

在TypeScript 3.0实现了对defaultProps的编译器支持之前,您仍然可以使用它,并且它在运行时与React一起工作100%,但由于TypeScript在检查JSX属性时仅考虑道具,因此您有使用?标记默认为可选的道具。例如:

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

请注意:

  • 使用defaultProps注释Partial<>是个好主意,以便对您的道具进行类型检查,但您不必为每个必需的属性提供默认值,这没有任何意义,因为所需的属性永远不需要默认值。
  • 使用strictNullChecks时,this.props.foo的值将为possibly undefined,并且需要非空断言(即this.props.foo!)或类型保护(即if (this.props.foo) ... })删除undefined。这很烦人,因为默认的prop值意味着它实际上永远不会被定义,但TS并不理解这种流程。这是TS 3.0明确支持defaultProps的主要原因之一。

在TypeScript 2.1之前

这种方式相同,但您没有Partial种类型,因此只需省略Partial<>并为所有必需的道具提供默认值(即使这些默认值永远不会被使用)或完全省略显式类型注释。

Functional Components

的默认道具

您也可以在功能组件上使用defaultProps,但您必须在StatelessComponent FunctionComponent @types/react16.7.2 +中输入您的功能+ defaultProps )接口,以便TypeScript知道函数上的interface PageProps { foo?: string; bar: number; } const PageComponent: StatelessComponent<PageProps> = (props) => { return ( <span>Hello, {props.foo}, {props.bar}</span> ); }; PageComponent.defaultProps = { foo: "default" };

Partial<PageProps>

请注意,您不必在任何地方使用StatelessComponent.defaultProps,因为props已在TS 2.1 +中指定为部分。

另一个不错的选择(这是我使用的)是解构const PageComponent: StatelessComponent<PageProps> = ({foo = "default", bar}) => { return ( <span>Hello, {foo}, {bar}</span> ); }; 参数并直接指定默认值:

defaultProps

然后你根本不需要defaultProps!请注意,如果在函数组件上提供defaultProps,它将优先于默认参数值,因为React将始终显式传递:tab值(因此参数为从来没有未定义,因此从不使用默认参数。)所以你要使用其中一个,而不是两个。

答案 1 :(得分:16)

使用Typescript 2.1+,使用部分&lt; T&gt; ,而不是使您的界面属性可选。

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

public static defaultProps: Partial<Props> = {
    a: true
};

答案 2 :(得分:5)

使用Typescript 3.0,有一个new solution可以解决此问题:

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 />

请注意,要使其正常工作,您需要的@types/react版本比16.4.6要新。它可以与16.4.11一起使用。

答案 3 :(得分:4)

对于功能组件,我宁愿保留props参数,所以这是我的解决方案:

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;

答案 4 :(得分:3)

来自@pamelus对已接受答案的评论:

  

您必须使所有界面属性可选(坏)或   为所有必填字段指定默认值(不必要   样板)或避免在defaultProps上指定类型。

实际上你可以使用Typescript&#39; s interface inheritance。结果代码只是更冗长一点。

{{1}}

答案 5 :(得分:2)

功能组件

实际上,对于功能组件,最佳实践如下所示,我创建了一个示例Spinner组件:

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

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

const Spinner: FC<SpinnerProps> = ({
  color,
  size,
  animating,
  hidesWhenStopped,
}) => (
  <ActivityIndicator
    color={color}
    size={size}
    animating={animating}
    hidesWhenStopped={hidesWhenStopped}
  />
);

Spinner.defaultProps = {
  animating: true,
  color: colors.primary,
  hidesWhenStopped: true,
  size: 'small',
};

export default Spinner;

答案 6 :(得分:1)

对于那些具有需要默认值的可选道具的人。 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>
    )
  }
}

答案 7 :(得分:0)

您可以使用点差运算符为标准功能组件重新分配道具。我喜欢这种方法的地方是,您可以将必需的道具与具有默认值的可选道具混合使用。

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

const defaultProps = {
   optionalText = "foo";
}

const MyComponent = (props: MyProps) => {
   props = { ...defaultProps, ...props }
}