Why should you care?
The developers who write dynamic languages always argue how easy it is to get started. However, once they start using statically typed languages, it's actually hard for them get back into dynamic languages. Languages like JavaScript, Python (Python has Typehints for typesafety), and many others don't care about type safety and thus can throw really bad runtime errors if not handled correctly.
While TypeScript is indeed a superset of JavaScript, it does type checking during compile time, which in fact reduces the chances of a runtime error. Modern languages like Rust and Go focus strictly on type safety (PS. Go isn't as strict as Rust).
If you want to build solid software, static languages should be considered. While many may argue that a lot of companies are using dynamic languages, and I agree with them, they in fact do, but why would you want to waste time debugging instead of just catching it during development? Doesn't it save more time than having to debug in production?
Preventing errors
One of the benefits of static typing is its ability to catch errors during the development time, rather than at runtime. In dynamically typed languages, it's possible to write code that compiles and runs without any issues, only to encounter unexpected errors at runtime due to type mismatches or other issues. This can lead to frustrating debugging sessions and costly production issues. With static typing, the compiler can perform type checking during the compilation, identifying and reporting these types of errors before the code is even executed. This allows to address problems early in the development process, saving time and reducing the risk of bugs making it to production.
The problem:
const user = await getUser(id)
console.log(user)
Everything looks fine, right? So, where's the issue? The issue is that you don't know what the function returns. This may not be an issue in small projects, but once you dive deeper into larger projects, this will become a headache.
The solution:
type UserType = 'user' | 'admin'
interface User {
name: string
email: string
type: UserType
isActive?: boolean
}
interface Error {
message: string
code: number
}
async function getUser(id: string): Promise<User | Error> {
try {
const { error, userData } = await fetchUser(id)
if (!userData) {
return error
}
return userData as User
} catch (error) {
return {
message: 'internal_server_error',
code: 500,
}
}
}
Perhaps that looks like a lot but if you trust me it will save you a lot of time and headache in the future. Plus, you'll enjoy free autocompletion provided by the language server.
Improved Code Quality and Maintainability
Static typing contributes to better code quality and maintainability. By enforcing a clear and consistent type system, static typing helps us write more robust and reliable code. The type annotations provide valuable documentation, making it easier for other team members to understand and work with the codebase.
Conclusion
Initially, the setup may look like a lot of work, but once you get used to it, it's an investment that will definitely pay off in the long run. You will thank yourself in the future for taking the time to get this right. (Not joking - I'm a victim of this myself.)