189

我不知道如何使用 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 组件都依赖于它们,不使用它们不是一种选择。

4

10 回答 10

390

带有类组件的默认道具

使用static defaultProps是正确的。您还应该为道具和状态使用接口,而不是类。

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

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

<PageComponent bar={ "hello" } />

注意:

  • foo没有标记为可选(即),即使它不是 JSX 属性所必需的foo?: string标记为可选意味着它可能是undefined,但实际上它永远不会是undefined,因为defaultProps它提供了一个默认值。可以将其想象为类似于如何将函数参数标记为可选或使用默认值,但不能同时使用两者,但两者都意味着调用不需要指定 value。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你仍然可以使用它,并且它在运行时与 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>
        );
    }
}

注意:

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

在 TypeScript 2.1 之前

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

带有功能组件的默认道具

您也可以defaultProps在函数组件上使用,但您必须将函数键入FunctionComponentStatelessComponent@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"
};

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

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

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

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

于 2016-05-17T16:58:56.397 回答
19

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

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

public static defaultProps: Partial<Props> = {
    a: true
};
于 2017-02-23T05:37:02.393 回答
8

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

请注意,要使其正常工作,您需要更新版本的@types/reactthan 16.4.6。它适用于16.4.11.

于 2018-07-23T20:24:38.340 回答
8

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

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

const defaultProps = {
   optionalText = "foo";
}

const MyComponent = (props: MyProps) => {
   props = { ...defaultProps, ...props }
}
于 2020-12-02T04:01:51.843 回答
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>
    )
  }
}
于 2019-02-01T12:36:24.943 回答
4

来自@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"
    };
于 2017-02-17T09:36:04.797 回答
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;
于 2020-04-02T10:04:09.837 回答
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;
于 2020-06-27T13:28:45.427 回答
1

功能组件的使用optionaldefault道具(Typescript 4.4+):

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

像这样使用它:

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

于 2021-11-14T14:19:19.593 回答
0

钩子(带打字稿)

export interface ApprovalRejectModalProps{
 singleFileApprove:boolean;
}

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

export const ApproveRejectModal:React.FC<ApprovalRejectModalProps>=(props)=>{
return (
        <div>
            ....
        </div>
       )
}
于 2021-09-01T15:16:40.840 回答