簡體   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