A continuación se muestra un pequeño ejemplo, en el que me gustaría preguntar por qué el eslinter para mecanografiado se queja del objeto, que posiblemente no esté definido en ese caso, donde en realidad se agrega una verificación indefinida, solo que se extrae en un función separada, por lo que obtiene un nombre más significativo.
import { ReactElement } from "react"; import "./styles.css"; interface Item { id?: string; images?: string[]; } const itemHasImages = (item: Item): boolean => item.images !== undefined && item.images.length > 0; const renderItem = (item: Item): ReactElement => { if(itemHasImages(item)){ // <<< using this the compiler complains below in JSX that item could possibly be null // vs if(item.images !== undefined && item.images.length > 0) <<< here the compiler recognizes the check return ( <> <img src={item.images[0]} alt={""} /> </> ); } else { return <></> } }; export default function App() { const dummyItems = [ { images: ["https://picsum.photos/200/300", "https://picsum.photos/200/300"] }, { images: ["https://picsum.photos/200/300", "https://picsum.photos/200/300"] } ]; if (itemHasImages(dummyItems[0])) { console.log("hello") renderItem(dummyItems[0]); } else { return <div>Hello</div>; } return <div>Hello</div>; }Disculpe el formato débil, para una mejor interacción puede encontrar un enlace de código-sandbox aquí:
https://codesandbox.io/s/unruffled-bogdan-c74u6?file=/src/App.tsx
¿Por qué el eslinter para mecanografiado se queja del objeto, que posiblemente no esté definido?
La razón es porque agregaste un ? (opcional) a la propiedad en la declaración de la interfaz.
Una forma de solucionarlo es verificar si no está definido al renderizarlo con el ? operador.
<img src={item.images ? item.images[0] : ""} alt={""} /> Tenga en cuenta que solo está ocultando el problema, la solución real es asegurarse de no renderizar ese elemento si no hay imágenes, es decir, agregue una !!item.images?.length .
Desafortunadamente, TypeScript no entiende qué hace exactamente su función itemHasImages , incluso si tiene exactamente el mismo cuerpo que una expresión que TS puede entender cuando está en línea.
Por lo tanto, cuando factoriza su verificación en una función externa, TypeScript ya no puede usarla para realizar el estrechamiento de tipos (aquí para asegurarse de que una propiedad opcional esté realmente presente).
Este es uno de los casos en los que TypeScript puede obligarnos a no ser "demasiado inteligentes" y dejar un cheque en línea.
Otra solución aceptable es forzar / emitir el tipo después de su verificación:
if(itemHasImages(item)){ const images = item.images as unknown as string[]; // Force type since we have externally verified it return ( <> <img src={images[0]} alt={""} /> </> ); } Podría ser posible una solución más compleja al proporcionar definiciones de sobrecarga para su función itemHasImages , de modo que TS pueda usarlas para realizar el estrechamiento de tipos.
En su código publicado, Typescript no tiene forma de saber que el boolean devuelto por su función está destinado a ser un tipo de protección para el item del elemento.
Para resolver esto, puede declarar un predicado de tipo como el tipo de retorno de su función que indica que Typescript debe interpretar el booleano devuelto como una afirmación del predicado. Deberá tener un tipo/interfaz que describa un item con una propiedad de images requerida para usar en el predicado.
Puede hacer esto explícitamente extendiendo la interfaz del Item y haciendo que la propiedad de images sea requerida. (caja de arena , ts-playground )
interface Item { id?: string; images?: string[]; } interface ItemWithImages extends Item { images: string[]; } function itemHasImages(item: Item): item is ItemWithImages { return item.images !== undefined && item.images.length > 0; } const renderItem = (item: Item): ReactElement => { if (itemHasImages(item)) { // vs if(item.images !== undefined && item.images.length > 0) <<< here the compiler recognizes the check return ( <> <img src={item.images[0]} alt={""} /> </> ); } else { return <></>; } };O puede implementar el tipo de utilidad descrito en el duplicado marcado que convierte una propiedad opcional en una requerida. (caja de arena , ts-playground )
/** * @see https://stackoverflow.com/questions/52127082/ensure-existance-of-optional-property-in-typescript-interface */ type MakeRequired<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>> & { [P in K]-?: Exclude<T[P], undefined> }; interface Item { id?: string; images?: string[]; } function itemHasImages(item: Item): item is MakeRequired<Item, "images"> { return item.images !== undefined && item.images.length > 0; } const renderItem = (item: Item): ReactElement => { if (itemHasImages(item)) { // vs if(item.images !== undefined && item.images.length > 0) <<< here the compiler recognizes the check return ( <> <img src={item.images[0]} alt={""} /> </> ); } else { return <></>; } };