Idea: 'rest' index signatures and the 'error' type
See original GitHub issue[This idea is still in a relatively early stage of development, but I thought it may be of worth to someone or even for the TS team itself. Feel free to share your thoughts]
Having literal types, or unions of them, in index signatures is an idea that was brought to discussion lately (see #5683, #7656 and more general discussion in #7660):
interface Example {
[letter: "a" | "b" | "c"]: number;
}
However the conventional semantics of index signatures would imply that the type checking here would be very weak, unless noImplicitAny
is enabled:
let x: Example;
x["a"] = 123; // OK
x["a"] = "ABCD"; // Error: type 'string' cannot be assigned to 'number'
x["d"] = 123; // No error with noImplicitAny disabled
x["d"] = "ABCD"; // No error with noImplicitAny disabled
x[123] = "ABCD"; // No error with noImplicitAny disabled
x[Symbol("ABCD")] = true; // No error with noImplicitAny disabled
let y = x["a"] // OK, 'y' gets type 'number'
let y = x["d"] // No error with noImplicitAny disabled, 'y' gets type 'any'
let y = x[123] // No error with noImplicitAny disabled, 'y' gets type 'any'
let y = x[Symbol("ABCD")] // No error with noImplicitAny disabled, 'y' gets type 'any'
What if it there was a way to specify the type of all the ‘remaining’ access keys to the interface with a special “rest” index signature (notated as [key: ...any]: T
) that would set a particular type for everything other than that was specified in the interface?
interface Example {
[letter: "a" | "b" | "c"]: number;
[otherKeys: ...any]: string;
}
This may also be useful to avoid unwanted type errors when noImplicitAny
is enabled:
interface Example {
[letter: "a" | "b" | "c"]: number;
[otherKeys: ...any]: any;
}
And what if it would be set to some sort of an ‘error’ type? I.e. a type that would not be assignable to or from anything? (perhaps except itself, still thinking about it…).
interface Example {
[letter: "a" | "b" | "c"]: number;
[otherKeys: ...any]: <Error>;
}
With the <Error>
type, any assignment to or from a key that is not "a"
, "b"
or "c"
would yield an error, as it cannot be assigned to or from anything:
let x: Example;
x["a"] = 123; // OK
x["a"] = "ABCD"; // Error: type 'string' is not assignable to 'number'
x["d"] = 123; // Error: type 'number' is not assignable to '<Error>'
x["d"] = "ABCD"; // Error: type 'string' is not assignable to '<Error>'
x[123] = "ABCD"; // Error: type 'string' is not assignable to '<Error>'
x[Symbol("ABCD")] = true; // Error: type 'boolean' is not assignable to '<Error>'
let y = x["a"] // OK, 'y' gets type 'number'
let y = x["d"] // Error: type '<Error>' cannot be assigned to anything
let y = x[123] // Error: type '<Error>' cannot be assigned to anything
let y = x[Symbol("ABCD")] // Error: type '<Error>' cannot be assigned to anything
Or even:
x["d"] = <null> {}; // Error: type 'null' is not assignable to '<Error>'
x["d"] = <undefined> {}; // Error: type 'undefined' is not assignable to '<Error>'
x["d"] = <void> {}; // Error: type 'void' is not assignable to '<Error>'
x["d"] = <any> {}; // Error: type 'any' is not assignable to '<Error>'
Open questions:
- What would be the implications in terms of indexing into an entity having this signature in its type?
- What would be the implications in terms of assigning to an entity having this signature in its type?
- What would be the implications in terms of assigning from an entity having this signature in its type?
- Would adding
[key: any...]: <Error>
convert any interface to a “strict” interface? (i.e. one that cannot be assigned from a ‘wider’ type containing more properties). And if it would, would that be seen as desirable or useful?
Edits: expanded and corrected examples to the actual behavior with noImplicitAny
enabled.
Edits: converted from ‘bottom’ to <Error>
as I seemed to have used a less common interpretation of the ‘bottom’ type
Edits (13 May 2016): changed [...]
to [key: ...any]
for better consistency with the current syntax.
Issue Analytics
- State:
- Created 7 years ago
- Reactions:9
- Comments:6
Top GitHub Comments
Wait, what? I was utterly surprised when I found out that I can’t do this:
because I would get
Another problem with string literals in index signature keys (and “normal” properties as well), is that is not possible to define:
That wouldn’t work, as the second index signature conflicts with the first one. As a possible solution, what if there were specialized ‘rest’ signatures that only included the ‘rest’ of a particular key type, like:
This can be useful with regular properties as well since even today this produces an error:
(The same would happen if all properties were optional)
So a typed ‘rest’ index signature would allow specifying all other string keys: