Typescript: Свойства по умолчанию не работают в функциональном компоненте

Созданный на 4 мая 2019  ·  16Комментарии  ·  Источник: microsoft/TypeScript

Версия TypeScript: 3.4.5

Условия поиска:
реагировать по умолчанию props defaultProps функциональный компонент без состояния

Код

import React from "react";

interface Props {
  name: string;
  optional: string;
}

const Component = ({ name, optional = "default" }: Props) => (
  <p>{name + " " + optional}</p>
);

const Test = () => <Component name="test" />;

Ожидаемое поведение:
Согласно примечаниям к выпуску TypeScript 3.0, свойство optional не должно требоваться в Test поскольку оно было определено по умолчанию с использованием функции инициализаторов по умолчанию ES2015 в Component .

Фактическое поведение:
Выдается следующая ошибка компиляции:

Свойство 'optional' отсутствует в типе '{name: string; } ', но требуется в типе' Props '.

Ссылка на игровую площадку:
Ссылка
Не похоже, что игровая площадка поддерживает TSX.

Связанные вопросы:

27425

Question

Самый полезный комментарий

С TypeScript 3.1 или выше вы должны уметь писать:

export interface Props {
    name: string;
}

function Greet(props: Props) {
    return <div>Hello {props.name.toUpperCase()}!</div>;
}
Greet.defaultProps = {
  name: "world",
} as Partial<Props>; // Good practice, without it you could use a number as default prop and TypeScript wouldn't complain…

const component = <Greet />; // no errors

Все 16 Комментарий

В документации это может быть неясно, но вы должны пометить свойство как необязательное в вашем интерфейсе Props :

interface Props {
    name: string;
    optional?: string;
}

Похоже, это не соответствует тому, что говорится в примечаниях к выпуску:

export interface Props {
    name: string;
}

// ...

function Greet({ name = "world" }: Props) {
    return <div>Hello {name.toUpperCase()}!</div>;
}

Сделать опору необязательной также означает, что при доступе к реквизитам вне компонента (в тесте) система типов не гарантирует, что пропс существует, даже если он будет существовать всегда.

Пример:

const wrapper = shallow(<Test/>);

// optional is string | undefined instead of string
const optional = wrapper
        .find(Component)
        .props()
        .optional;

Если бы optional была функцией, вам нужно было бы проверить ее существование перед ее выполнением. Например, если вы предоставляете опору по умолчанию onClick которая просто вызывает preventDefault .

С TypeScript 3.1 или выше вы должны уметь писать:

export interface Props {
    name: string;
}

function Greet(props: Props) {
    return <div>Hello {props.name.toUpperCase()}!</div>;
}
Greet.defaultProps = {
  name: "world",
} as Partial<Props>; // Good practice, without it you could use a number as default prop and TypeScript wouldn't complain…

const component = <Greet />; // no errors

Это ошибка, что использование функции инициализатора по умолчанию не работает для свойств по умолчанию? Это похоже на регресс, учитывая, что пример, приведенный в примечаниях к выпуску, не работает в последней версии TypeScript.

@tbassetto Проблема в том, что TS не заставляет вас фактически

import React from 'react';

interface Props {
  myProp: string;
}

const Comp = (props: Props) => {
  return (
    <div>
      {props.myProp.toUpperCase()}
    </div>
  );
};

Comp.defaultProps = {

} as Pick<Props, 'myProp'>;

const comp = <Comp />;

Другая неприятная вещь - это то, что вам нужно сделать это в первую очередь: если я наберу Comp как React.FC<Props> , тогда defaultProps набирается хорошо, но возникают другие проблемы .

В общем, поддержка TypeScripts JSX / React сейчас, кажется, находится в странном, не совсем идеальном состоянии, но работает так, как будто все в порядке (по крайней мере, на мой взгляд - в основном, я не могу найти твердого примечания о том, что в настоящее время является «официальным» способом создания компонентов React без сохранения состояния типобезопасным способом для 3.4.5).

Хотя я вижу, что все ссылаются на обходные пути, я не могу заставить их работать.

Я на версии 3.3.3

export interface Props {
  shouldTruncateContent: boolean;
}

const Alert: React.FunctionComponent<Props> = ({
  children,
  shouldTruncateContent = false
}) => {
  return (
        <S.Content shouldTruncateContent={shouldTruncateContent} size="fill">
          {children}
        </S.Content>
  );
};

Alert.defaultProps = {
  shouldTruncateContent: false
} as Pick<Props, 'shouldTruncateContent'>;

export default Alert;

Кто-нибудь знает, почему это все еще вызывает ошибку

TS2741: Property 'shouldTruncateContent' is missing in type '{ children: string; }' but required in type 'Props'.

Я испробовал все исправления, предложенные в # 27425 и # 519.

@tomspeak Либо мой обходной путь, либо woraround от @Hotell (хотя здесь все реквизиты

Хотя я вижу, что все ссылаются на обходные пути, я не могу заставить их работать.

Я на версии 3.3.3

export interface Props {
  shouldTruncateContent: boolean;
}

const Alert: React.FunctionComponent<Props> = ({
  children,
  shouldTruncateContent = false
}) => {
  return (
        <S.Content shouldTruncateContent={shouldTruncateContent} size="fill">
          {children}
        </S.Content>
  );
};

Alert.defaultProps = {
  shouldTruncateContent: false
} as Pick<Props, 'shouldTruncateContent'>;

export default Alert;

Кто-нибудь знает, почему это все еще вызывает ошибку

TS2741: Property 'shouldTruncateContent' is missing in type '{ children: string; }' but required in type 'Props'.

Я испробовал все исправления, предложенные в # 27425 и # 519.

Вам просто нужно сделать shouldTruncateContent: boolean; необязательный shouldTruncateContent?: boolean;

В документации это может быть неясно, но вы должны пометить свойство как необязательное в вашем интерфейсе Props :

interface Props {
    name: string;
    optional?: string;
}

Я использую этот дополнительный тип везде. Почему голосование против? Может кто-нибудь объяснить?

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

Цель проблемы в том, что реквизиты не являются необязательными, но имеют значение по умолчанию (поэтому их можно избежать при использовании компонента). Если вы пометите опору как необязательную с помощью ? тогда TS предполагает, что она может быть неопределенной, и заставит вас обработать этот случай. Однако он никогда не будет неопределенным, потому что есть значение по умолчанию. Голосование против связано с тем, что комментатор, похоже, не понял проблему и предложил что-то, что противоречило примечаниям к версии 3.0.

@RyanCavanaugh, почему бот закрыл эту проблему?

Возможно, я пропустил это, но я не вижу фактического ответа, несмотря на множество обходных путей?

PS: с использованием TypeScript v4

Вы можете сделать это таким образом, здесь свойства в Props являются необязательным параметром и в дальнейшем используются в компоненте.

interface Props {
  children: ReactNode;
  properties?: Record<string, unknown>;
}

const defaultProps: Props = {
  children: '',
  properties: {
    marginTop: 32,
    marginLeft: 0,
    marginBottom: 8,
    marginRight: 0,
  },
};

const RadioListLabel: React.FC<Props> = ({
  children,
  properties = defaultProps.properties,
}) => (
  <View
    margin={[
properties ? properties.marginTop : 0
        properties ? properties.marginLeft : 0
        properties ? properties.marginBottom : 0
        properties ? properties.marginRight : 0
      ]}
  >
    <Text size="m" variant="heading2" lineHeight="body">
      {children}
    </Text>
  </View>
  )

@lakhmeranikita Цель этой проблемы - избежать пометки типа опоры как необязательного.

Мне не удалось получить аргументы функции по умолчанию или другие предложения для правильной проверки типа. Единственное работоспособное решение, которое мне удалось найти, это следующее:

import React from "react";

type DefaultProps = { optionalProp: number };
type Props = {
  requiredProp: "some_string" | "other_string";
} & DefaultProps;

export const BaseComponent = (props: Props) => {
  const { requiredProp, optionalProp } = props;
  return (
    <div>
      {requiredProp} {optionalProp}
    </div>
  );
};

BaseComponent.defaultProps = {
  optionalProp: 0,
} as Partial<DefaultProps>;

const comp = <BaseComponent requiredProp="some_string" />;

Была ли эта страница полезной?
0 / 5 - 0 рейтинги