Значение свойства по умолчанию в компоненте React с использованием TypeScript

94

Я не могу понять, как установить значения свойств по умолчанию для моих компонентов с помощью Typescript.

Это исходный код:

class PageState
{
}

export class PageProps
{
    foo: string = "bar";
}

export class PageComponent extends React.Component<PageProps, PageState>
{
    public render(): JSX.Element
    {
        return (
            <span>Hello, world</span>
        );
    }
}

И когда я пытаюсь использовать компонент следующим образом:

ReactDOM.render(<PageComponent />, document.getElementById("page"));

Я получаю сообщение об ошибке, когда свойство foo отсутствует. Я хочу использовать значение по умолчанию. Я также пытался использовать static defaultProps = ... внутри компонента, но он не имел никакого эффекта, как я подозревал.

src/typescript/main.tsx(8,17): error TS2324: Property 'foo' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<PageComponent> & PageProps & { children?: ReactEle...'.

Как я могу использовать значения свойств по умолчанию? Многие компоненты JS, которые использует моя компания, полагаются на них и не используют их, не являются выбором.

  • 0
    static defaultProps является правильным. Вы можете опубликовать этот код?
Теги:
tsx

5 ответов

214
Лучший ответ

Реквизит по умолчанию с компонентом класса

Использование static defaultProps является правильным. Вы также должны использовать интерфейсы, а не классы, для реквизита и состояния.

Обновление 2018/12/1: TypeScript улучшил проверку типов, связанную с defaultProps течением времени. Продолжайте читать для последнего и самого лучшего использования вплоть до старых обычаев и проблем.

Для TypeScript 3.0 и выше

В TypeScript специально добавлена поддержка defaultProps чтобы проверка работала так, как вы ожидаете. Пример:

interface PageProps {
  foo: string;
  bar: string;
}

export class PageComponent extends React.Component<PageProps, {}> {
    public static defaultProps = {
        foo: "default"
    };

    public render(): JSX.Element {
        return (
            <span>Hello, { this.props.foo.toUpperCase() }</span>
        );
    }
}

Которые можно рендерить и компилировать без передачи атрибута foo:

<PageComponent bar={ "hello" } />

Обратите внимание, что:

  • foo не помечен как необязательный (т.е. foo?: string), даже если он не требуется в качестве атрибута JSX. Пометка как необязательная означала бы, что она может быть undefined, но на самом деле она никогда не будет undefined поскольку defaultProps предоставляет значение по умолчанию. Думайте об этом подобно тому, как вы можете пометить параметр функции необязательным или значением по умолчанию, но не обоими, но оба означают, что вызову не нужно указывать значение. TypeScript 3. 0+ обрабатывает defaultProps аналогичным образом, что действительно здорово для пользователей React!
  • defaultProps не имеет явной аннотации типа. Этот тип выводится и используется компилятором, чтобы определить, какие атрибуты JSX требуются. Вы можете использовать defaultProps: Pick<PageProps, "foo"> чтобы гарантировать, что defaultProps соответствует подмножеству PageProps. Подробнее об этом предостережение объясняется здесь.
  • Это требует, чтобы @types/react версию 16.4.11 для правильной работы.

Для TypeScript 2.1 до 3.0

До того, как в TypeScript 3.0 была реализована поддержка компилятора для defaultProps вы все еще могли использовать ее, и она работала на 100% с React во время выполнения, но, поскольку TypeScript рассматривал только реквизиты при проверке атрибутов JSX, вы должны пометить реквизиты, которые имеют значения по умолчанию, как необязательные с ? , Пример:

interface PageProps {
    foo?: string;
    bar: number;
}

export class PageComponent extends React.Component<PageProps, {}> {
    public static defaultProps: Partial<PageProps> = {
        foo: "default"
    };

    public render(): JSX.Element {
        return (
            <span>Hello, world</span>
        );
    }
}

Обратите внимание, что:

  • Хорошей идеей будет аннотировать defaultProps с помощью Partial<> чтобы он проверял тип по вашим реквизитам, но вам не нужно указывать для каждого обязательного свойства значение по умолчанию, что не имеет смысла, поскольку для обязательных свойств никогда не требуется значение по умолчанию.
  • При использовании strictNullChecks значение this.props.foo будет possibly undefined и потребует ненулевого утверждения (т.е. this.props.foo!) Или type-guard (т. if (this.props.foo)...) для удалить undefined. Это раздражает, так как значение prop по умолчанию означает, что оно фактически никогда не будет неопределенным, но TS не понимает этот поток. Это одна из главных причин, по которой в TS 3.0 добавлена явная поддержка defaultProps.

До TypeScript 2.1

Это работает так же, но у вас нет Partial типов, поэтому просто опустите Partial<> и либо предоставьте значения по умолчанию для всех необходимых реквизитов (даже если эти значения по умолчанию никогда не будут использоваться), либо полностью пропустите явную аннотацию типа.

Стандартный реквизит с функциональными компонентами

Вы также можете использовать defaultProps для компонентов функций, но вам нужно ввести свою функцию в интерфейс StatelessComponent (FunctionComponent в @types/react версию 16.7.2 +), чтобы TypeScript знал о defaultProps для функции:

interface PageProps {
  foo?: string;
  bar: number;
}

const PageComponent: StatelessComponent<PageProps> = (props) => {
  return (
    <span>Hello, {props.foo}, {props.bar}</span>
  );
};

PageComponent.defaultProps = {
  foo: "default"
};

Обратите внимание, что вам не нужно нигде использовать Partial<PageProps> потому что StatelessComponent.defaultProps уже определено как частичное в TS 2. 1+.

Другая хорошая альтернатива (это то, что я использую) - это деструктурировать параметры вашего props и напрямую назначить значения по умолчанию:

const PageComponent: StatelessComponent<PageProps> = ({foo = "default", bar}) => {
  return (
    <span>Hello, {foo}, {bar}</span>
  );
};

Тогда вам не нужны defaultProps ! Имейте в виду, что если вы предоставите defaultProps для компонента функции, он будет иметь приоритет над значениями параметров по умолчанию, поскольку React всегда будет явно передавать значения defaultProps (поэтому параметры никогда не определяются, поэтому параметр по умолчанию никогда не используется.) Итак, вы ' буду использовать один или другой, а не оба.

  • 0
    Я ctrl + c / ctr + v'd ваш код (и исправил точку с запятой, которая не должна быть там), и это дает мне ту же ошибку: src / typescript / main.tsx (8,17): ошибка TS2324: Свойство 'foo' отсутствует в типе 'IntrinsicAttributes & IntrinsicClassAttributes <PageComponent> & PageProps & {children ?: ReactEle ...'.
  • 2
    Ошибка звучит так, как будто вы используете <PageComponent> где-то без пропуска foo prop. Вы можете сделать это необязательным, используя foo?: string в вашем интерфейсе.
Показать ещё 17 комментариев
16

С Typescript 2.1+ используйте Partial <T> вместо того, чтобы сделать ваши свойства интерфейса необязательными.

export interface Props {
    obj: Model,
    a: boolean
    b: boolean
}

public static defaultProps: Partial<Props> = {
    a: true
};
5

С TypScript 3.0 есть новое решение этой проблемы:

export interface Props {
    name: string;
}

export class Greet extends React.Component<Props> {
    render() {
        const { name } = this.props;
        return <div>Hello ${name.toUpperCase()}!</div>;
    }
    static defaultProps = { name: "world"};
}

// Type-checks! No type assertions needed!
let el = <Greet />

Обратите внимание, что для этого вам потребуется более новая версия @types/react 16.4.6 чем 16.4.6. Он работает с 16.4.11.

  • 0
    Отличный ответ! Как можно обрабатывать: export interface Props { name?: string;} где имя - это дополнительная опора? Я продолжаю получать TS2532 Object is possibly 'undefined'
  • 0
    @ Фидо Мне не нужно было иметь значение по умолчанию для необязательного реквизита, поскольку undefined является своего рода автоматическим значением по умолчанию для этих реквизитов. Вы хотите иметь возможность передавать undefined как явное значение иногда, но есть другое значение по умолчанию? Вы пробовали export interface Props {name: string | undefined;} вместо? Я не пробовал это, просто идея.
Показать ещё 2 комментария
3

Из комментария @pamelus о принятом ответе:

Вам либо нужно сделать все свойства интерфейса необязательными (плохими), либо укажите значение по умолчанию также для всех обязательных полей (ненужных шаблон) или не указывать тип в файлах defaultProps.

На самом деле вы можете использовать Typescript наследование интерфейса. Полученный код будет немного более подробным.

interface OptionalGoogleAdsProps {
    format?: string;
    className?: string;
    style?: any;
    scriptSrc?: string
}

interface GoogleAdsProps extends OptionalGoogleAdsProps {
    client: string;
    slot: string;
}


/**
 * Inspired by https://github.com/wonism/react-google-ads/blob/master/src/google-ads.js
 */
export default class GoogleAds extends React.Component<GoogleAdsProps, void> {
    public static defaultProps: OptionalGoogleAdsProps = {
        format: "auto",
        style: { display: 'block' },
        scriptSrc: "//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"
    };
0

Для тех, у кого есть дополнительные реквизиты, которым нужны значения по умолчанию. Кредит здесь :)

interface Props {
  firstName: string;
  lastName?: string;
}

interface DefaultProps {
  lastName: string;
}

type PropsWithDefaults = Props & DefaultProps;

export class User extends React.Component<Props> {
  public static defaultProps: DefaultProps = {
    lastName: 'None',
  }

  public render () {
    const { firstName, lastName } = this.props as PropsWithDefaults;

    return (
      <div>{firstName} {lastName}</div>
    )
  }
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню