
Strong vs Weak Typing: Why JS Gets So Much Hate
Apple + 3 = ? Error means Strong. 'Apple3' means Weak. The magic of [] + [] = 0 and why TypeScript is our savior.

Apple + 3 = ? Error means Strong. 'Apple3' means Weak. The magic of [] + [] = 0 and why TypeScript is our savior.
Why does my server crash? OS's desperate struggle to manage limited memory. War against Fragmentation.

Two ways to escape a maze. Spread out wide (BFS) or dig deep (DFS)? Who finds the shortest path?

Fast by name. Partitioning around a Pivot. Why is it the standard library choice despite O(N²) worst case?

Establishing TCP connection is expensive. Reuse it for multiple requests.

[] + []When learning programming languages, I always wondered: why does the same code throw an error in one language but "magically works" in another? In Python, "3" + 3 raises a TypeError. In JavaScript, it becomes "33". At first, I thought "Maybe JavaScript is smarter?" But then I saw [] + [] become an empty string "", and [] + {} become "[object Object]". That's when I realized: this isn't smart. The type system just gave up.
People often confuse Static/Dynamic Typing with Strong/Weak Typing. Static/Dynamic is about when types are checked (compile-time vs runtime). Strong/Weak is about how strictly types are enforced. More precisely, it's about how much implicit type coercion is allowed.
Today I'm documenting my journey: the frustration of JavaScript's infamous coercion rules, the clarity I found in Python, and how to tame this chaos with TypeScript and linters.
I was building a simple calculator feature at work. User input from an HTML input is always a string, obviously.
const userInput = document.getElementById('number').value; // "5"
const result = userInput + 3;
console.log(result); // I expected: 8, Reality: "53"
First shock. The + operator did string concatenation. "Okay, so - won't work either, right?" I tested it.
const userInput = "5";
console.log(userInput - 3); // 2 (???)
Second shock. The - operator converts to numbers and does math. Same input, but + and - behave differently. This is the essence of Weak Typing. Each operator has its own arbitrary conversion rules.
I ran examples from Gary Bernhardt's famous "Wat" talk myself.
// JavaScript's implicit conversion showcase
console.log([] + []); // "" (empty string)
console.log([] + {}); // "[object Object]"
console.log({} + []); // 0 (might differ in browser console)
console.log(true + true); // 2
console.log("5" + null); // "5null"
console.log("5" - null); // 5
console.log(null == 0); // false
console.log(null >= 0); // true (????)
The last two lines blew my mind. null == 0 is false but null >= 0 is true. Because == does type conversion but treats null/undefined specially, while >= forces numeric conversion. Who can memorize all this and actually code?
Another classic example of Weak Typing biting you in the back is Array.sort().
const numbers = [10, 2, 5, 1];
numbers.sort();
console.log(numbers);
// Expected: [1, 2, 5, 10]
// Reality: [1, 10, 2, 5]
Why? Because JavaScript's default sort implementation converts everything to Strings before sorting. "10" comes before "2" in dictionary order. If this isn't Weak Typing trolling you, I don't know what is.
To fix it, you have to provide a comparator:
numbers.sort((a, b) => a - b);
Around the same time, I started using Python for data analysis. Coming from JavaScript, I naturally wrote this:
user_input = "5"
result = user_input + 3
TypeError: can only concatenate str (not "int") to str
"It errored out. But I feel good." Sounds weird, but I meant it. Python doesn't lie to me. It clearly says: you can't mix strings and integers. It doesn't "help" by creating the nonsensical result "53" like JavaScript.
In Python, if you want to mix types, you explicitly convert them.
user_input = "5"
result = int(user_input) + 3 # Explicit conversion
print(result) # 8
# Or
result = user_input + str(3) # "53"
This is Strong Typing. If types don't match, you get an error. The language demands you state your intention clearly. Seems tedious at first, but it catches bugs early. Much safer.
What's interesting: Python is a Dynamic Typing language. You don't declare types when creating variables (x = 5). Types are checked at runtime. Yet it's strongly typed. So when you check and how strictly you enforce are separate concepts.
If JavaScript produces funny results, C produces dangerous ones. C is also weakly typed, and pointer casting completely destroys type safety.
int main() {
int num = 1025;
int *ptr = #
char *char_ptr = (char *)ptr; // Force cast
printf("%d\n", *char_ptr); // 1 (reads only lower byte)
// More dangerous: buffer overflow
char buffer[8];
int *evil_ptr = (int *)buffer; // Treat char array as int
evil_ptr[5] = 42; // Memory manipulation beyond buffer bounds
}
Pointer casting lets you "reinterpret" memory as a different type. The compiler either warns or just allows it. This causes security vulnerabilities like buffer overflows. Reading and writing memory with wrong types crashes programs, or worse, creates exploitable holes for hackers.
JavaScript tutorials often include tables of "type conversion rules": truthy/falsy values, + operator precedence, difference between == and ===, etc. But in my experience, you shouldn't try to memorize this. Instead, establish "avoidance rules".
===== attempts type conversion which is unpredictable. ESLint's eqeqeq rule warns you every time you use ==.
+ Only for Numeric AdditionThe + operator handles both string concatenation and numeric addition. If you're adding numbers, explicitly convert with Number() or parseInt() first.
if (value) is convenient but can't distinguish between value being 0 vs null. Explicit conditions (val !== 0) make intent clear.
no-implicit-coercion RuleTricks like +str, !!val look concise but make readers pause. Using explicit functions like Number(), Boolean() clarifies intent.
The fundamental solution to JavaScript's Weak Typing problem is adding a static type system. That's TypeScript.
// TypeScript catches this at compile time
const userInput: string = "5";
const result = userInput + 3; // Error: string + number
// Forces explicit conversion
const result = Number(userInput) + 3; // OK
TypeScript's strict mode makes it even more powerful.
strictNullChecks forces explicit handling of null and undefined. noImplicitAny errors when types can't be inferred. This blocks most of JavaScript's "I'll figure it out" magic.
Of course, TypeScript compiles to JavaScript at runtime, so external inputs (API responses, user input) still need validation (using libraries like Zod). But internal code type safety is definitely guaranteed.
To reduce type-related bugs in company projects, I proposed these rules to the team:
eqeqeq, no-implicit-coercion, @typescript-eslint/strict-boolean-expressions.strict: true.value is Type to narrow types safely.as User.The difference between Strong and Weak Typing isn't just "degree of strictness". It's about who takes responsibility for type safety. Weak Typing has the compiler say "I'll handle it" and dodge responsibility. The result is chaos like [] + [] becoming "". Strong Typing demands developers to "state it clearly". Seems tedious, but prevents bugs early.
JavaScript is inherently weakly typed, but armed with TypeScript and linters, you can get most benefits of Strong Typing. The key is abandoning the expectation that "the language will figure it out". Use explicit tools like Number(), String(), === to clearly communicate intent to code readers (including future you). That's where type safety begins.