Режимы машинописного ввода: типы свойств компонента доступа
npm package @types/react
позволяет нам использовать React внутри наших приложений TypeScript. Мы определяем компоненты как
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, {}> {}
Ответы
Ответ 1
2019: заметил, что все ответы выше довольно устарели, поэтому вот свежий.
Тип поиска
В новых версиях TS вы можете использовать типы поиска.
type ViewProps = View['props']
Несмотря на то, что это очень удобно, это будет работать только с компонентами класса.
React.ComponentProps
В React typedefs поставляется утилита для извлечения типа реквизита из любого компонента.
type ViewProps = React.ComponentProps<typeof View>
type InputProps = React.ComponentProps<'input'>
Это немного более многословно, но в отличие от решения поиска типов:
- намерение разработчика более ясно
- это будет работать с обоими функциональными компонентами и компонентами класса
Все это делает это решение наиболее перспективным: если вы решите перейти от классов к перехватчикам, вам не потребуется рефакторинг любого клиентского кода.
Ответ 2
Начиная с 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 через React.ComponentProps
.
Ответ 3
Чтобы взять тип свойств из компонента
type Props = typeof MyComponent.defaultProps;
Вы можете спросить себя, почему я беру typeof из defaultProps, а не из 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 имеют необработанные типы
Ответ 4
С учетом компонента 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.
Ответ 5
Вы можете использовать import * as
синтаксис:
import * as MC from './MyComponent';
type Props = MC.Props;