Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript Error: type 'string' can't be used to index type X

Tags:

typescript

I have a simple code:

const allTypes = { jpg: true, gif: true, png: true, mp4: true };
const mediaType = url.substring(url.lastIndexOf('.') + 1).toLowerCase();
return Boolean(allTypes[mediaType]);

TypeScript is complaining:

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

I think I need to treat mediaType as keyof typeof allTypes, but don know how. Please help

For sake of completion, the complete code is:

// 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>
    );
};
like image 623
Amarsh Avatar asked Aug 27 '19 03:08

Amarsh


People also ask

How can you use string as a index type in TypeScript?

TypeScript - String indexOf() This method returns the index within the calling String object of the first occurrence of the specified value, starting the search at fromIndex or -1 if the value is not found.

Has an any type because expression of type string can't be used to index type '{?

The error "Element implicitly has an 'any' type because expression of type 'string' can't be used to index type" occurs when we use a string to index an object with specific keys. To solve the error, type the string as one of the object's keys.

Can not be used as an index type?

The error "Type cannot be used as an index type" occurs when we try to use a type that cannot be used to index an array or object, e.g. one of the non-primitive types like String . To solve the error, use primitive (lowercase) types, e.g. number or string when typing values.

Is not assignable to type string?

The "Type 'string' is not assignable to type" TypeScript error occurs when we try to assign a value of type string to something that expects a different type, e.g. a more specific string literal type or an enum. To solve the error use a const or a type assertion.


2 Answers

All you need is to define the index signature:

const allTypes: {[key: string]: boolean} = { jpg: true, gif: true, png: true, mp4: true };

Indexable Types

Similarly to how we can use interfaces to describe function types, we can also describe types that we can “index into” like a[10], or ageMap["daniel"]. Indexable types have an index signature that describes the types we can use to index into the object, along with the corresponding return types when indexing. Let’s take an example:

interface StringArray {
  [index: number]: string;
}

let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0];

Above, we have a StringArray interface that has an index signature. This index signature states that when a StringArray is indexed with a number, it will return a string.

Utility type: Record<Keys, Type>

Another solution is to use the TypeScript utility type Record<Keys, Type>:

Constructs an object type whose property keys are Keys and whose property values are Type. This utility can be used to map the properties of a type to another type.

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]}`);
}
like image 123
Christopher Peisert Avatar answered Oct 04 '22 04:10

Christopher Peisert


You can use indexable types, but this widens the type of allTypes to contain any (string) key, when it looks like you have a limited list of keys that you want to support.

A better solution - allowing you to use the proper type of allTypes - is (as you already indicated in your question) to tell the compiler your assumption that mediaType is one of the keys of the type of allTypes with a type assertion:

return Boolean(allTypes[mediaType as keyof typeof allTypes]);

This type is in this case equivalent to the union type "jpg" | "gif" | "png" | "mp4", but it is computed automatically.

(How you ensure that your assumption is correct at runtime is a separate concern, of course.)

like image 25
LCC Avatar answered Oct 04 '22 04:10

LCC