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

Tom*_*Tom 123 typescript reactjs 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>
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

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

ReactDOM.render(<PageComponent />, document.getElementById("page"));
Run Code Online (Sandbox Code Playgroud)

我得到一个错误,说财产foo缺失.我想使用默认值.我也尝试static defaultProps = ...在组件内部使用,但它没有像我怀疑的那样有效.

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

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

Aar*_*all 264

带有类组件的默认道具

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

更新2018/12/1:TypeScript改进了与defaultProps时间相关的类型检查.继续阅读最新和最好的用法,直到旧的用法和问题.

适用于TypeScript 3.0及更高版本

TypeScript特别添加了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>
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

哪个可以在不传递foo属性的情况下进行渲染和编译:

<PageComponent bar={ "hello" } />
Run Code Online (Sandbox Code Playgroud)

注意:

  • foo没有标记为可选(即foo?: string),即使它不是必需的JSX属性.标记为可选将意味着它可能是undefined,但事实上它永远不会是undefined因为defaultProps提供默认值.可以想象它类似于如何将函数参数标记为可选,或者使用默认值,但不是两者,但两者都意味着调用不需要指定值.TypeScript 3.0+ defaultProps以类似的方式对待,这对React用户来说真的很酷!
  • defaultProps没有明确的类型注释.它的类型是由推断器推断和使用的,用于确定需要哪些JSX属性.您可以使用defaultProps: Pick<PageProps, "foo">以确保defaultProps匹配子集PageProps.这里有更多关于这个警告的解释.
  • 这需要@types/react版本16.4.11才能正常工作.

对于TypeScript 2.1直到3.0

在TypeScript 3.0实现之前,编译器支持defaultProps仍然可以使用它,并且它在运行时100%与React协同工作,但由于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>
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:

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

在TypeScript 2.1之前

这个工作方式相同但你没有Partial类型,所以只需省略Partial<>并为所有必需的道具提供默认值(即使永远不会使用这些默认值)或完全省略显式类型注释.

带有功能组件的默认道具

您也可以defaultProps在函数组件上使用,但必须在FunctionComponent(StatelessComponent@types/react版本16.7.2+中)界面中键入函数,以便TypeScript了解defaultProps函数:

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

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

PageComponent.defaultProps = {
  foo: "default"
};
Run Code Online (Sandbox Code Playgroud)

请注意,您不必在Partial<PageProps>任何地方使用,因为FunctionComponent.defaultProps已在TS 2.1+中指定为部分.

另一个不错的选择(这是我使用的)是解构props参数并直接分配默认值:

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

然后你根本不需要它defaultProps!请注意,如果确实提供defaultProps了函数组件,它将优先于默认参数值,因为React将始终显式传递defaultProps值(因此参数永远不会被定义,因此永远不会使用默认参数.)所以你要使用一个或另一个,不是两个.

  • 这个错误听起来就像你在某处使用`<PageComponent>`而没有传递`foo`道具.您可以在界面中使用`foo?:string`使其可选. (2认同)
  • @AdrianMoisa 更新了 s 函数组件示例 (2认同)

Ame*_*icA 19

功能组件

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

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;
Run Code Online (Sandbox Code Playgroud)

如果您的组件有,children最好使用React.FC,如下所示:

export interface TypographyProps {
  color?: string;
}

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

export default Typography;
Run Code Online (Sandbox Code Playgroud)


Lea*_*ner 17

使用Typescript 2.1+,使用Partial <T>而不是使您的界面属性可选.

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

public static defaultProps: Partial<Props> = {
    a: true
};
Run Code Online (Sandbox Code Playgroud)


Chr*_*her 12

您可以使用展开运算符通过标准功能组件重新分配 props。我喜欢这种方法的一点是,您可以将必需的道具与具有默认值的可选道具混合在一起。

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

const defaultProps = {
   optionalText = "foo";
}

const MyComponent = (props: MyProps) => {
   props = { ...defaultProps, ...props }
}
Run Code Online (Sandbox Code Playgroud)

  • “对声明为函数参数的变量进行赋值可能会产生误导,并导致令人困惑的行为,因为修改函数参数也会改变参数对象。通常,对函数参数的赋值是无意的,并且表明存在错误或程序员错误。” https://eslint.org/docs/rules/no-param-reassign (4认同)

Har*_*off 5

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

您要么必须将所有接口属性设置为可选(不好),要么为所有必填字段指定默认值(不必要的样板),或者避免在 defaultProps 上指定类型。

实际上你可以使用Typescript的接口继承。生成的代码只是稍微冗长一些。

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"
    };
Run Code Online (Sandbox Code Playgroud)


Cor*_*han 5

使用Typescript 3.0,可以解决此问题:

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 />
Run Code Online (Sandbox Code Playgroud)

需要注意的是这个工作,你需要一个较新版本@types/react16.4.6.它适用于16.4.11.

  • 投票因为这是现在的正确答案!还用这个新功能更新了我接受的答案(它开始成为一本历史书),并做了更多解释。:) (2认同)

Mor*_*kop 5

对于那些具有需要默认值的可选道具的人。信用在这里:)

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>
    )
  }
}
Run Code Online (Sandbox Code Playgroud)