Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type 'null' cannot be used as an index type

Tags:

typescript

I'm using Typescript with strict null checking enabled. When I try to compile the following code I get the error "type 'null' cannot be used as an index type."

function buildInverseMap(source: Array<string | null>) {
    var inverseMap: { [key: string]: number } = {};
    for (let i = 0; i < source.length; i++) {
        inverseMap[source[i]] = i;
    }
}

Obviously inverseMap cannot have null as a key because the type constraint disallows it. However if I change the type of inverseMap to this:

var inverseMap: { [key: string | null]: number } = {};

I get the error "Index signature parameter type must be 'string' or 'number'." This is odd because in Javascript it is legal to use null as an index. For example, if you run the following code in your browser:

var map = {};
map[null] = 3;
map[null];

The output is 3. Is there a way to make this happen in Typescript or is Typescript not smart enough to do this?

like image 257
Aaron Avatar asked Sep 04 '17 19:09

Aaron


1 Answers

Object keys in JavaScript are, believe it or not, always strings (okay, or Symbols). (See this answer also). When you pass a non-string value as a key, it gets coerced into a string first. So, in

var map = {};
map[null] = 3;
map[null];

You are actually setting map["null"]. Observe:

console.log(map["null"]===map[null]); // true

So, in TypeScript they actively decided to only allow the string or number type as index signatures. Probably because most of the time, anyone trying to index into an object using something like null is indicative of an error.

In your case, you can do something like this:

function buildInverseMap(source: Array<string | null>) : {[key: string] : number} {
    var inverseMap: { [key: string]: number } = {};
    for (let i = 0; i < source.length; i++) {
        inverseMap[String(source[i])] = i; // coerce to string yourself
    }
    return inverseMap;
}

Note how we coerce source[i] to string ourselves, and that makes TypeScript happy. If you remember to wrap the key with String() whenever you might use it with null, it should work for you:

const inverseMap = buildInverseMap(['a', 'b', null, 'c']);
const aIndex = inverseMap['a'];
const nullIndex = inverseMap[String(null)];

Hope that helps! Good luck.

like image 169
jcalz Avatar answered Sep 18 '22 13:09

jcalz