web-dev-qa-db-fra.com

Utilisation de composants stylisés avec des accessoires et du texte

J'essaie d'intégrer TypeScript dans notre projet et jusqu'à présent, je suis tombé sur un problème avec la bibliothèque styled-components

Considérez ce composant

import * as React from "react";
import styled from "styled-components/native";
import { TouchableOpacity } from "react-native";

// -- types ----------------------------------------------------------------- //
export interface Props {
  onPress: any;
  src: any;
  width: string;
  height: string;
}

// -- styling --------------------------------------------------------------- //
const Icon = styled.Image`
  width: ${(p: Props) => p.width};
  height: ${(p: Props) => p.height};
`;

class TouchableIcon extends React.Component<Props> {
  // -- default props ------------------------------------------------------- //
  static defaultProps: Partial<Props> = {
    src: null,
    width: "20px",
    height: "20px"
  };

  // -- render -------------------------------------------------------------- //
  render() {
    const { onPress, src, width, height } = this.props;
    return (
      <TouchableOpacity onPress={onPress}>
        <Icon source={src} width={width} height={height} />
      </TouchableOpacity>
    );
  }
}

export default TouchableIcon;

La ligne suivante génère 3 erreurs, de même nature <Icon source={src} width={width} height={height} />

Tapez {source: any; largeur: chaîne; height: string;} n'est pas assignable au type IntrinsicAttributes ... La propriété 'onPress' est manquante dans le type {source: any; largeur: chaîne; hauteur: chaîne;}

Je ne sais pas exactement de quoi il s'agit et comment y remédier, dois-je en quelque sorte les déclarer sur Icon ou quelque chose de ce genre?

EDIT: TypeScript v2.6.1, styled-components v2.2.3

10
Ilja

Cette réponse est obsolète, la réponse la plus récente est ici: https://stackoverflow.com/a/52045733/1053772

Pour autant que je sache, il n'y a pas (encore?) De moyen officiel de le faire, mais vous pouvez le résoudre avec un peu de ruse. Créez d'abord un withProps.ts fichier avec le contenu suivant:

import * as React from 'react'
import { ThemedStyledFunction } from 'styled-components'

const withProps = <U>() => <P, T, O>(fn: ThemedStyledFunction<P, T, O>) =>
    fn as ThemedStyledFunction<P & U, T, O & U>

export { withProps }

Maintenant, à l'intérieur de votre .tsx fichiers, utilisez-le comme ceci:

// ... your other imports
import { withProps } from './withProps'

export interface IconProps {
  onPress: any;
  src: any;
  width: string;
  height: string;
}

const Icon = withProps<IconProps>()(styled.Image)`
  width: ${(p: IconProps) => p.width};
  height: ${(p: IconProps) => p.height};
`;

Et vous devriez être prêt à partir. C'est certainement pas idéal et j'espère qu'il y aura bientôt un moyen de fournir des génériques aux modèles littéraux dans TS, mais je suppose que pour l'instant c'est votre meilleure option.

Le crédit est accordé lorsque le crédit est dû: j'ai copié ceci à partir de ici

4
juandemarco

Il y a eu quelques développements récents et avec une nouvelle version de TypeScript (par exemple 3.0.1) et des composants de style (par exemple 3.4.5), il n'y a pas besoin d'un assistant séparé. Vous pouvez spécifier directement l'interface/le type de vos accessoires pour les composants de style.

interface Props {
  onPress: any;
  src: any;
  width: string;
  height: string;
}

const Icon = styled.Image<Props>`
  width: ${p => p.width};
  height: ${p => p.height};
`;

et si vous voulez être plus précis et ignorer le onPress

const Icon = styled.Image<Pick<Props, 'src' | 'width' | 'height'>>`
  width: ${p => p.width};
  height: ${p => p.height};
`;
28
elnygren

Je me bats moi-même, mais je pense que le problème est que vous utilisez l'interface Props à l'intérieur du composant stylisé. Essayez de créer une autre interface avec uniquement les accessoires d'image et utilisez-la dans votre composant stylisé:

import * as React from "react";
import styled from "styled-components/native";
import { TouchableOpacity } from "react-native";

// -- types ----------------------------------------------------------------- //
export interface Props {
  onPress: any;
  src: any;
  width: string;
  height: string;
}


export interface ImageProps {
  src: string;
  width: string;
  height: string;
}

// -- styling --------------------------------------------------------------- //
const Icon = styled.Image`
  width: ${(p: ImageProps ) => p.width};
  height: ${(p: ImageProps ) => p.height};
`;

class TouchableIcon extends React.Component<Props> {
  // -- default props ------------------------------------------------------- //
  static defaultProps: Partial<Props> = {
    src: null,
    width: "20px",
    height: "20px"
  };

  // -- render -------------------------------------------------------------- //
  render() {
    const { onPress, src, width, height } = this.props;
    return (
      <TouchableOpacity onPress={onPress}>
        <Icon source={src} width={width} height={height} />
      </TouchableOpacity>
    );
  }
}

export default TouchableIcon;

Semble fonctionner mais je déteste devoir dupliquer ces interfaces. J'espère que quelqu'un d'autre peut montrer la bonne façon ou peut-être intégrer les ImageProps dans les accessoires?

1
thul