Material-ui: Error de tipo de mecanografiado en el componente devuelto por withStyles ()

Creado en 28 sept. 2017  ·  55Comentarios  ·  Fuente: mui-org/material-ui

Cuando uso withStyles() hoc en mecanografiado, obtengo el siguiente error al intentar usar el componente devuelto:

Type '{}' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<App> & Readonly<{ children?: ReactNode; }> & Reado...'.
  Type '{}' is not assignable to type 'Readonly<WithStyles<"main">>'.
    Property 'classes' is missing in type '{}'.
  • [x] He buscado las ediciones de este repositorio y creo que esto no es un duplicado.

Parece que este cambio en la definición de tipo podría estar relacionado con este problema.

Comportamiento esperado

Dado el código del componente App continuación, debería poder usar el componente <App /> sin el error de tipo como hice en 1.0.0-beta.10.

Comportamiento actual

Dado el código del componente App continuación, intentar usar <App /> da como resultado el error mencionado anteriormente.

El código

import * as React from 'react';
import { withStyles } from 'material-ui/styles';

const styles = {
    main: {
        marginTop: 48,
        padding: 10,
    },
    foo: {
        margin: 0,
    },
};

interface Props {
    message: string;
};

type ClassNames = { classes: { [className in keyof typeof styles]: string } };

class App extends React.Component<Props & ClassNames> {
    render() {
        const { classes, message } = this.props;
        return (
            <div className={classes.main}>
                <div className={classes.foo} >
                    Hello World! {message}
                </div>
            </div>
        );
    }
}

export default withStyles(styles)(App);

Contexto

El código funcionó bien en 1.0.0-beta.10, cuando actualicé a 1.0.0-beta.12 obtuve el error de tipo.

En el fragmento de código proporcionado, utilicé el truco keyof typeof styles para no tener que definir una lista de nombres de clases dos veces (no me gusta mucho la repetición). También probé otras variaciones:

type ClassNames = WithStyles<keyof typeof styles>;

y hacerlo de la manera más común (como se ve en styles.spec.tsx ):

type ComponentClassNames = 'main' | 'foo';
type ClassNames = WithStyles<ComponentClassNames>;

Sigo recibiendo el mismo error.

Parece que la definición de tipo anterior devolvería un componente cuyo tipo de accesorios sería StyledComponentProps que tiene una propiedad opcional classes . La nueva definición ...

<P, C extends React.ComponentClass<P & StyledComponentProps<Names>>>(
    component: C
  ): C;

... devuelve el mismo tipo C que el componente, esto significa que pasar ClassNames que no está marcado como opcional se propaga al componente devuelto. Veo mencionado aquí el uso de Partial<> que creo que es un truco antiestético.

Tu entorno

| Tech | Versión |
| -------------- | --------- |
| Material-UI | 1.0.0-beta.12 |
| Reaccionar | 15.6.1 |
| navegador | Chrome 61.0.3163.100 |

discussion typescript

Comentario más útil

Lo resolví usando recomponer

ejemplo

import { StyleRules, Theme, withStyles } from "material-ui/styles";
import * as React from "react";
import { compose } from "recompose";

interface IProps {
    classes?: any; // <-- add question mark
    text: string;
}

const styles = (theme: Theme): StyleRules => ({
    root: {

    },
});

@compose(
    withStyles(styles),
)
export class MyComponent extends React.Component<IProps> {
    render() {
        const { classes, text } = this.props;
        return (
            <div className={classes.root}>
                {text}
            </div>
        );
    }
}

Todos 55 comentarios

@cfilipov al decorar una clase de componente, debe usar StyledComponentProps lugar de WithStyles , y si está en modo de verificación de tipo estricto, debe usar el operador de aserción no nula ! para extraer los campos de classes . Este es un compromiso para permitir el uso de withStyles como decorador de clases. Los decoradores de clases en TypeScript sufren la limitación de que su tipo de retorno debe coincidir con el tipo de argumento. Dadas estas limitaciones, solo es posible una opción:

  1. no admite el uso de withStyles como decorador de clases
  2. requiere que pase un classes prop ficticio al construir un elemento del tipo de componente decorado (el compromiso anterior, que podría decirse que era más molesto )
  3. requerir que classes se considere anulable dentro del componente, lo que le obliga a usar el operador ! al acceder a sus campos ( el compromiso actual )

Recomiendo encarecidamente que si su componente no tiene estado, utilice un componente funcional sin estado, que requerirá menos anotaciones de tipo y será más seguro para los tipos:

export default withStyles(styles)<Props>(({ classes, message }) => (
  <div className={classes.main}>
    <div className={classes.foo}>
      Hello World! {message}
    </div>
  </div>
));

Veo mencionado aquí el uso de Partial <>, que creo que es un truco antiestético.

Si lees mi seguimiento de ese comentario, verás que Partial<> no es obligatorio.

Si lee mi seguimiento de ese comentario, verá que Parcial <> no es obligatorio.

Vi su comentario sobre Partial<> . Usar StyledComponentProps esencialmente equivale a lo mismo (está usando Partial<> en esa definición). Mi queja con eso ahora es que se debe acceder a los nombres de clases con ! (como mencionaste). Creo que pasar un classes prop ficticio o requerir el uso de ! son compromisos pobres.

Para aclarar, supongo que el problema principal que tengo aquí es la opción de introducir una regresión al hoc en un esfuerzo por evitar una limitación de una característica experimental de la escritura mecanografiada. Admito que tengo algunos prejuicios, ya que no me importan los decoradores, mientras que a otros claramente sí, pero la compensación aquí no tiene sentido para mí.

@cfilipov mi primer refactor de la mecanografía withStyles fue elegir la opción (1), es decir, hacer que la mecanografía sea completamente correcta para los componentes de clase y función, a expensas de usar withStyles como decorador de clase . Luego recibí comentarios de que usar la sintaxis del decorador era una solicitud popular, así que cambié a la opción (3). Estoy feliz de revisar esa decisión; Yo también preferiría la seguridad de tipos a apoyar a los decoradores en su estado semifuncional actual.

Sí, entiendo el deseo de apoyar una solicitud popular como esta. También quiero usar decoradores, pero sigo teniendo muchos problemas con ellos cada vez que lo hago (fuera de mui), así que personalmente decidí no usarlos hasta que estén listos.

Parece que comparte mi preocupación y no tengo mucho más que agregar, así que espero que otras personas puedan brindar comentarios en esta dirección para ayudar a influir.

Cambié a beta.13 desde beta.10 para ver si algo había cambiado y sí, este es un problema real. Para tirar mis dos centavos aquí, para mí, los decoradores son experimentales. Obviamente, podrían cambiarse en el futuro. Y hasta entonces apoyaría totalmente la forma 100% precisa. Preferiría tener una seguridad de tipo coherente para piratear mis tipos para que las cosas funcionen.

8550 parece una prueba más de que la gente está confundida por esto, y deberíamos considerar no admitir @withStyles() como decorador (en TypeScript).

Así es como se vería "decorar" una clase si hiciéramos la escritura correcta:

type NonStyleProps = {
  text: string
};

const styles = {
  root: {
    backgroundColor: 'red'
  }
};

const DecoratedComponent = withStyles(styles)(
  class extends React.Component<NonStyleProps & WithStyles<'root'>> {
    render() {
      return (
        <div className={this.props.classes.root}>
          {this.props.text}
        </div>
      );
    }
  }
);

@pelotom Todavía tengo que avanzar desde beta.10 debido a este problema. Hice un comentario sobre el estilo de una clase con el método de conexión de Redux antes. Creo que es relativamente fácil y robusto. en # 8059 el tercer comentario soy yo mismo con los tipos.

@pelotom Muchas gracias por informarnos sobre este tema. Soy un gran usuario de decoradores TS, pero en ese caso, me complacería que withStyles abandonara su soporte de decoración para tener una mejor seguridad de tipos.

deberíamos considerar no admitir @withStyles () como decorador (en TypeScript).

@pelotom Personalmente estoy a favor de este cambio. @sebald ¿Qué quieres hacer aquí?

Es un cambio simple; Seguí adelante y abrí un PR en caso de que quieras ir con él 🙂

@pelotom, ¿ seguirían funcionando los mecanografiados si los usara así?

interface IStyles {
    // styles interface
}
interface IHeaderInfoStyles {
    classes: any;
}
interface IHeaderInfoProps {
    // properties resulting from dispatches
}
interface IHeaderInfoInjectedProps {
   // props injected from parent if any
}
interface IHeaderInfoDispatchProps {
    // dispatches
}
interface IHeaderInfoState {
    // your state for the class
}

type HeaderInfoProps = IHeaderInfoProps & IHeaderInfoInjectedProps & IHeaderInfoDispatchProps & IHeaderInfoStyles;

class HeaderInfo extends Component<HeaderInfoProps, IHeaderInfoState> {

export const HeaderInfo_container = withStyles<IHeaderInfoInjectedProps, IStyles>(styles)(
    connect<IHeaderInfoProps, IHeaderInfoDispatchProps, (IHeaderInfoInjectedProps & IHeaderInfoStyles)>(mapStateToProps, mapDispatchToProps)(HeaderInfo),
);

@marcusjwhelan withStyles ya no toma 2 parámetros de tipo y no debería tener que proporcionar un parámetro de tipo para los estilos (se puede inferir de styles ). En su lugar, debería poder escribir

withStyles(styles)<NonStyleProps>(...);

Si da los tipos para mapStateToProps y mapDispatchToProps , podría mostrarle cómo se vería esto para su ejemplo.

tl; dr; Hagamos los cambios y veamos cuál es la reacción, supongo 👷🏻


@oliviertassinari @pelotom ¯_ (ツ) _ / ¯ Yo no uso decoradores, así que personalmente, realmente no me importa este cambio, porque no me afecta. Pero parece que a mucha gente le importa esta "función". Es por eso que lo agregamos en primer lugar. Eso es en mi humilde opinión lo que tiene prioridad aquí.

Estoy muy contento con los cambios de withStyles , pero cuando echas un vistazo a otras bibliotecas más funcionales, como ramda o recompose , la mecanografía no es tan estricta, tampoco son súper seguros para los tipos. Muchas veces es necesario pasar un genérico, que representa el valor de retorno de una función. No es bonito, pero funcionará para un 99,9% de los usuarios.

¿Por qué no podemos recuperar los viejos mecanografiados que funcionaban para las personas que usaban decoradores? Debido a que tenía dos genéricos, es posible que podamos tener ambos tipos uno al lado del otro.

Además, estoy un poco confundido sobre cuáles son los problemas de las personas con los decoradores (con respecto a "no funciona"). Libs como Angular y https://nestjs.com/ hacen un uso intensivo de ellos después de todo. En nuestro caso, la gente podría simplemente agregar WithStyles a los accesorios y está bien.

@sebald

¿Por qué no podemos recuperar los viejos mecanografiados que funcionaban para las personas que usaban decoradores? Debido a que tenía dos genéricos, es posible que podamos tener ambos tipos uno al lado del otro.

No estoy seguro de lo que quieres decir. Nunca hubo mecanografía que permitiera a las personas usar decoradores sin dolor. La primera implementación sufrió de # 8267, que era que no se podía construir un elemento de un componente decorado sin pasar un classes prop ficticio, por ejemplo, <StyledComp classes={{} as any} /> . La segunda implementación (actual) sufre el problema inverso, que classes se considera potencialmente undefined dentro de la clase de componente. Si desea utilizar decoradores de TypeScript en su forma actual, esas son sus únicas 2 opciones; elige tu veneno.

La tercera forma es usar el tipo correcto, de modo que classes se defina dentro de la clase pero no se requiera pasar como un accesorio. Para tener ambas condiciones, debe renunciar a los decoradores.

@pelotom Sí, lo siento. Tienes razón. Realmente no es mi día ... 🤐 ¡Así que fusionémonos!

@sebald

Además, estoy un poco confundido sobre cuáles son los problemas de las personas con los decoradores (con respecto a "no funciona"). Libs como Angular y https://nestjs.com/ hacen un uso intensivo de ellos después de todo.

No estoy seguro de cómo se usan en Angular, pero ciertamente hay casos de uso en los que los decoradores se pueden usar sin dolor; Básicamente, si el decorador no necesita cambiar el tipo de clase que está decorando, funcionan bien. Esa no es la situación que tenemos aquí; withStyles necesita cambiar el tipo de componente que decora.

@pelotom Sí, exactamente. Es solo la mutación lo que es malo. En realidad, la forma en que TS actualmente implementa decoradores podría incluso ser buena para los usuarios de Angular, ya que los decoradores AFAIK establecieron contratos con el marco, como "registre esta clase como un componente" o "agregue metadatos para que pueda usar esto en el DI "... Dios incluso escribir sobre esto me hace sentir 🤢

@pelotom, la razón por la que tengo los tipos de la forma en que los hago es que proporciona seguridad de tipos para mis componentes. Actualmente, los tipos no tienen seguridad de tipos cuando se trata de componentes. Los injectedProps en mi ejemplo son los tipos que requiere el componente para funcionar. Los tipos de conexión de react-redux deben ser los de Props que provienen de mapStateToProps y DispatchProps que provienen de mapDispatchToProps .

Al final, debe haber injectedProps para que mi componente principal sepa que necesito inyectarlos o mi proyecto no se compilará. (esto es lo que quiero). ¿Este cambio va a migrar de nuevo a este tipo de typeSafety?

@marcusjwhelan nuevamente, si puede proporcionar un ejemplo completo (pero mínimo) que se estaba compilando en la versión beta 10, puedo mostrarle cómo migrar. AFAIK, la nueva versión debería ser tan expresiva y más segura como antes.

@pelotom Pregunta tonta, ¿hay alguna manera de que me notifiquen una vez que se realice un nuevo lanzamiento?

@wcandillon Síganos en Twitter.

@pelotom Publiqué un ejemplo arriba ... ¿Por qué necesitarías ver más que eso? Puede asumir que las propiedades son a, b, c, d, e. Lo único es que los accesorios inyectados deben emitirse como requisito.

Editar

Me lo imaginé.

Estoy interviniendo en esta discusión un poco tarde, pero he estado viendo un error similar al mencionado aquí, y todavía tengo que determinar cómo puedo corregirlo. Estoy usando [email protected] y [email protected] .

const LoginForm = withStyles(styles)(
    class extends React.Component<WithStyles<'root' | 'button'>, ILoginFormState> {
    // ...
    }
);

render(
  <MuiThemeProvider theme={theme}>
    <LoginForm />
  </MuiThemeProvider>,
  document.getElementById('login-form')
);

El mensaje de error que recibo en este caso particular es:

Type '{}' is not assignable to type 'IntrinsicAttributes & WithStyles<"root" | "button"> & StyledComponentP...'.
  Type '{}' is not assignable to type 'WithStyles<"root" | "button" | "progress" | "textField">'.
    Property 'classes' is missing in type '{}'.
 not assignable to type 'WithStyles<"root" | "button" | "progress" | "textField">'.
    Property 'classes' is missing in type '{}'.

al decorar una clase de componente, debe usar StyledComponentProps en lugar de WithStyles

@pelotom - ¿hay algún ejemplo de esto en alguna parte? Tengo muchos problemas para determinar cómo diseñar una clase de componente con estado en TypeScript.

@iamjem es un poco difícil de decir ya que no ha proporcionado styles , pero parece que el primer problema es que tiene más claves de clase mencionadas en styles de las que menciona en WithStyles<...> . Creo que si lo cambias a

const LoginForm = withStyles(styles)(
    class extends React.Component<WithStyles<keyof typeof styles>, ILoginFormState> {
    // ...
    }
);

debería ocuparse del primer problema. Entonces parece que hay un segundo problema, que es que el tipo resultante de LoginForm es

React.ComponentType<WithStyles<"root" | "button"> & StyledComponentProps<"root" | "button">>

lo cual obviamente no es correcto; parece que la ausencia de accesorios sin estilo confunde el sistema de tipos. Puede ayudarlo siendo explícito sobre qué son los accesorios que no son de estilo, pasando {} como un argumento de tipo:

const LoginForm = withStyles(styles)<{}>( // <-- note the type argument
    class extends React.Component<WithStyles<keyof typeof styles>, ILoginFormState> {
    // ...
    }
);

Lo siento, es tan complicado, ¡ojalá supiera una manera más fácil de hacer que esto funcione!

¡Eso funcionó! Gracias por la ayuda rápida @pelotom. He estado usando React durante mucho tiempo, pero recientemente me lancé a Material-ui y pensé en probar Typecript mientras lo hago. No hace falta decir que descubro que hay algunos casos extremos en los que es difícil saber cómo hacer feliz a Typecript.

Lo resolví usando recomponer

ejemplo

import { StyleRules, Theme, withStyles } from "material-ui/styles";
import * as React from "react";
import { compose } from "recompose";

interface IProps {
    classes?: any; // <-- add question mark
    text: string;
}

const styles = (theme: Theme): StyleRules => ({
    root: {

    },
});

@compose(
    withStyles(styles),
)
export class MyComponent extends React.Component<IProps> {
    render() {
        const { classes, text } = this.props;
        return (
            <div className={classes.root}>
                {text}
            </div>
        );
    }
}

Estaba luchando con este problema (y el número 8704), sin un resultado claro en los últimos días. Luego seguí el consejo de @pelotom :

debe usar StyledComponentProps lugar de WithStyles

y busqué en GitHub formas similares de resolver esto. Y encontré UN ejemplo de trabajo 😂. Sin embargo, es bueno y resolvió mi problema: contenedor y componente separados con TypeScript como una cookie feliz: ejemplo mencionado aquí ( Nota: en mi caso, el mapeo de componentes está en un archivo contenedor separado, pero la idea es la mismo.).

Si alguien piensa que esta es una mala solución, estoy abierto a cualquier cambio e idea. Ahora mismo, me alegro de que mi código haya dejado de quejarse.

type Styles = 'foo';
const styles: StyleRulesCallback<Styles> = (theme: Theme) => ({
    foo: {
        position: 'relative',
    }
});

interface Props {
  something: string;
}

class Sidebar extends React.Component<Props & WithStyles<Styles>> {
    render() {
        const { classes, something } = this.props;

        return (
                    <div className={classes.foo}>{something}</div>
        );
    }
}

export default withStyles(styles)<Props>(Sidebar);

No quiero crear un nuevo problema, pero he intentado todo lo que vi en la documentación, el ejemplo y los problemas pasados, incluso con la recomposición, pero no puedo hacer que mi componente funcione cuando le agrego algunas propiedades.
Y los recursos que encontré son en su mayoría con versiones anteriores de TS, MUI o incluso React.

Aquí está mi componente:

import React from 'react';
import AppBar from 'material-ui/AppBar';
import { withStyles, WithStyles, StyleRulesCallback } from 'material-ui/styles';

const styles: StyleRulesCallback<'positionFixed'> = () => ({
  positionFixed: {
    top: 'auto',
    bottom: 0,
  },
});

const decorate = withStyles(styles);

interface Props {
   someProp: string;
};

export const BottomNav = decorate<Props>(
  class extends React.Component<Props & WithStyles<'positionFixed'>, {}> {
    render() {
      return (
        <AppBar />
      );
    }
  }
);

export default BottomNav;

Y el error es:

TS2322: Type '{}' is not assignable to type 'IntrinsicAttributes & Props & StyledComponentProps<"positionFixed"> & { children?: ReactNode; }'.
  Type '{}' is not assignable to type 'Props'.
    Property 'someProp' is missing in type '{}'.

Soy bastante principiante con TS, pero encuentro la página de documentación y el ejemplo bastante confuso y / o incompleto.

Si tenéis alguna idea, gracias ;-)

@otroboe, ¿ está

<BottomNav />

Si es así, el problema es que debe proporcionar someProp prop (que se requiere de acuerdo con su definición de Props ):

<BottomNav someProp="foo" />

Qué vergüenza ... Oh, qué vergüenza.
Estaba tan concentrado en la integración de TS que me olvidé de mirar a mi alrededor y dar algunos pasos hacia atrás.

Muchas gracias @pelotom : smile:

@otroboe También elimine la duplicación de cadenas ...

type Styles = 'positionFixed';

Ojalá fuera más fácil aunque ...

Sí, también lo hice, gracias: +1:

Acabo de enfrentar este mismo problema, pero resulta que ocurre solo si he inicializado el objeto de estilos en el mismo archivo que mi clase o función. Alternativamente, si estoy importando los estilos de otro archivo, no obtengo este error.

¿Alguna pista de por qué sucede esto?

@ nishmeht7 ¿puedes publicar un fragmento independiente?

@pelotom Acabo de trabajar en hacer uno y funciona bien en mi entorno de caja de arena. Actualmente estoy trabajando en una aplicación grande y todavía estoy en la versión 1.2.2 de mui, mientras que mi versión env de sandbox es la más reciente. Así que supongo que una vez que actualice las versiones no podré reproducir mi error.

Seguí el ejemplo de un formulario básico de https://material-ui.com/demos/selects/ pero recibí quejas de que theme no tiene root (usando el último mecanografiado y material- ui), y no se estaba aplicando ninguna clase al elemento form . Intenté seguir la discusión anterior, pero no parece concluyente. Y, de hecho, a la lista de clases heredadas le faltaba el nombre de la clase generada para form . Si he añadido el nombre de clase generada manualmente (que se encuentra en withStyles con console.log(theme) en herramientas dev inspeccione elementos, todos muy bien, por lo que aparentemente la clase se generaba correctamente. No estaba recibiendo pasado por withStyles a la form embargo, el elemento

Así que volví a los estilos por ahora hasta que esto se solucione:

<form style = {{display:'flex',flexWrap:'wrap'}} autoComplete="off">

@HenrikBechmann ¿Ha intentado seguir la documentación de estilos para mecanografiar? En el pasado, me ha sido de gran ayuda. https://material-ui.com/guides/typescript/

¡Gracias @lookfirst! Miré (aunque no primero :-)) ese documento y usé

export default withStyles({
  root: {
    display: 'flex',
    flexWrap: 'wrap',
  },
})(BaseForm)

(pasando el objeto en lugar de la función)

... que evitó los errores de mecanografía y permitió la inyección de la clase generada.

Es de esperar que los otros consejos ayuden con construcciones más complejas.

También confirmé que la estructura más compleja para la función styles funciona (inyecta un className generado en form ):

import React from 'react'

import { withStyles, createStyles, Theme } from '@material-ui/core/styles'

/*
    patterned after first demo https://material-ui.com/demos/selects/ for 3.03
    use Typsecript fixes from here: https://material-ui.com/guides/typescript/
*/

const styles = (theme:Theme) => createStyles({
  root: {
    display: 'flex',
    flexWrap: 'wrap',
  },
})

class BaseForm extends React.Component<any,any> {

    render() {
        const { classes } = this.props

        return (
            <form className = {classes.root} autoComplete="off">
                {this.props.children}
            </form>
        )
    }
}

export default withStyles(styles)(BaseForm)

Editar: @ eps1lon ha señalado que esto es innecesario con el uso de WithStyles !

Tuve cierto éxito usando ReturnType<T> para generar el ClassKey para mí:

import * as React from 'react';

import withStyles, {
  StyledComponentProps, 
  StyleRulesCallback,
} from '@material-ui/core/styles/withStyles';

import { Theme } from '@material-ui/core/styles/createMuiTheme';

const overrideStyles = (theme: Theme) => ({
  root: {
    display: 'flex',
    flexDirection: 'column',
  },
});

type Props = StyledComponentProps<keyof ReturnType<typeof overrideStyles>>;

class MyComponent extends React.PureComponent<Props> {
  render() {
    return <div className={this.props.classes.root}></div>;
  }
}

export default withStyles(overrideStyles as StyleRulesCallback, {withTheme: true})(MyComponent);

El uso de keyof ReturnType<typeof styleOverrides> como StyledComponentProps ClassKey obtendrá las claves del objeto devuelto por overrideStyles y le evitará tener que mantener una lista de esas claves manualmente . La única falla que he notado es un error de tipo si no lanzo overrideStyles as StyleRulesCallback en la llamada withStyles . No estoy 100% seguro de por qué. Creo que es withStyles no entender qué es overrideStyles por alguna razón.

Para aclarar ese tipo bastante elaborado, typeof styleOverrides resuelve la función que devuelve el objeto de estilo. ReturnType<T> le dará el objeto de estilo en sí. keyof obtendrá las claves del objeto de estilo.

@chrislambe Debería consultar la guía mecanografiada . No debería necesitar usar ReturnType etc. createStyles y WithStyles deberían ser suficientes como ayudantes.

@ eps1lon ¡Oye, muy bien! ¡Gracias!

fwiw Me gusta el par createStyles / withStyles cada vez más a medida que los uso. Promueve un código ordenado, se ocupa de todos mis problemas de estilo / mecanografía y, si quiero CSS condicional local, simplemente creo atributos de estilo local, que por supuesto tienen prioridad sobre las clases.

¡¡Bonito!!

Siguiendo la guía de mecanografiado con @ material-ui / [email protected] obtengo Test does not have required attribute classes en:

import React from 'react'
import { Theme, WithStyles, withStyles, createStyles } from '@material-ui/core/styles'

const styles = (theme: Theme) => createStyles({
  root: {
    color: theme.palette.action.active
  },
})

interface Props extends WithStyles<typeof styles> {
  asd: boolean
}

class TestComponent extends React.Component<Props> {

  render() {
    const { classes } = this.props

    return (
      <div className={classes.root}>
      </div>
    )
  }
}

const Test = withStyles(styles)(TestComponent)

const a = () => <Test asd={true}/>

@valoricDe ¿Has resuelto tu problema?

@TrejGun Acabo de comprobarlo. Con un componente funcional y @ material-ui / [email protected] no tengo este problema

Realmente no lo entiendo.
Siguiendo los documentos aquí: https://material-ui.com/guides/typescript/#augmenting -your-props-using-withstyles
parece dar como resultado el problema de este problema original. Donde cuando usa el componente en algún lugar, el mecanografiado le exige que _pasar_ la propiedad de las clases como un accesorio en lugar de darse cuenta de que va a ser _inyectado_ por withStyles.

He estado leyendo estos números durante algunas horas y realmente no lo entiendo. Cualquier ayuda sería muy buena en este momento.

Con respecto a esta sugerencia anterior

@cfilipov al decorar una clase de componente, debe usar StyledComponentProps lugar de WithStyles , y si está en modo de verificación de tipo estricto, debe usar el operador de aserción no nula ! para extraer los campos de classes . Este es un compromiso para permitir el uso de withStyles como decorador de clases. Los decoradores de clases en TypeScript sufren la limitación de que su tipo de retorno debe coincidir con el tipo de argumento. Dadas estas limitaciones, solo es posible una opción:

  1. no admite el uso de withStyles como decorador de clases
  2. requiere que pase un classes prop ficticio al construir un elemento del tipo de componente decorado (el compromiso anterior, que podría decirse que era más molesto )
  3. requerir que classes se considere anulable dentro del componente, lo que le obliga a usar el operador ! al acceder a sus campos ( el compromiso actual )

Recomiendo encarecidamente que si su componente no tiene estado, utilice un componente funcional sin estado, que requerirá menos anotaciones de tipo y será más seguro para los tipos:

export default withStyles(styles)<Props>(({ classes, message }) => (
  <div className={classes.main}>
    <div className={classes.foo}>
      Hello World! {message}
    </div>
  </div>
));

¿Cómo averiguo cómo usar StyledComponentProps ? Parece que solo paso cadenas de las claves definidas en el objeto de estilos.

¿Pero los médicos nos dicen que hagamos algo que simplemente no funcionará? ¿Qué me estoy perdiendo? Me gustaría usar https://material-ui.com/guides/typescript/#augmenting -your-props-using-withstyles ...

es posible?

@valoricDe , ¿Cómo hiciste el componente funcional que no tenía ese problema?

@TrejGun Acabo de comprobarlo. Con un componente funcional y @ material-ui / [email protected] no tengo este problema

Estoy intentando algo como esto:

`` `importar React, {ChangeEvent, Component, Dispatch} desde" react ";
importar PropTypes de "prop-types";
importar {conectar} de "react-redux";
importar {Grid, FormControlLabel, Theme, createStyles, withStyles, Radio, WithStyles} desde "@ material-ui / core";
import Importe de "./Amount";
importar {onPastDueFormFieldChange} de "../../store/actions/selectPaymentAmountActions";

estilos const = (tema: Tema) =>
createStyles ({
Monto: {
alignSelf: "centro",
},
});

interfaz OwnProps se extiende WithStyles{}

interfaz StateProps {
pastDue ?: número;
pastDueOrTotalOrOther: cadena;
}

interfaz DispatchProps {
onPastDueFormFieldChange: OnPastDueFormFieldChange;
}

type Props = StateProps & DispatchProps & OwnProps;

const PastDueFormField = withStyles (estilos) (
({clases, pastDue, pastDueOrTotalOrOther, onPastDueFormFieldChange}: Props) => (
value = "pastDue"
comprobado = {pastDueOrTotalOrOther === "pastDue"}
onChange = {onPastDueFormFieldChange}
label = "Vencido:"
control = { }
/>




),
);

const mapStateToProps = (estado: RootState): StateProps => ({
pastDue: state.customerData.balanceDue.pastDue,
pastDueOrTotalOrOther: state.customerPaymentsForm.pastDueTotalOrOther,
});

exportar conexión predeterminada(
mapStateToProps,
{onPastDueFormFieldChange},
) (PastDueFormField);

When I use this component I have this error:
```import PastDueFormField
Property 'classes' is missing in type '{}' but required in type 'Readonly<PropsWithChildren<Pick<Pick<Props, "pastDue" | "pastDueOrTotalOrOther" | "onPastDueFormFieldChange"> & StyledComponentProps<"amount">, "classes" | "innerRef"> & OwnProps>>'

@yehudamakarov Pruebe su código sin react-redux primero y agregue connect cuando todo funcione como se esperaba. Es increíblemente difícil obtener una buena visión general cuando se inyecta qué accesorio.

Cuando me encuentre con estos problemas,

  1. escriba mis accesorios de componente primero
  2. compruebe si todo se requiere como se esperaba, es decir, obtenga missing props
  3. aplicar hoc
  4. ver si mecanografiado reconoció todos los accesorios inyectados
  5. repita 3 hasta que se apliquen todos los hocs.

Promueve un código más limpio. Especialmente en lo que respecta al orden de las operaciones. Actualmente, mezcla withStyles y connect sin una separación de preocupaciones.

Muchas gracias Sebastian.
Resolví mi problema simplemente no pasando argumentos genéricos para conectar. Saqué el trozo <StateProps ...> .

Creo que esos argumentos genéricos estaban jugando con mi interfaz OwnProps que extiende WithStyles <>.

Como de todos modos paso todo al componente, la verificación de tipo que obtengo de mis Props es suficiente. No estoy seguro de por qué id necesita los genéricos connect <>.

¡Gracias!

Así es como se vería "decorar" una clase si hiciéramos la escritura correcta:

type NonStyleProps = {
  text: string
};

const styles = {
  root: {
    backgroundColor: 'red'
  }
};

const DecoratedComponent = withStyles(styles)(
  class extends React.Component<NonStyleProps & WithStyles<'root'>> {
    render() {
      return (
        <div className={this.props.classes.root}>
          {this.props.text}
        </div>
      );
    }
  }
);

Y cómo agregar temas (useTheme ()) dentro como:

const decorate = withStyles((theme: Theme) => ({
  root: {
    display: "flex"
  },
  appBar: {
    zIndex: theme.zIndex.drawer + 1,
    transition: theme.transitions.create(["width", "margin"], {
      easing: theme.transitions.easing.sharp,
      duration: theme.transitions.duration.leavingScreen
    })
  }
})
¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

mattmiddlesworth picture mattmiddlesworth  ·  3Comentarios

revskill10 picture revskill10  ·  3Comentarios

ryanflorence picture ryanflorence  ·  3Comentarios

chris-hinds picture chris-hinds  ·  3Comentarios

ericraffin picture ericraffin  ·  3Comentarios