My view is that if you feel the need to define a type up front, it may be because you're overusing it; it's being referenced by too many different functions. This often indicates poor separation of concerns between components (low cohesion) and tight coupling.
Good abstraction would imply that you're dealing with a different, more abstract type as you move up the component/dependency hierarchy. If the same type (especially a complex type) is present at many layers in your code hierarchy, it often means that the abstraction is leaky.
A common issue I often see is when devs try to pass a Logger instance to all components and sub-components in their app... Instead, why not just make all the components emit events (or invoke some kind of listeners/callbacks), then handle and log all the events at the root of the code in one place? This is a lot more flexible because then you can use any off-the-shelf generic component and it doesn't need to know about the existence of your Logger.
Also, it's a lot easier to read and maintain code if all the logging is invoked in one place. Logging is a single concern, so ideally, it should only affect a single component/source file.
It sure helps to know that something is an object or an array and thus intellisense can actually do something useful for you before you run in the bowser, or what an API returns, or a parameter `users` to a function is an array of users vs a Set of users, or that the the field on a `User` type went from companyName to organizationName.
Typescript is going to help you out greatly there. Nothing about any of that will slow you down, in fact it'll do the opposite. If you're leaving those all as `any` until the last minute I think you're really leaving a lot of productivity on the table.
Good abstraction would imply that you're dealing with a different, more abstract type as you move up the component/dependency hierarchy. If the same type (especially a complex type) is present at many layers in your code hierarchy, it often means that the abstraction is leaky.
A common issue I often see is when devs try to pass a Logger instance to all components and sub-components in their app... Instead, why not just make all the components emit events (or invoke some kind of listeners/callbacks), then handle and log all the events at the root of the code in one place? This is a lot more flexible because then you can use any off-the-shelf generic component and it doesn't need to know about the existence of your Logger.
Also, it's a lot easier to read and maintain code if all the logging is invoked in one place. Logging is a single concern, so ideally, it should only affect a single component/source file.