反应: 访问组件属性类型

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, {}> {}
96469 次浏览

从组件获取属性类型

type Props = typeof MyComponent.defaultProps;

你可以问问自己为什么我从 defaultProps 而不是 propType 中获取 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 具有原始类型

给定一个反应部分:

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上发布了这个。

从 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获得正式的反应类型。

2019年 : 注意到以上所有的答案都很过时,所以这里有一个新的答案。


查找类型

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

type ViewProps = View['props']

尽管非常方便,这将 只能使用类组件


反应,组件道具

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

type ViewProps = React.ComponentProps<typeof View>


type InputProps = React.ComponentProps<'input'>

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

  • 开发商的意图更加明确
  • 这将同时使用功能组件和类组件

所有这些使得这个解决方案成为 最能防止未来发生的事解决方案: 如果您决定从类迁移到钩子,就不需要重构任何客户机代码。

这就是我如何从零件中获得道具的解决方案


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”中