Error de Typescript: el tipo 'string' no se puede usar para indexar el tipo X
Tengo un código sencillo:
const allTypes = { jpg: true, gif: true, png: true, mp4: true };
const mediaType = url.substring(url.lastIndexOf('.') + 1).toLowerCase();
return Boolean(allTypes[mediaType]);
TypeScript se está quejando:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ jpg: boolean; gif: boolean; png: boolean; mp4: boolean; }'.
No index signature with a parameter of type 'string' was found on type '{ jpg: boolean; gif: boolean; png: boolean; mp4: boolean; }'. TS7
Creo que necesito tratar mediaType como keyof typeof allTypes, pero no sé cómo.
Por el bien de la completitud, el código completo es:
// these are all the types of media we support
const allTypes = { jpg: true, gif: true, png: true, mp4: true };
const MediaGallery = () => {
const classes = useStyles();
const [ filters, setFilters ] = useState(allTypes);
return (
<div className={classes.root}>
{mediaList
.filter(({ url }) => {
const type = url.substring(url.lastIndexOf('.') + 1).toLowerCase();
return Boolean(filters[type]);
})
.map(({ url, caption, hash }) => <Media url={url} caption={caption} key={hash} />)}
<FiltersPanel onFiltersChanged={(newFilters: any) => setFilters(newFilters)} />
</div>
);
};
Respuesta #1
Todo lo que necesitas es definir la firma de índice:
const allTypes: {[key: string]: boolean} = { jpg: true, gif: true, png: true, mp4: true };
Tipos indexables
De manera similar a cómo podemos usar interfaces para describir tipos de funciones, también podemos describir tipos en los que podemos “indexar”, como
a[10], oageMap["daniel"]. Los tipos indexables tienen una firma de índice que describe los tipos que podemos usar para indexar en el objeto, junto con los tipos de retorno correspondientes al indexar. Tomemos un ejemplo:interface StringArray { [index: number]: string; } let myArray: StringArray; myArray = ["Bob", "Fred"]; let myStr: string = myArray[0];Arriba, tenemos una interfaz
StringArrayque tiene una firma de índice. Esta firma de índice establece que cuando se indexa unStringArraycon unnumber, devolverá unstring.
Tipo de utilidad: Record<Keys, Type>
Otra solución es usar el tipo de utilidad de TypeScript Record<Keys, Type>:
Construye un tipo de objeto cuyos claves de propiedad son
Keysy cuyos valores de propiedad sonType. Esta utilidad se puede usar para asignar las propiedades de un tipo a otro tipo.
const allTypes: Record<string, boolean> = { jpg: true, gif: true, png: true, mp4: true };
for (const key of Object.keys(allTypes)) {
console.log(`${key}: ${allTypes[key]}`);
}
Respuesta #2
Puedes usar tipos indexables, pero esto amplía el tipo de allTypes para contener cualquier clave (string), cuando parece que tienes una lista limitada de claves que deseas soportar.
Una mejor solución, que te permite usar el tipo adecuado de allTypes, es (como ya indicaste en tu pregunta) decirle al compilador tu suposición de que mediaType es una de las claves de el tipo de allTypes mediante una afirmación de tipo:
return Boolean(allTypes[mediaType as keyof typeof allTypes]);
Este tipo en este caso es equivalente al tipo unión "jpg" | "gif" | "png" | "mp4", pero se calcula automáticamente.
(Cómo te aseguras de que tu suposición sea correcta en tiempo de ejecución es una preocupación aparte, por supuesto.)
