我不知道如何使用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丢失了。我想使用默认值。我还尝试使用静态defaultProps =…但它并没有像我怀疑的那样起作用。

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

如何使用默认属性值?我的公司使用的许多JS组件都依赖于它们,不使用它们是不可取的。


默认道具与类组件

使用静态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 is not marked optional (ie foo?: string) even though it's not required as a JSX attribute. Marking as optional would mean that it could be undefined, but in fact it never will be undefined because defaultProps provides a default value. Think of it similar to how 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+ treats defaultProps in a similar way, which is really cool for React users! The defaultProps has no explicit type annotation. Its type is inferred and used by the compiler to determine which JSX attributes are required. You could use defaultProps: Pick<PageProps, "foo"> to ensure defaultProps matches a sub-set of PageProps. More on this caveat is explained here. This requires @types/react version 16.4.11 to work properly.

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

注意:

It's a good idea to annotate defaultProps with Partial<> so that it type-checks against your props, but you don't have to supply every required property with a default value, which makes no sense since required properties should never need a default. When using strictNullChecks the value of this.props.foo will be possibly undefined and require a non-null assertion (ie this.props.foo!) or type-guard (ie if (this.props.foo) ...) to remove undefined. This is annoying since the default prop value means it actually will never be undefined, but TS didn't understand this flow. That's one of the main reasons TS 3.0 added explicit support for defaultProps.

在TypeScript 2.1之前

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

功能组件的默认道具

你也可以在函数组件上使用defaultProps,但是你必须将你的函数输入到FunctionComponent(在16.7.2版本之前@types/react中的StatelessComponent)接口中,这样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+中已经被指定为Partial。

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

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

那么你就根本不需要defaultProps了!请注意,如果你在函数组件上提供了defaultProps,它将优先于默认参数值,因为React总是显式地传递defaultProps值(因此参数永远不会是undefined,因此默认参数永远不会被使用)。所以你只能用其中一个,而不是两个。


来自@pamelus对公认答案的评论:

您要么必须使所有接口属性都是可选的(糟糕),要么 还为所有必选字段指定默认值(不需要) boilerplate)或避免在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"
    };

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

注意,要做到这一点,你需要一个比16.4.6更新的@types/react版本。它适用于16.4.11。


对于那些需要默认值的可选道具。图片来源:

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;

功能组件

实际上,对于功能性组件的最佳实践如下所示,我创建了一个示例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;

如果你的组件有子组件,最好使用React。FC,如下:

export interface TypographyProps {
  color?: string;
}

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

export default Typography;

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

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

const defaultProps = {
   optionalText = "foo";
}

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

钩子(带有Typescript)

export interface ApprovalRejectModalProps{
 singleFileApprove:boolean;
}

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

export const ApproveRejectModal:React.FC<ApprovalRejectModalProps>=(props)=>{
return (
        <div>
            ....
        </div>
       )
}

功能组件的可选和默认道具(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>


看看我的解决方案:

interface Props {
  foo?: string | undefined;
  bar: string;
  other?: string;
}

export const Component = ({foo, bar, other = 'default text'}:Props) => {
    console.log(foo, bar, other);
    return(
        ...//some cool stuff your component should do
    )
}

<Component bar='obligatory text'/>