Typescript React:访问组件属性类型

Posted

技术标签:

【中文标题】Typescript React:访问组件属性类型【英文标题】:Typescript React: Access component property types 【发布时间】:2017-08-31 01:01:49 【问题描述】:

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

type Props = ...

type State = ...

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

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

在我们声明了这些类型之后,TypeScript 使用它来验证我们组件的使用情况(传递给它的 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, > 

【问题讨论】:

嗨。从 2019 年开始,您应该更愿意使用此解决方案。其他人可以工作,但不是最新的和面向未来的解决方案:***.com/a/55005902/82609 【参考方案1】:

从组件中获取一种属性

type Props = typeof MyComponent.defaultProps;

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

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

【讨论】:

酷!谢谢。虽然这不是获取类型的显式方式,但它现在可以工作(直到作者重构实现)。 不幸的是,这不再有效,因为 defaultProps 现在的类型为 Partial&lt;P&gt; @OliverJosephAsh 这是真的。如果您真的需要,您可以使用模块扩充来扩展反应类型以放置允许您获取道具类型的虚拟类型。在Module Augmentation 部分看这里typescriptlang.org/docs/handbook/declaration-merging.html【参考方案2】:

给定一个 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 发布的。

【讨论】:

【参考方案3】:

从 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 只会在类型表达式中定义 - 所以类型 = 的右侧,或任何你想要的地方通常会像输入函数的参数一样放置一个接口或类型 如果您将其与泛型一起使用,则泛型必须如下所示:T extends React.FC&lt;any&gt; | React.ComponentClass&lt;any&gt;,然后您将能够执行此操作:type PropType = React.ComponentProps&lt;T&gt;;【参考方案4】:

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


查找类型

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

type ViewProps = View['props']

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


React.ComponentProps

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

type ViewProps = React.ComponentProps<typeof View>

type InputProps = React.ComponentProps<'input'>

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

开发者意图更清晰 这将与功能组件和类组件一起使用

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

【讨论】:

你甚至可以通过传递字符串 React.ComponentProps&lt;'input'&gt; 来获得像 &lt;input /&gt; 这样的原生元素的 props。谢谢:) 不幸的是,这不适用于像typeof View&lt;ViewData&gt;这样的通用类型组件 不错的答案!但是它没有考虑defaultProps。这个可以:type MyCompProps = JSX.LibraryManagedAttributes&lt;typeof MyComp, React.ComponentProps&lt;typeof MyComp&gt;&gt;。见github.com/Microsoft/TypeScript/issues/… 如何在通用接口上写这样的东西...孩子们:(道具:React.ComponentProps)=> TComponent;?其中 TComponent 扩展了 React.ReactNode 对于内置元素,你也可以使用:JSX.IntrinsicElements["div"](虽然你可能想使用Omit&lt;..., "ref"&gt;,如果你在你的渲染函数中使用...rest【参考方案5】:

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


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'

【讨论】:

可能PropsOfFC 可能是Parameters&lt;C&gt;[0]

以上是关于Typescript React:访问组件属性类型的主要内容,如果未能解决你的问题,请参考以下文章

在 TypeScript 中使用高阶组件在使用时省略 React 属性

如何使用 TypeScript 以类型安全的方式访问 React 子元素道具?

TypeScript 3.0下react默认属性DefaultProps解决方案

如何在 TypeScript v2.x 中从自定义 React 组件公开“通用”事件

React Native - 如何在Typescript的接口中声明一个StyleSheet属性?

TSLINT 错误:“typeof React”类型上不存在属性“组件”