typescript

Is there a way to have a negative number as a key of an index signature?


I want an index signature of type : [key: number]: string;

For example:

type MoveNbWithLocation = {     
    [key: number]: string; 
}  

Then:

const initPieceLocation: MoveNbWithLocation = {0: ''}; //This is fine
const initPieceLocation: MoveNbWithLocation = {1.3: ''}; //This is also fine
const initPieceLocation: MoveNbWithLocation = {-1: ''}; //This is NOT fine

I prefer to have a key of type 'number'.

However if there is no way to use negative numbers, maybe I have to use type 'string' for the key.


Solution

  • TL;DR, coerce the number to a string like {"-1": "abc"} or use a computed key like {[-1]: "abc"}.


    JavaScript objects don't actually have numeric keys. Property names are only ever strings or symbols. If you use something other that that, JavaScript will coerce it to a string. When you write a numeric literal key in an object literal like {0: ""}, this is exactly the same as if you coerced it to a string first, like {"0": ""}. There is no difference between those. And both of those are the same as if you use a computed property like {[0]: ""} or {["0"]: ""}.

    That means a TypeScript numeric index signature is a bit of a convenient fiction. It actually means something like "strings of the form String(n) for some number n". A numeric index signature will accept numeric keys, but it will also accept string keys that correspond to serializing a number:

    let x: MoveNbWithLocation;
    x = { 1.3: "abc" }; // okay
    x = { "1.3": "abc" }; //okay
    x = { 1.30: "abc" }; // okay
    x = { "1.30": "abc" }; // error!
    

    The first three are all the same. The numeric literal 1.3 and the numeric literal 1.30 are the same number, and coerce to "1.3". On the other hand, the string "1.30" is not what you get for String(1.30) or String(1.3) or String(n) for any number, so it doesn't match the string index signature.

    It's not obvious, but in JavaScript, there are no negative numeric literals. If you write -1, what you are actually doing is applying the unary negation operator (-) to the numeric literal 1. You are allowed to give an object literal a numeric literal key. But you are not allowed to use a negative number as a numeric literal key because there are no negative numeric literals. If you write {-1: ""}, that's a syntax error in JavaScript.

    But since the key -1 and "-1" are the same, you can still get the exact same object, by either coercing it to the string yourself, or by using a computed property:

    // x = { -1: "abc"}; // syntax error!
    x = { "-1": "abc" }; // okay
    x = { [-1]: "abc" }; // okay
    

    And TypeScript accepts both of these as matching the numeric index signature, because they coerce to the right sort of string.

    Playground link to code