If you haven't, you should explore domain-driven design (DDD). It's an explicit idea in that framework that a problem needs a "ubiquitous language" and an identified "bounded context" where the said language is valid.
Like you, when approaching a new problem I like to nail down the terms we are discussing, literally scaffolding classes and types (in C++) as the problem is being discussed. It drives the adoption of unambiguous terms. Then as solutions are discussed the "ubiquitous language" helps clearly identify the interactions between types.
One advantage of using a very open-ended type system like TypeScript's is that you don't have to know as much up-front.
I could declare some type Foo, and that type might end up being:
- A class
- A record type
- A union type
- An intersection type
- A constant
or whatever else. But I can go ahead and start referring to that type in terms of function signatures, etc. Particularly for this kind of sketch-coding, it's really nice to be able to declare a concept and start talking about it before you even know what it is.
Like you, when approaching a new problem I like to nail down the terms we are discussing, literally scaffolding classes and types (in C++) as the problem is being discussed. It drives the adoption of unambiguous terms. Then as solutions are discussed the "ubiquitous language" helps clearly identify the interactions between types.