Tests save you a lot of pain and make even a complicated refactor seem easy.
Wait… isn’t this about typing?
Typing is also testing!
There’s a reason why types are considered static testing
.
Recently I had to both refactor and add more things in one function.
Before it would get data from the ORM, which would give a complex object that it would further be transformed, until returning data in one format.
After the changes, it would get data from more than one place, not to mention a few other steps it didn’t have before to further manipulate the data, and finally the catch: it should still return the data in the same format.
The example
I’m not good with examples, but here’s a try.
It’s still a lot simpler than what I had to do but should work as an example.
The untyped problem
function func(args) {
return {
a: args.a + (args.c.d
? args.c.f.g.reduce((a, b) => a + b)
: 0
),
b: args.c.d ? args.c.e : args.b,
c: args.h.i
? args.h.j.reduce((a, b) => a + b)
: args.h.j.reduce((a, b) => a * b)
};
}
Without types, there's just a bunch of random things.
And now I’m saying you need a couple of sources while maintaining the same output.
After crying for a while, you open the debugger and console.log
every single line like there’s no tomorrow. Run, step debugger, check the log… again and again, and hope you manage to still have the same output.
With types…
I’m using Typescript here, but I actually used JSDoc on the project (you can probably find other times I’ve talked about it).
type TypeIn1 = {
a: number,
b: string,
c: {
d: boolean,
e: string,
f: {
g: number[],
},
},
h: {
i: boolean,
j: number[],
},
};
type TypeOut = {
a: number;
b: string;
c: number;
};
function func(args: TypeIn1): TypeOut {
return {
a: args.a + (args.c.d
? args.c.f.g.reduce((a, b) => a + b)
: 0
),
b: args.c.d ? args.c.e : args.b,
c: args.h.i
? args.h.j.reduce((a, b) => a + b)
: args.h.j.reduce((a, b) => a * b)
};
}
Let’s say this is what it was.
As an aside, of course, good naming will help a lot. But even then it could give you problems: is the date a string or Date? Is the id a string or number? Am I spelling it right? Does this exist in this object or the other?
All of that is solved with types. And then I want to add new data.
type TypeIn2 = {
r: number,
s: string,
t: {
u: boolean,
v: number[],
},
w: boolean,
};
type TypeIn3 = {
x: boolean,
y: string,
z: number[],
};
Again, this is a really simple and naive example, but how many times I had to debug or run the code and/or log it until I got the change I wanted, let’s say to this?
function func2(
args: TypeIn2,
other: TypeIn3,
): TypeOut {
return {
a: args.r + (args.t.u
? args.t.v.reduce((a, b) => a + b)
: 0
),
b: args.w ? args.s : other.y,
c: other.x
? other.z.reduce((a, b) => a + b)
: other.z.reduce((a, b) => a * b),
};
}
Zero time debugging, Zero console.log
If you know the exact type of what's going in and out. The rest is just pure coding.
No debugging was needed.
No console.log
scattered through all the code.
Times running the code? Zero.
I just changed the input types and… coded!
I then saved it, ran it only once and it just worked. Just like that.
Sometimes things aren’t that complicated
We are the ones that end up complicating things.
It’s really amazing that people still avoid typing when it does so much.
Some claim it’s hard…
Do you know what's hard?
Finding a misspelled object, sometimes even typing can be hard and you end up copying paste everywhere and then, of course, paste in the wrong places and then have to find where.
Some claim it’s verbose…
Do you know what’s verbose?
Errors and stack traces.
But I just want to code.
Me too… that’s why I type stuff and stop wasting coding time with debugging for obvious, with types, errors, or logs to find out what’s going in and out of functions I’m writing.
Outro
It, of course, doesn’t solve all the problems.
But it brings much more than any other problem you might have with it while subtracting a lot more problems that you would have otherwise.
And “type gymnastics” can happen, but unless you’re doing something like a library, that multiple people will use over and over… yes, while some complicated types can be more helpful than generic ones, anything is better than nothing.