iTakeshi
iTakeshi

Reputation: 66

Forbid number property for string-typed index signature

Suppose we have a type definition like

type Foo = {
    [_: string]: number
}

But we can assign an object with a numbered property to this type:

const foo: Foo = {
    10: 42
}

Question: Is there any way (compiler options or something) to forbid this behavior? i.e., I want to forbid assigning numbered properties into a string-typed index signature.

(Note) The "indexable types" section of the official typescript handbook says "when indexing with a number, JavaScript will actually convert that to a string before indexing into an object" and I know this is why such assignments are accepted. I want to know how to get around this.

Upvotes: 0

Views: 205

Answers (1)

jcalz
jcalz

Reputation: 328097

From the documentation you cited:

It is possible to support both types of indexers, but the type returned from a numeric indexer must be a subtype of the type returned from the string indexer.

Consider the never type:

The never type is a subtype of, and assignable to, every type; however, no type is a subtype of, or assignable to, never (except never itself).

That suggests that you can redefine Foo as:

type Foo = {
    [_: string]: number
    [_: number]: never
}

This means: all string-valued keys should have a number property type, and all number-valued keys should have a never property type. Since you can't ever make a valid never type (no type is assignable to it), any attempt to use a numeric key will give you an error:

const goodFoo: Foo = {
    a: 1,
    b: 2,
    c: 3
}

const badFoo: Foo = {
    eight: 8,
    nine: 9,
    10: 42 // error!
    // number is not assignable to never
};

Looks good to me. Note that this has the side-effect of prohibiting "numeric string indices" also:

const alsoBad: Foo = { // error!
    eight: 8,
    nine: 9,
    "10": 42 
}
//   Property '"10"' is incompatible with index signature.

Hopefully that doesn't matter much, since as you noted, at runtime {10: 42} and {"10": 42} are the same thing.

Hope that helps give you some direction; good luck!

Playground link to code

Upvotes: 2

Related Questions