Reputation: 66
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
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
(exceptnever
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!
Upvotes: 2