En la primera parte de este tutorial de TypeScript, vimos cuáles son los tipos de sindicatos y cómo distinguirse mutuamente utilizando predicados de tipo. Si no ha leído esta publicación, le recomiendo que haga esto ahora, porque establece la base, deberá comprender lo que discutiremos hoy. En esta publicación, hablaremos sobre “hacer estados imposibles imposibles” utilizando tipos de sindicatos y cómo un mejor modelo de datos conduce a un código más robusto y confiable. Además, veremos, con respecto al código fuente de las ventanas emergentes NELI, cómo TypeScript puede ayudarnos a escribir mejores componentes.
Cómo hacer imposibles estados imposibles de comenzar con un ejemplo simple. Imagine que desea crear un componente posterior a la lista que muestre una lista de publicaciones. Cuando el componente se muestra por primera vez, hace que una solicitud al servidor cargue la lista de publicaciones (suponga que tiene un gancho de efecto llamado UsePosts para él). Mientras carga datos, muestra una animación; Cuando las publicaciones están listas, las muestran. Podríamos implementar dicho componente de la siguiente manera: Export const postist = ({postids}) => {const {isloading, publica} = usePosts (postids); if (isloading) return ; if (! post.length) {return
no publica
// end if return (
{post.map ((post) => …)} ); } Según la descripción del componente que acabo de compartir y el fragmento anterior, podemos deducir que UsePosts puede tener el siguiente tipo de retorno:
Escriba postsult = {readonly isloading: boolean; ¿Publicaciones de lectura?: ReadonlyArray ; } Es decir, un objeto con un atributo de isarización booleana y una lista (opcional) de publicaciones. Puede argumentar si las publicaciones deben ser opcionales o no; Supongo que es porque, bueno, mientras los datos se cargan, no tenemos publicación, ¿verdad? Sin embargo, utilizando la definición anterior del tipo, se pueden crear las siguientes instancias: // resultado 1 {isloading: true} // resultado 2 {isloading: true, publicaciones: []} // resultado 3 {isload: true, post : [{…}, {…},…]} // resultado 4 {isloading: false} // resultado 5 {isloading: false, publicaciones: []} // resultado 6 {isloading: false, publicaciones: [ {}, {…},…]} que, como puede ver en TypeScript Playground, son todas instancias válidas del tipo de Postultura. Sin embargo, en realidad hay algunos resultados que no tienen sentido. Específicamente, los resultados 2 y 3, por un lado, y el resultado 4, por otro lado, son extraños. Si observamos los resultados 2 y 3, vemos que las publicaciones aún se están cargando (esto indica el atributo de isarización ) y sin embargo, al mismo tiempo, tenemos algunos resultados disponibles (hay una lista de publicaciones). Por otro lado, en el resultado 4, deberíamos tener la lista de publicaciones disponibles … ¡pero el atributo ni siquiera está configurado! Está claramente mal, ¿no? Este es un claro ejemplo del modelo de datos débiles. Con postresult, podemos representar estados imposibles. Debería ser imposible “cargar resultados” y, al mismo tiempo, “tener una lista de publicaciones”. Además, debería ser imposible “saber que los resultados están cargados” y “se pierdan el atributo de publicaciones”. Y, sin embargo, ambas situaciones son posibles en nuestro modelo de datos.
Nuestro objetivo como programadores (buenos) es crear un buen software, y esto significa que tenemos que “hacer estados imposibles imposibles”. En nuestro ejemplo de la banda de rodadura, podemos lograr esto utilizando el siguiente tipo de unión (que, por cierto, es discriminado por el atributo de carga): type postresult = loadingPoStresult | Loadedpostresult; Type loadingPostresult = {readonly isloading: true; }; Type LoadedPostresult = {readonly isloading: false; Publicaciones de Readonly: ReadonlyArray ; } Si implementamos esta nueva definición postal en TypeScript Playground, veremos cómo los resultados 2, 3 y 4 son realmente inválidos. Por lo tanto, ahora tenemos una definición que captura perfectamente la realidad y, como resultado, TypeScript nos ayudará a asegurarnos de que nuestras restricciones estén adecuadamente garantizadas. Si desea obtener más información sobre el tema, recomiendo esta discusión de Richard Feldman. Él usa Elm en su discurso, pero los ejemplos y las perspectivas que comparte siguen siendo muy interesantes:
Definición (Parte A) del modelo de datos Popups Neli al comienzo de la publicación, le prometí que usaremos un ejemplo real, por lo que es hora de hacerlo. Si echa un vistazo al código fuente que no es de Popups en WordPress.org, verá que hay una carpeta en SRC/Common llamado tipos con múltiples archivos donde definimos los principales tipos de ventanas emergentes. Si tomamos uno de esos archivos aleatorios (por ejemplo, SRC/Common/Tipos/PopUps/Style.ts) y observamos su contenido, veremos que los tipos que contiene son bastante similares a los que acabamos de hablar sobre la sección anterior:
// … Exportar sobre superposición de superposiciones = | {Readonly isEnabled: false; } | {Readonly isEnabled: true; Readonly Color: Color; }; // … Exportar tipo de Bordersettings = | {Readonly isEnabled: false; } | {Readonly isEnabled: true; Readonly Radius: cssiseSizeUnit; Readonly Color: Color; Readonly Ancho: CSSisisUnit; }; Es decir, cada tipo tiene un indicador que nos dice si una determinada propiedad está activa o no y, entonces, tenemos los atributos adicionales para su configuración. Entonces, ¿cómo nos ayuda esto a escribir un mejor código? React Components Como puede ver, los tipos de ventanas emergentes de Neli son bastante simples y simplemente aplican el siguiente principio: “hacer lo imposible”. Veamos cómo tal principio puede guiar nuestro desarrollo. Uno de los ejemplos que hemos extraído de las ventanas emergentes de Neli son las superposiciones. Como sugieren sus nombres y propiedades, las ventanas emergentes de Nelio pueden incluir o no superposición y, cuando lo hacen, el usuario puede definir su color. Un componente que administra una configuración sería la siguiente:
Superponiendo la configuración de una ventana emergente.
con el siguiente código fuente: import * como react de ‘@wordpress/element’; Import {togGlecontrol} de ‘@WordPress/Components’; Import {_x} de ‘@wordpress/i18n’; Import {colorcontrol} de ‘@nelio/popups/componentes’; Import {usePopupmeta} de ‘@nelio/popups/gooks’; Exportar const overlayControl = (): jsx.ement => {const [overlay, setOverlay] = UsePopupMeta (‘Overlay’); const {isEnabled} = superpuesto; Const onchange = (isChecked: boolean) => isChecked? SetOverlay ({color: ‘#000000cc’, isEnabled: true}): setOverlay ({isEnabled: false}); Return ( {isEnabled && (colorControl Color = {Overlay .Color} onChange = {(newColor) => setOverlay ({… superpuesto, color: newColor,}) />)} ); }; Hay varias cosas en el fragmento anterior que vale la pena mencionar. En primer lugar, observe cómo, dada una cancha de superposición, podemos acceder a su soporte ISELABLed sin defensas ni cheques. Esto se debe al hecho de que, como puede ver en la definición de su tipo (superposiciones), el atributo es el discriminador del tipo de unión y, por lo tanto, se define en todos los “subtipos” del tipo de unión. Por lo tanto, podemos destruirlo de manera segura.

En segundo lugar, el JSX que regresamos a la oficina tiene dos partes.Por un lado, el componente siempre devuelve un interruptor para activar o desactivar la configuración superpuesta.Es decir, tenemos un subcomponente responsable de administrar el atributo ISInabled.Por otro lado, tenemos un segundo subcomponente para administrar la superposición del color.Sin embargo, este segundo componente es visible si y solo si la superposición está IsEnable, lo que tiene un significado perfecto, porque tenemos un soporte de color solo cuando la superposición se activa realmente.En este momento, es posible que se pregunte por qué creamos un tipo de unión si un tipo más simple como este haría el trabajo:

Type overlaysettings = {readonly isEnEnabled: true; Readonly Color: Color; }; ¡Bien pensado! Bueno, como dije, ser precisos cuando describamos nuestro modelo de datos nos ayudará a largo plazo. Entonces, echemos un vistazo más de cerca a cómo, con un breve ejemplo, el primer tipo es mejor que el último. Usando el tipo más fácil que acabamos de propuesto, podríamos escribir el método de Onchage que encontramos en nuestro componente de varias maneras: const Onchange1 = (IsChecked: Boolean): Void => isChecked? SetOverlay ({color: ‘#000c’, isEnabled: true}): setOverlay ({color: ‘#000c’, isEnabled: false}); Const onchange2 = (isChecked: boolean): void => setOverlay ({color: ‘#000c’, isEnabled: isChecked}); Conc OnCange3 = (isChecked: boolean): void => setOverlay ({… superpuesto, isEnabled: isChecked}); Pero desafortunadamente, no todos son correctos. Por ejemplo, en la tercera variante no tenemos idea de cómo se define actualmente la superposición: si activamos la superposición usando OnChange3, usaríamos los valores actuales de Onverlay y, por lo tanto, su color actual. Pero, ¿cuál es su color actual? ¿Estamos seguros de que tiene un valor correcto? ¿Quién lo inicializó? ¿Cuándo? ¿Qué valor usaron? Si tenemos lo siguiente?
Const overlay = {isEnabled: false, color: ”,};Sin embargo, el primer tipo (unión) que propusimos nos obliga a establecer explícitamente un color cuando activamos la configuración y OnChange3 no funcionaría.En cambio, necesitamos escribir una función que establezca explícitamente un valor para el color: const onchange = (isChecked: boolean): void => isChecked?SetOverlay ({color: ‘#000c’, isEnabled: true}): setOverlay ({isEnabled: false});
Cómo crear mejores componentes con tipografías y ganchos reaccionados (ii)
Tags Cómo crear mejores componentes con tipografías y ganchos reaccionados (ii)
homefinance blog