繁体   English   中英

打字稿React:访问组件属性类型

[英]Typescript React: Access component property types

npm包@types/react允许我们在TypeScript应用程序内部使用React。 我们将组件定义为

type Props = {...}

type State = {...}

export default class MyComponent extends React.Component<Props, State> {
}

在这里,我们必须声明组件属性和状态的类型(在类型变量中)。

在声明了这些类型之后,TypeScript将使用该类型来验证组件的用法(传递给它的道具的形状)。

我想围绕这样的组件创建一个容器。 容器将重复使用组件的道具。 但是,为了用相同的道具创建另一个组件,我必须再次声明道具的类型。 或从原始组件文件中导出它们并导入到容器中:

// original file
export type Props = {...}

// container file
import MyComponent, { Props } from './original'

但是我已经从该文件导入MyComponent 这个组件已经包含了消耗道具的信息(感谢在React.Component输入变量)。

问题是如何在不显式导出/导入道具类型的情况下从组件类本身访问该信息

我想要类似的东西:

import MyComponent from './MyComponent'

type Props = MyComponent.Props // <= here access the component prop types

export default class MyContainer extends React.Component<Props, {}> {}

2019 :注意到上面的所有答案都已经过时了,所以这里是一个新鲜的答案。


查询类型

使用较新的TS版本,您可以使用查找类型。

type ViewProps = View['props']

尽管非常方便,但这仅适用于类组件


React.ComponentProps

React typedef附带了一个实用程序,可从任何组件中提取道具的类型。

type ViewProps = React.ComponentProps<typeof View>

type InputProps = React.ComponentProps<'input'>

这有点冗长,但是与类型查找解决方案不同:

  • 开发人员的意图更加明确
  • 这将与功能组件和类组件一起使用

所有这些使该解决方案成为最可靠的解决方案:如果您决定从类迁移到挂钩,则无需重构任何客户端代码。

从TypeScript 2.8开始,可以使用条件类型,例如:

interface MyComponentProps { bar: string; }
declare const MyComponent: React.Component<MyComponentProps>;

interface MyComponentClassProps { bar: string; }
declare const MyComponentClass: React.ComponentClass<MyComponentClassProps>;

interface MyStatelessComponentProps { bar: string; }
declare const MyStatelessComponent: React.StatelessComponent<MyStatelessComponentProps>;

我们可以定义以下助手:

type GetComponentProps<T> = T extends React.ComponentType<infer P> | React.Component<infer P> ? P : never

并像这样使用它们:

// $ExpectType MyComponentProps
type MyComponentPropsExtracted = GetComponentProps<typeof MyComponent>

// $ExpectType MyComponentClassProps
type MyComponentClassPropsExtracted = GetComponentProps<typeof MyComponentClass>

// $ExpectType MyStatelessComponentProps
type MyStatelessComponentPropsExtracted = GetComponentProps<typeof MyStatelessComponent>

更新2018-12-31 :现在可以通过React.ComponentProps在官方的React React.ComponentProps

从组件中获取一种属性

type Props = typeof MyComponent.defaultProps;

您可以问自己为什么我要从defaultProps而不是propTypes获取typeof。 为了说明这一点,让我们看一下定义文件

  interface ComponentClass<P> {
        new(props?: P, context?: any): Component<P, ComponentState>;
        propTypes?: ValidationMap<P>;
        contextTypes?: ValidationMap<any>;
        childContextTypes?: ValidationMap<any>;
        defaultProps?: P;
        displayName?: string;
    }

如您所见,propTypes包装在ValidationMap中,获取原始类型并不容易。 幸运的是,defaultProps具有原始类型

给定一个React组件:

import React, { ComponentType, StatelessComponent } from 'react';

const MyComponent: StatelessComponent<{ foo: string }> = props => <div>{props.foo}</div>;

你可以做:

const getProps = function<Props> (_MyComponent: ComponentType<Props>): Props {
  return {} as Props;
};
const props = getProps(MyComponent);

// { foo: string; }
type MyComponentProps = typeof props;

另外,您可以增加React类型以添加GetComponentProps帮助器:

import React from 'react';

type NonNullable < T > = T & {};

declare module 'react' {
  // Add helper for accessing props type of given component. Based off of
  // https://github.com/DefinitelyTyped/DefinitelyTyped/pull/24182.
  type GetComponentProps < C extends ComponentType < any > > = NonNullable<C['_doNotUse_props']>;

  // We use interface merging to append properties to these types
  interface StatelessComponent<P = {}> {
    // eslint-disable-next-line camelcase
    _doNotUse_props?: P;
  }
  interface ComponentClass<P = {}> {
    // eslint-disable-next-line camelcase
    _doNotUse_props?: P;
  }
}

用法如下所示:

// { foo: string; }
type MyComponentProps = React.GetComponentProps<typeof MyComponent>;

我最初将此内容发布在https://github.com/DefinitelyTyped/DefinitelyTyped/pull/24182中

这是我如何从组件中获取道具的解决方案


type Propsable = {
  FC: React.FC;
  C: React.Component;
  CC: React.ComponentClass<any>;
  F: (...args: any) => any;
}
type PropsOfFC<C extends Propsable["FC"]> = {
  [K in keyof C["propTypes"]]: C["propTypes"][K] extends React.Validator<infer P>
    ? P
    : K
};
type PropsOfF<C extends Propsable["F"]> = Parameters<C>[0]
type PropsOfC<C extends Propsable["C"]> = C extends React.Component<infer P> ? P : never;
type PropsOfCC<C extends Propsable["CC"]> = C extends React.ComponentClass<infer P> ? P : never;


type PropsOf<C extends ValueOf<Propsable>> =
  C extends Propsable["FC"] ? PropsOfFC<C> :
  C extends Propsable["C"] ? PropsOfC<C> :
  C extends Propsable["CC"] ? PropsOfCC<C> :
  C extends Propsable["F"] ? PropsOfF<C> : any;


如果您使用功能组件,类组件或样式化组件,则该解决方案应为您提供帮助。
如何使用:

type Props = PropsOf<typeof YourComponent>

您可以将其添加到“ react.d.ts”

您可以使用import * as语法:

import * as MC from './MyComponent';

type Props = MC.Props;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM