Typescript React:访问组件属性类型

IT技术 reactjs typescript
2021-04-13 15:59:17

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

type Props = {...}

type State = {...}

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

这里我们必须声明组件 props 和 state 的类型(在类型变量中)。

在我们声明这些类型之后,TypeScript 使用它来验证我们组件的使用情况(传递给它的 props 的形状)。

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

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

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

但我已经MyComponent从那个文件导入了这个组件已经包含了关于它消耗的 props 的信息(感谢在 中输入变量React.Component)。

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

我想要这样的东西:

import MyComponent from './MyComponent'

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

export default class MyContainer extends React.Component<Props, {}> {}
5个回答

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


查找类型

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

type ViewProps = View['props']

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


React.ComponentProps

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

type ViewProps = React.ComponentProps<typeof View>

type InputProps = React.ComponentProps<'input'>

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

  • 开发者意图更明确
  • 这将适用于功能组件和类组件

所有这一切都使这个解决方案成为最具前瞻性的解决方案:如果您决定从类迁移到钩子,您将不需要重构任何客户端代码。

不错的答案!然而,它没有defaultProps考虑。这个是:type MyCompProps = JSX.LibraryManagedAttributes<typeof MyComp, React.ComponentProps<typeof MyComp>>参见github.com/Microsoft/TypeScript/issues/...
2021-05-25 15:59:17
如何在通用接口上写这样的东西......孩子:(道具:React.ComponentProps<TComponent>)=> TComponent;?其中 TComponent 扩展了 React.ReactNode
2021-05-27 15:59:17
对于内置元素,您还可以使用 :(JSX.IntrinsicElements["div"]尽管您可能想使用Omit<..., "ref">,如果您...rest在渲染函数中使用)
2021-06-10 15:59:17
您甚至可以<input />通过在那里传递字符串来获取本机元素的道具React.ComponentProps<'input'>谢谢 :)
2021-06-14 15:59:17
不幸的是,这不适用于通用类型组件,例如 typeof View<ViewData>
2021-06-20 15:59:17

从 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.

对于那些可能也被我错过的东西绊倒的typescript新手:React.ComponentProps 只会在类型表达式中定义 - 所以类型 = 的右侧,或者你通常会放置接口或类型的任何地方输入函数的参数
2021-05-22 15:59:17
如果您将它与泛型一起使用,则泛型必须如下所示:T extends React.FC<any> | React.ComponentClass<any>然后您将能够执行以下操作:type PropType = React.ComponentProps<T>;
2021-05-29 15:59:17

从组件中获取一种类型的属性

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 有原始类型

凉爽的!谢谢。虽然这不是获取类型的显式方法,但它现在有效(直到作者重构实现)。
2021-05-22 15:59:17
@OliverJosephAsh 是真的。如果您真的需要,您可以使用module扩充来扩展反应类型以放置允许您获取道具类型的虚拟类型。看看这里typescriptlang.org/docs/handbook/declaration-merging.htmlModule Augmentation
2021-06-02 15:59:17
不幸的是,这不再有效,因为defaultProps现在有一种Partial<P>.
2021-06-06 15:59:17

给定一个 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 中

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


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”

可能PropsOfFCParameters<C>[0]
2021-05-31 15:59:17