The way I used to write the constructor above would be much simpler:
this.info = {
keyIndexes: {key: "", info: 0},
keyIsSet: {key: "", info: true}
};
this.keys = [""];
This is nice and terse. There's no messy type information mucking up your code. you can get right to using your object,
and typescript totally sorts this all out when you finally get to the place where you assign this initial value to a place
where the type is declared. This is really great and most of my typescript code looks like this. I would even write
this particular code like this even today, it's just not complex enough to warrant the actual situation where this
becomes useful, but for an example, I've tried to reduce the example down to something indicative but no so complex
as to obfuscate the main point.
But what I find is that there are complex interfaces that do not warrant becoming a class and the overhead it would entail.
In these sitatuations there are usually some module-scoped functions that act as if they were class-methods but are unbound
in the module scope. Since they are natually linked to the interface, I want to tightly bind local variables to the
interface itself -- not unlike what a class gives me with types for class members, in this way there are fewer changes
to make to the code as it evolves over time.
The other reason this is beneficial is that typescript obviously doesn't do any type checking until you get to the place
in code where types are declared. When that place is a complex interface the error messages get really unweidly and
decoupled from where the type error was introduced so I find myself declaring intermediate types along the way so that
TypeScript can give me errors much earlier and closer to the code that's actually broken.
At the intersection of these two situations, I find it useful to declare a local variable with a type of an array instead of
declaring it explicitly -- and doubly so when the type of the array is itself a generic -- because in that sitation, if the
generic changes, we're back to not detecting the conflict until we get to the complex type assignment.
In the case of a generic array type, we can work around this by declaring a type alias to the generic. E.g.
type StringToNumberAssociation = Association<string, number>;
. And, yes, that's true. Do that where it makes sense.
On a case-by-case basis, though, sometimes I think it's cleaner to work backwards from an interface's member's
type declaration.