Declared vs Inferred Types, a Debate of the Century

Diving into the battleground and unravelling the complexities that define this ongoing clash, reveals insights that will reshape how you approach programming.

What on earth is all of this about?

Well, I might be exaggerating on the title. But, I think this little journal will get a little exciting (and a little bit spicy of course). If you’ve ever written Typescript before, you’ll notice that the Typescript compiler is smart enought to infer types of the code you’ve been writing. It’s surely really helpful, right?

A double-edged sword Let’s create a simple function for the sake of demonstration:

function convertString(str: string) {
  return parseInt(str);
}

const result: number = convertString("1");

Well, everything looks fine, right? Is there a problem here? Well, yes. but there’s a catch.

Developers are human (at least for now)

Developers can make mistakes. They can make a lot of mistakes. My personal preference is that a piece of code should (as best as possible) explain themselves. This often being supported by comments left by the developer itself, name, and the types.

Let’s set an imaginary case. If your teammates (or even you) try to change the return of a function without checking all of the references of the function, this can lead to messy problems that might waste a lot of your time. If we take the above piece of code and directly mess around with the return type:

function convertString(str: string) {
  return parseInt(str).toString();
}

const result: number = convertString("1");
// ^?: Type 'string' is not assignable to type 'number'.

If the implementation, the function (producer) and the usage (consumer) itself are simple, this problem might be easy to fix. But, often time in real-world implementations, a real-world application, the implementation is astronomically more difficult than in the example above.

In the code above, you as a developer might think that your consumer is at fault, and spend a lot of time trying to have a lot of weird working around just to “make it work”.

const result: number = parseint(convertString("1"));

Just “say” it out loud

Some peoples might find this as a hot takes, but declaring your types (for input, and expected output) might be a tedious work at start, but it is surely worth it. Why? Let’s take a look at the code again:

function convertString(str: string): number {
  return parseInt(str).toString();
  // ^?: Type 'string' is not assignable to type 'number'.
}

const result: number = convertString("1");

As you can see, the error moves from the consumer to the producer itself. Because it is the producer’s fault itself at faults here. The example above is surely an oversimplification of what actually could happen in a real-world application.

Read it easily

Not only does our function become more “secure”, it also makes our function itself more readable. And, as I mentioned earlier, the code speaks for itself. Even though we name our function convertString(), since we explicitly declare refer function as a number, it’ll intuitively explain that our function is converting a string to a number.

Should I declare everything then?

Well, finally it comes back to your use cases and preferences. Have been working with a lot of low-level languages made me comfortable with declaring types explicitly rather than relying on the compiler to do that for me.

It surely is an extra piece of code, but I felt amazing using it. In the end, it comes down to you to decide.

Published on: