The ever-lasting strife of static vs dynamic typing – TypeScript won’t help
When my friend and I were of school age and aspiring to become software developers, we daydreamed of designing some cool stuff together – like a game or a mega-useful app.
Here was my vision of the game. I was like, ok, we have such types as game, tool, item, map, location. I have a basic understanding of how they work together, so I describe them, compile the project – and it works. After all, the compiler has verified my code, and I did it right. Next, I start writing code that uses those types. They make my life much easier. My IDE gives me tooltips and checks for any errors. If a project can be compiled, it probably works. I spent some effort on type description and it yielded results. That’s my approach in a nutshell.
My friend had the opposite idea — to jump to code-writing right away without taking any time to describe the types. He was not ready to define the problem as a family of types. He was not keen on using it as a foundation, as he did not see the problem as a set of classes, types, records of anything of the kind. I thought it was inconceivable. We both had a point, it’s just that our points were mutually exclusive.
Seriously, we were talking for hours, yet each was saying his own thing, as if we were speaking different languages. Mind you, I couldn’t blame it on us being stuck in our old ways. Just a year ago, I migrated painlessly from the world of object-oriented programming to the world of functional programming and back. Moreover, I spent quite a while on learning JS, and he — on learning a number of statically typed languages.
However, to any developer the technology they used for their first real job often defines them to the extent that two experienced adults simply lack the patience to listen to each other. Over these years of software engineering, our visions were shaped in such different ways that our approaches to problem solving just didn’t fit well together.
In the end we abandoned the idea of working as a team. Your first response might be that the problem was in our personalities. You may be right, but I also saw this happen to others in the industry.
The fundamental, irreconcilable difference between static and dynamic typing
My code provides the solution to the problem of how to work with it, whereas the code of seasoned dynamic typing advocates solves the problem of how it works. Both mindsets are legitimate and supported by existing toolsets, but only one can have the highest priority at any single moment.
Static typing is good for large-scale projects involving hundreds of developers who work on them for years, whereas dynamic typing is good for smaller teams and projects that often require write-only code. Dynamic typing allows you to save time and efforts at the beginning of the development, whereas static typing gives you a boost at the end of it.
The idea of putting types first has seriously affected my thinking as a developer. Having chosen C# at the beginning of my career, I have hard-coded static typing into my mindset, and I’m paying the price of this inflexibility now. Once I see a task, I try to imagine its solution as a set of types and rules of their relationship. When I’m developing a module, my first step is to define the types it operates and uses to interact with its environment. I just don’t remember how I used to solve problems before that.
The whole process of learning to program in Java is about learning to design and use types. .NET CLR — the C# runtime — is built on types and for types. Static typing stands at the core of the object-oriented programming paradigm (hello, JS classes, I decided to give you a break). The canonical implementations of most OOP patterns are saturated with the Interface keyword, which makes no sense whatsoever in a dynamically typed language.
The design patterns themselves are cross-language concepts, but can anyone tell me why on earth I’d need the State pattern in a dynamically typed language? What about Builder? These patterns have nothing to do with development, they are primarily about types. Types and OOP have a tight link.
You can’t build your business logic on types and yet know nothing about them as you start writing code. That’s why we have front-end developers who arm their code with an incredible number of unit tests which specifically check the code base for any type errors.
We all know that protection based on code coverage is an illusion. Tests are written manually, and they are by definition less reliable than the built-in system of type verification available in the language.
It doesn’t mean that dynamically typed languages don’t make sense (although I confess that I do think they don’t). It means that while using them, you need to step back from OOP as the dominating paradigm. All this unity of data and data-driven operations is for fellows who have static typing.
The developers I’ve come across don’t think that static typing affects the way of coding to any extent, so they just write their code as if they were using a dynamic language, only they add type checking. I believe this is inherently wrong. It is especially obvious in the case of modern front-end.
For some reason, these guys are just not ready to live in a world where their vision has tangible deficiencies. That’s why I criticize only those of them who tampers with my definitely-typed project to rework it in their relaxed, “Any“ way.
We would keep dwelling in our tiny worlds, but TypeScript brought us together
Take me for example: because of the type restrictions, my code is impossible to use improperly. When I’m working on a project, I rely on others to use types too. Then my code will work just as designed. So I deliberately don’t cover all the cases in which this code can be used incorrectly (because typing makes it impossible). But then a JS developer joins my project, takes my type, wraps it up in Any and starts using it incorrectly, which results in hard-to-replicate bugs.
Their killer argument is that TypeScript is just a superset of JS. In practice, one cannot ignore the fact that TypeScript is an independent language, even if one is a freaking king of front-end. Because it requires a different approach – the one of static, not dynamic, verification.
The major benefit of static typing is that it gives you guarantees. If you use it in one module and choose to not use it in another, then you just waste your time and energy on describing and designing those types, without getting any guarantees whatsoever.
Many think that TypeScript is a compromise of type systems between JS and Java. Well, it is not a compromise of any kind, it’s got a special type system of its own.
Worst of all, today one in two front-end positions requires proficiency in TypeScript. This spurs JS developers to cast a glance at TypeScript’s features and immediately jump to writing code in it, generating a proliferation of harmful practices. Situations occur when they don’t really need static type checking, but it was kind of imposed on them, so they messed with it. We need to finally acknowledge that approaches to programming with static vs dynamic typing are in conflict with each other and can’t be mixed up.
The pattern emerged because front-end developers heard somewhere about cool immutability, so they decided to drag this shit into their language, which needs it like a hole in the head. In TypeScript, I can design a similar factory, but with a compile-time restriction on mutations and without any exceptions.
So I don’t want to look for compromises, instead I’ll put it straight. If you’re just making your first steps in development, start with static typing!