Typescript: Os adereços padrão não funcionam no componente funcional

Criado em 4 mai. 2019  ·  16Comentários  ·  Fonte: microsoft/TypeScript

Versão TypeScript: 3.4.5

Termos de pesquisa:
react default props defaultProps componente funcional stateless

Código

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" />;

Comportamento esperado:
De acordo com as notas de lançamento do optional prop não deve ser exigido em Test , pois foi definido com um padrão usando o recurso de inicializadores padrão ES2015 em Component .

Comportamento real:
O seguinte erro de compilação é gerado:

Propriedade 'opcional' ausente no tipo '{nome: string; } 'mas necessário no tipo' Props '.

Link Playground:
Ligação
Não parece que o playground suporta TSX.

Assuntos relacionados:

27425

Question

Comentários muito úteis

Com TypeScript 3.1 ou superior, você deve ser capaz de escrever:

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

Todos 16 comentários

A documentação pode não ser clara sobre isso, mas você deve marcar a propriedade como opcional em sua interface Props :

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

Isso não parece corresponder ao que dizem as notas de lançamento:

export interface Props {
    name: string;
}

// ...

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

Tornar o adereço opcional também significa que, quando você acessa os adereços fora do componente (em um teste), o sistema de tipos não garante que o adereço exista, embora sempre existirá.

Exemplo:

const wrapper = shallow(<Test/>);

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

Se optional fosse uma função, você teria que verificar se ela existe antes de executá-la. Um exemplo é se você fornecer um onClick prop padrão que apenas chama preventDefault .

Com TypeScript 3.1 ou superior, você deve ser capaz de escrever:

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

É um bug que usar o recurso inicializador padrão não funciona para adereços padrão? Parece uma regressão, visto que o exemplo fornecido nas notas de lançamento não funciona na última versão do TypeScript.

@tbassetto O problema com isso é que o TS não força você a fornecer um valor padrão:

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 />;

A outra coisa irritante é o fato de você ter que fazer isso em primeiro lugar: se eu digitar Comp como React.FC<Props> , então defaultProps está bem digitado, mas outros problemas surgem .

Em suma, o suporte de TypeScripts JSX / React agora parece estar em um estado estranho "não muito perfeito", mas funcionando como se tudo estivesse bem (pelo menos aos meus olhos - principalmente, não consigo encontrar nenhum sólido notas sobre o que atualmente é a maneira "oficial" de fazer componentes sem estado do React de uma maneira segura para o tipo 3.4.5).

Embora eu veja todos fazendo referência a work arounds, não consigo fazer nenhum deles funcionar.

Estou na versão 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;

Alguém tem alguma ideia de por que isso ainda gera o erro

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

Eu tentei todas as correções propostas em # 27425 e # 519 também.

@tomspeak Ou minha solução alternativa ou 's @Hotell woraround (embora este faz todos os adereços opcional). Testado em 3.5 com seu código de amostra.

Embora eu veja todos fazendo referência a work arounds, não consigo fazer nenhum deles funcionar.

Estou na versão 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;

Alguém tem alguma ideia de por que isso ainda gera o erro

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

Eu tentei todas as correções propostas em # 27425 e # 519 também.

Você só precisa fazer shouldTruncateContent: boolean; opcional shouldTruncateContent?: boolean;

A documentação pode não ser clara sobre isso, mas você deve marcar a propriedade como opcional em sua interface Props :

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

Estou usando esse tipo opcional em todos os lugares. Por que o voto negativo? Alguém poderia explicar?

@ralexhassle , não posso explicar os votos negativos, mas posso explicar que é certamente uma forma correta de digitar a propriedade opcional de um componente.
Você pode preferir usar defaultProps , que o exemplo da pergunta não está usando, mas, no que me diz respeito, marcar propriedades opcionais como opcionais é provavelmente perfeitamente adequado ...

O objetivo do problema é que os adereços não são opcionais, mas têm um valor padrão (portanto, podem ser iludidos ao usar o componente). Se você marcar o prop opcional com ? , o TS assume que ele pode ser indefinido e força você a lidar com esse caso. No entanto, ele nunca será indefinido porque há um valor padrão. Os votos negativos são porque o comentarista não pareceu entender o problema e propôs algo que contradiz as notas de lançamento do 3.0.

@RyanCavanaugh por que o bot fechou esse problema?

Talvez eu tenha perdido, mas não vejo uma resposta real, apesar de muitas soluções alternativas.

PS: usando TypeScript v4

Você pode fazer isso desta forma, aqui as propriedades em Props são o parâmetro opcional e posteriormente usado no componente.

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 O objetivo desta edição é evitar marcar um tipo de suporte como opcional.

Não consegui obter argumentos padrão de função ou outras propostas para digitar a verificação corretamente. A única solução viável que consegui encontrar é esta:

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" />;

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

manekinekko picture manekinekko  ·  3Comentários

wmaurer picture wmaurer  ·  3Comentários

uber5001 picture uber5001  ·  3Comentários

weswigham picture weswigham  ·  3Comentários

dlaberge picture dlaberge  ·  3Comentários