Je n'arrive pas à comprendre comment définir les valeurs de propriété par défaut de mes composants à l'aide de TypeScript.
C'est le code source:
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>
);
}
}
Et quand j'essaye d'utiliser le composant comme ceci:
ReactDOM.render(<PageComponent />, document.getElementById("page"));
Je reçois une erreur en disant que la propriété foo
est manquante. Je veux utiliser la valeur par défaut. J'ai également essayé d'utiliser static defaultProps = ...
dans le composant, mais cela n'a eu aucun effet, comme je le pensais.
src/TypeScript/main.tsx(8,17): error TS2324: Property 'foo' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<PageComponent> & PageProps & { children?: ReactEle...'.
Comment utiliser les valeurs de propriété par défaut? De nombreux composants JS que mon entreprise utilise dépendent d'eux et ne pas les utiliser n'est pas un choix.
Utiliser static defaultProps
est correct. Vous devriez également utiliser des interfaces, pas des classes, pour les accessoires et l'état.
Mise à jour 2018/12/1: TypeScript a amélioré la vérification de type liée à defaultProps
au fil du temps. Lisez-le pour en savoir plus sur l'utilisation la plus récente jusqu'aux utilisations et problèmes plus anciens. _
TypeScript spécifiquement ajout de la prise en charge de defaultProps
pour que la vérification de type fonctionne comme prévu. Exemple:
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>
);
}
}
Ce qui peut être rendu et compilé sans passer un attribut foo
:
<PageComponent bar={ "hello" } />
Notez que:
foo
est pas marqué comme facultatif (c.-à-d. foo?: string
) même s'il n'est pas requis en tant qu'attribut JSX. Marquer comme optionnel signifierait qu'il pourrait s'agir de undefined
, mais en réalité il ne sera jamais undefined
car defaultProps
fournit une valeur par défaut. Pensez-y de la même manière que vous pouvez marquer un paramètre de fonction comme facultatif ou avec une valeur par défaut, mais pas les deux, mais les deux signifient que l'appel n'a pas besoin de spécifier une valeur . TypeScript 3.0+ traite defaultProps
de la même manière, ce qui est vraiment bien pour les utilisateurs de React!defaultProps
n'a pas d'annotation de type explicite. Son type est déduit et utilisé par le compilateur pour déterminer quels attributs JSX sont requis. Vous pouvez utiliser defaultProps: Pick<PageProps, "foo">
pour vous assurer que defaultProps
correspond à un sous-ensemble de PageProps
. Plus sur cette mise en garde est expliqué ici .@types/react
version 16.4.11
pour fonctionner correctement.Avant TypeScript 3.0 implémenté la prise en charge du compilateur pour defaultProps
, vous pouviez toujours vous en servir, et cela fonctionnait à 100% avec React au moment de l’exécution, mais TypeScript ne prenant en compte que les accessoires lors de la vérification des attributs JSX, vous devez marquer les accessoires avec des valeurs par défaut facultatives. ?
. Exemple:
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>
);
}
}
Notez que:
defaultProps
avec Partial<>
pour qu'il vérifie le type de vos accessoires, mais vous n'êtes pas obligé de fournir toutes les propriétés requises avec une valeur par défaut, ce qui n'a aucun sens, car les propriétés requises ne devraient jamais nécessiter de valeur par défaut.strictNullChecks
, la valeur de this.props.foo
sera possibly undefined
et nécessitera une assertion non nulle (c'est-à-dire this.props.foo!
) ou un garde du type (c'est-à-dire if (this.props.foo) ...
) pour supprimer undefined
. Ceci est gênant puisque la valeur prop par défaut signifie qu’elle ne sera jamais indéfinie, mais TS n’a pas compris ce flux. C'est l'une des principales raisons pour lesquelles TS 3.0 a ajouté un support explicite pour defaultProps
.Cela fonctionne de la même manière, mais vous n'avez pas de types Partial
. Il vous suffit donc d'omettre Partial<>
et de fournir des valeurs par défaut pour toutes les propriétés requises (même si ces valeurs par défaut ne seront jamais utilisées) ou d'omettre complètement l'annotation de type explicite.
Vous pouvez également utiliser defaultProps
sur des composants de fonction, mais vous devez taper votre fonction sur l'interface StatelessComponent
(FunctionComponent
dans @types/react
version 16.7.2
+) afin que TypeScript connaisse l'existence de defaultProps
sur la fonction:
interface PageProps {
foo?: string;
bar: number;
}
const PageComponent: StatelessComponent<PageProps> = (props) => {
return (
<span>Hello, {props.foo}, {props.bar}</span>
);
};
PageComponent.defaultProps = {
foo: "default"
};
Notez qu'il n'est pas nécessaire d'utiliser Partial<PageProps>
n'importe où, car StatelessComponent.defaultProps
est déjà spécifié comme partiel dans TS 2.1+.
Une autre alternative intéressante (c’est ce que j’utilise) consiste à déstructurer vos paramètres props
et à attribuer directement les valeurs par défaut:
const PageComponent: StatelessComponent<PageProps> = ({foo = "default", bar}) => {
return (
<span>Hello, {foo}, {bar}</span>
);
};
Alors vous n’avez pas besoin de la defaultProps
du tout! Sachez que si vous do fournissez defaultProps
sur un composant de la fonction, il sera prioritaire sur les valeurs de paramètre par défaut, car React transmettra toujours explicitement les valeurs defaultProps
(les paramètres ne sont jamais indéfinis; utilisé.) Vous utiliseriez donc l’un ou l’autre, pas les deux.
Avec TypeScript 2.1+, utilisez Partial <T> au lieu de rendre les propriétés de votre interface facultatives.
export interface Props {
obj: Model,
a: boolean
b: boolean
}
public static defaultProps: Partial<Props> = {
a: true
};
Avec TypeScript 3.0, il existe une nouvelle solution à ce problème:
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 />
Notez que pour que cela fonctionne, vous avez besoin d’une version plus récente de @types/react
que 16.4.6
. Cela fonctionne avec 16.4.11
.
D'après un commentaire de @pamelus sur la réponse acceptée:
Vous devez soit rendre toutes les propriétés d'interface facultatives (mauvaises) ou spécifiez également la valeur par défaut pour tous les champs obligatoires (inutile boilerplate) ou évitez de spécifier le type sur defaultProps.
En fait, vous pouvez utiliser l'héritage d'interface de TypeScript . Le code résultant est seulement un peu plus détaillé.
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"
};
Pour ceux qui ont des accessoires optionnels qui nécessitent des valeurs par défaut. Crédit ici :)
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>
)
}
}