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

IT技术 reactjs typescript tsx
2021-03-29 02:31:10

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

6个回答

带有类组件的默认props

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

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

<PageComponent bar={ "hello" } />

注意:

  • foo没有标记为可选(即foo?: string),即使它不是必需的JSX属性。标记为 optional 意味着它可能是undefined,但实际上它永远不会是,undefined因为defaultProps它提供了一个默认值。想想它类似于如何将函数参数标记为可选,或使用默认值,但不能两者兼而有之,但这两者都意味着调用不需要指定 valueTypeScript 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 属性时只考虑 props,因此您必须将具有默认值的 props 标记为可选?例子:

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进行类型检查,但您不必为每个必需的属性提供默认值,这是没有意义的,因为必需的属性永远不需要默认值。
  • 当使用willstrictNullChecks的值并需要非空断言(即)或类型保护(即)来删除. 这很烦人,因为默认的 prop 值意味着它实际上永远不会是未定义的,但是 TS 不理解这个流程。这是 TS 3.0 明确支持.this.props.foopossibly undefinedthis.props.foo!if (this.props.foo) ...undefineddefaultProps

在 TypeScript 2.1 之前

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

带有功能组件的默认props

您也可以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值(因此参数永远不会未定义,因此永远不会使用默认参数。)所以你会使用一个或另一个,而不是两者。

@Jared 是的,一切(编译器和 React 运行时)都需要public static defaultPropsstatic defaultPropspublic默认)才能正常工作。它可能在运行时工作,private static defaultProps仅因为private并且public在运行时不存在,但编译器将无法正常工作。
2021-05-28 02:31:10
@AdrianMoisa 更新了 s 函数组件示例
2021-06-01 02:31:10
@Aaron 但是 tsc 会抛出编译错误,因为 defaultProps 没有实现 PageProps 接口。您要么必须将所有接口属性设为可选(错误),要么为所有必填字段(不必要的样板)指定默认值,或者避免在 defaultProps 上指定类型。
2021-06-11 02:31:10
该错误听起来像是您在<PageComponent>某处使用而不传递foo道具。您可以foo?: string在您的界面中使其成为可选的
2021-06-15 02:31:10
@adrianmoisa 你是说默认道具?是的,它有效,但语法不同......当我回到我的电脑时,我会在我的答案中添加一个例子......
2021-06-15 02:31:10

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

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

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

使用 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/react16.4.6. 它与16.4.11.

赞成,因为这是现在的正确答案!还用这个新功能更新了我接受的答案(它开始成为历史书),以及更多的解释。:)
2021-05-24 02:31:10
很好的答案!一个人会如何处理:export interface Props { name?: string;}名称是可选道具的地方?我不断得到TS2532 Object is possibly 'undefined'
2021-06-13 02:31:10
@Fydo 我不需要为可选道具设置默认值,因为这undefined是这些道具的一种自动默认值。您希望有时能够undefined作为显式值传入,但有另一个默认值?你试过export interface Props {name: string | undefined;}吗?没试过,只是一个想法。
2021-06-19 02:31:10
添加?与添加相同|undefined我想有选择地传入道具,让我们defaultProps处理这种情况。看起来这在 TS3 中还不太可能。我就用可怕的name!语法,因为我知道这是从来没有undefined的时候defaultProps设置。不管怎么说,多谢拉!
2021-06-20 02:31:10

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

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

对于功能组件,我宁愿保留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;