TypeScript vs Go: Choosing Your Backend Language
Marcus Kohlberg
Posted on November 8, 2024
🤔 Picking between Go and TypeScript for your backend? Let's take a look at each language to see which suits your project best.
Quick Comparison
Feature | Go | TypeScript |
---|---|---|
Performance | Fast | Depends on the framework/runtime |
Learning Curve | Simple to learn, can be difficult to master | Easy for JS devs, some nuanced complexities |
Concurrency | Built-in (goroutines) | Depends on the runtime |
Type System | Static, simpler | Static, more dynamic |
Ecosystem | Growing | Vast (npm) |
Use Cases | Microservices, systems programming | Large web apps, full-stack JS |
Core Language Features
Let's dive into what makes Go and TypeScript tick when it comes to backend development.
Type Systems: Go vs TypeScript
Generally speaking, Go is focused on simplicity and speed, while TypeScript's built for versatility and adding type-safety in the JavaScript world. Let’s look at how these two type systems stack up against each other, with a few code examples along the way to keep things clear.
1. Type Safety: Play It Safe, or go with the flow?
Both Go and TypeScript are statically typed languages. But they take different approaches to keep your code in check.
- Go: Go likes to play it safe. Every variable has to have a clear, unambiguous type. This means that if you try to pass the wrong type, Go stops you cold at compile time. It can save you a lot of headaches later, especially in production.
// Go example
func greet(name string) string {
return "Hello, " + name
}
func main() {
greet(123) // Boom! Compilation error: can't use an int as a string
}
-
TypeScript: TypeScript is also strict, but it’s got some flexibility built in. TypeScript will infer types based on how you use a variable, and you can even use
any
as a quick workaround to skip type checks (if you dare!).
// TypeScript example
function greet(name: string): string {
return "Hello, " + name;
}
greet(123); // Nope! Error: number is not assignable to type string
2. Type Inference: How much can the compiler guess?
How much do you really have to spell out for the compiler? Let’s see how these languages handle inference.
-
Go: Go is somewhat basic with type inference. Within functions, you can use the handy
:=
to let Go infer the type, but it doesn’t go too far with guessing types in functions or structs.
// Go inference example
func main() {
age := 30 // inferred as int
var name = "Alice" // inferred as string
}
- TypeScript: TypeScript takes inference a few steps further. It can infer types not just in functions, but in lots of contexts. This can speed up development by reducing the need for type annotations, but if overdone it may slow you down in the long run with nuanced bugs.
// TypeScript inference example
const age = 30; // inferred as number
const name = "Alice"; // inferred as string
3. Generics: Code reusability and flexibility
Generics are all about creating code that’s flexible enough to work with any type. Here’s how each language handles them.
- Go: Go finally got generics in version 1.18. While its generics are straightforward, they’re less flexible than TypeScript’s. But if you like simple and effective, Go’s got you covered.
// Go generics example
func Print[T any](value T) {
fmt.Println(value)
}
func main() {
Print(123)
Print("Hello")
}
- TypeScript: TypeScript has had generics for a while, and they’re impressively flexible. You can add constraints, make types conditional, and do all sorts of cool things to ensure your code adapts.
// TypeScript generics example
function print<T>(value: T): void {
console.log(value);
}
print(123);
print("Hello");
Takeaway: TypeScript’s generics are more advanced, letting you customize and control types. Go’s approach is simpler and gets the job done without the frills.
4. Structs vs. Interfaces: Different takes on type organization
Let’s talk about data structures and how these languages let you organize types.
- Go: Go has structs to define custom types with fields. Need a few methods? Add them to the struct. Then, Go’s interfaces let you specify required methods, and any struct with those methods automatically satisfies the interface.
// Go example with structs and interfaces
type Animal interface {
Speak() string
}
type Dog struct{}
func (d Dog) Speak() string {
return "Woof"
}
- TypeScript: TypeScript uses interfaces for a lot more. You can define shapes for objects, methods, or even complex data structures. It’s structural typing at its best—if an object fits the shape, TypeScript accepts it.
// TypeScript example with interfaces
interface Animal {
speak(): string;
}
class Dog implements Animal {
speak() {
return "Woof";
}
}
Takeaway: Both languages use structural typing, but TypeScript’s interfaces are more versatile, covering both data structures and behaviors.
5. Union and Intersection Types: TypeScript’s secret weapons
TypeScript has some unique features—union and intersection types—that let you mix and match types in creative ways.
- TypeScript: Union types let a variable be one of multiple types, while intersection types combine types. They’re incredibly useful in JavaScript’s dynamic environment.
// TypeScript example with union types
function processId(id: string | number): void {
if (typeof id === "string") {
console.log("ID is a string: " + id);
} else {
console.log("ID is a number: " + id);
}
}
- Go: Go doesn’t support union or intersection types. If you need similar functionality, you might turn to interfaces, but it’s not quite the same.
Takeaway: TypeScript’s union and intersection types give you flexibility that Go doesn’t have, making TypeScript feel a bit more adaptable in mixed-type scenarios.
Handling Errors in Each Language
Here's where Go and TypeScript really part ways.
Go makes you deal with errors head-on:
file, err := os.Open("file.txt")
if err != nil {
// Deal with it
return
}
// Use the file
TypeScript follows JavaScript's lead with exceptions:
try {
const data = fs.readFileSync('file.txt', 'utf8');
// Use the data
} catch (err) {
// Handle the error
}
Go's way might seem wordy, but it forces you to think about what could go wrong. TypeScript's approach looks cleaner but might let you overlook some error cases.
Javier Perez from Stackademic puts it well:
"Go's error-handling might seem verbose, but it's got hidden benefits."
Speed and Scale
Go and TypeScript each have their strengths when it comes to backend development speed and scalability. Let's break it down.
Speed and Memory Use
Go is often considered the speed demon of the two. It's a compiled language, which gives it a big advantage over TypeScript in how fast it runs.
In this benchmark by WWT you can see Go beating TypeScript (Node.js) hands down:
But hold up, there's more to the story these days. There are now many ways of speeding up TypeScript applications by using a different runtime or by enhancing the Node.js runtime.
For instance, in this benchmark we've shown that a TypeScript application using the Open Source Encore.ts framework can outperform a standard Node.js application (using Express.js) by 9x(!) measured in requests/second:
And as you can see, other frameworks like Elysia using the Bun runtime are also highly performant vs. standard Node.
So these days, it's fair to say for many web applications you will probably get sufficient performance from a TypeScript application.
Handling Multiple Tasks
Go's got a cool trick up its sleeve: goroutines. These are like lightweight threads that make it easy to build systems that can do many things at once.
Here's a quick example:
func main() {
go func() {
fmt.Println("Hello from a goroutine!")
}()
// Main code keeps going
}
TypeScript (on Node.js) does things differently. It's event-driven and non-blocking, which works well for many cases. But it can struggle with tasks that need a lot of processing power because it's normally single-threaded.
Here's how you might do something similar in TypeScript:
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
}
TypeScript can be used to create multi-threaded applications, depending on the framework and runtime used.
For instance, when using Encore.ts you get multi-threading thanks to a Rust-based runtime that handles requests/validation/IO, integrated with Node.js using napi.
You can also replace Node.js with other runtimes to unlock multi-threading, for instance Deno and Bun[https://bun.sh/].
A look at available tools
Go and TypeScript both pack a punch when it comes to backend development tools. Let's break down how they compare in terms of development environments and support.
Code Editors
Both languages have solid IDE support, but with a few key differences:
Go's top picks:
- Visual Studio Code: Microsoft's free editor with a Go extension for debugging, syntax highlighting, and code completion.
- GoLand: JetBrains' Go-specific IDE with smart features and refactoring tools. It'll set you back $199 per user per year.
TypeScript's favorites:
- Visual Studio Code: Also shines for TypeScript, with built-in support and powerful extensions.
- WebStorm: Another JetBrains creation, offering advanced TypeScript support and smart coding features.
AI-enhanced Editors:
- Cursor: A Visual Studio Code fork that comes with first-class AI features built-in to enable AI-enhanced programming. Works very well for TypeScript and also has solid Go support.
Packages / Libraries
Go keeps it simple. Its standard library is big, so you often don't need many external packages. But finding the best packages can be tricky without organized repositories.
TypeScript taps into npm's huge ecosystem. This means lots of options, but you need to choose wisely as managing dependencies can quickly become as time-sink and security risk as your application grows.
Managing Libraries
Go and TypeScript take different approaches to package management:
Go uses a built-in module system:
- Go Modules: Simplifies dependency management since Go 1.11.
- GoVendor: Helps wrangle dependency trees for simple and complex packages.
TypeScript leans on npm (Node Package Manager):
- npm / pnpm: Status quo, the biggest software registry out there, with tons of JavaScript packages.
- yarn: A faster, more secure alternative to npm.
- Bun: Bun is a JavaScript runtime that comes with and a highly performant package manager.
Testing Tools
Both languages come with solid testing options:
Go's testing toolkit:
- Built-in testing package: Provides support for automated testing of Go packages, the integrated command
go test
is simple to use. Covers all the basics. - Testify: Popular framework with assertion functions and mock objects.
- Delve: Popular debugger for Go.
TypeScript's testing arsenal:
- Jest: A fan favorite, known for being user-friendly and feature-rich.
- Vitest: Released in 2022, seen as an upgraded Jest with built-in TypeScript support.
Popular Frameworks
Go and TypeScript both have solid frameworks for different needs. Although Go is historically used without a framework, relying on the standard library.
Go's top frameworks:
- Encore.go: A fully-featured framework for building type-safe distributed systems. Comes with built-in support for local infrastructure and cloud infra automation.
- Gin: A favorite for microservices and REST APIs. It's simple and fast.
- Echo: Known for great docs and HTTP/2 support.
- Beego: An MVC framework for quick enterprise web app development.
TypeScript's main players:
- Nest.js: A comprehensive framework for building large-scale applications. Its core follows the MVC pattern and makes heavy use of decorators.
- Fastify: Not TypeScript native and no longer the most performant framework. Still widely used.
- Encore.ts: The TypeScript native version of Encore, introduced in early 2024, is a fully-featured framework for building type-safe microservices application. Comes with built-in automation for running local infrastructure and integration for easy deployment to cloud infrastructure.
- Elysia: TypeScript native framework made to use with Bun. Provides a lot of features and focuses on developer experience. Lacks infrastructure automations.
Getting help from the community
Go's community is growing fast. It's now the 8th most used language according to TIOBE's 2024 survey (up from 11th in 2023).
TypeScript already has one of the biggest communities out there. Need help? You'll likely find it fast on places like Stack Overflow, various Discord communities, or from your favorite AI chatbot.
Large Scale Use
Both languages are battle-tested in big companies.
Go powers:
- Uber's microservices and high-speed backend systems
- Google's infrastructure
- For many services in Amazon
TypeScript runs in:
- Microsoft's Azure cloud services
- Most of AirBnB's web application
- Netflix's user session management and content streaming
Your choice? It depends on your team's skills, project needs, and current tech stack.
Picking Your Language
Go or TypeScript for your backend? It's not a simple choice. Let's break it down.
Best Uses for Each
Go is great when you need speed and simplicity:
- It's perfect for microservices. Why? Fast compilation and tiny binaries.
- Got a system that needs to juggle lots of tasks? Go's goroutines have you covered.
- Building for the cloud? Companies like Uber pick Go for its efficiency.
TypeScript shines in these areas:
- Full-stack JavaScript projects. If your frontend is JavaScript, TypeScript makes sense.
- Big, complex applications. Static typing helps keep things under control.
- Need real-time updates? Netflix uses TypeScript for streaming and managing user sessions.
Team Skills
Your team's know-how matters:
If they know JavaScript, TypeScript is an easy step. Airbnb did this for their big codebase.
Got a mix of coders? Go's simple syntax could work well for everyone.
Learning curve? One way of looking at it is this: Go has 25 keywords, while TypeScript has over 60. However, TypeScript has a more mature ecosystem of libraries and frameworks, meaning there's likely less knowledge required to solve for common startup use cases.
Conclusion
Go or TypeScript for your backend? It's not a one-size-fits-all choice, it depends on your situation. For example:
Go is your go-to when speed and simplicity matter most. It's great for building systems that need to handle a lot and do it fast.
Here's why Go stands out:
- It's simple. Only 25 keywords. Easy to learn, easy to read.
- It's fast. Beats Java, Python, and JavaScript in many speed tests.
- It handles multiple tasks and is great for microservices.
- It's strict about types and errors. Makes your code very stable.
TypeScript shines when you need strong typing in a JavaScript world or for big web apps. Its perks:
- If you know JavaScript, you basically know TypeScript.
- You get access to tons of npm modules.
- With the right tooling, you can get full type-safety (even at runtime e.g. using Encore).
So, how do you choose?
Think about:
What does your project need? High performance? Go might be your answer. Web-focused? Maybe TypeScript.
What does your team know? JavaScript pros might prefer TypeScript.
Speed of development or speed of running? Go runs faster, but TypeScript might let you build quicker.
What tools and support can you tap into? TypeScript has the JavaScript world behind it. Go's community is growing fast.
How easy will it be to keep your code clean long-term?
Bottom line: Neither is "better" overall. They're good at different things. Pick based on what you need and what your team knows.
Both languages have their strong points. Think about what your project needs, what your team knows, and how easy it'll be to maintain in the long run when you're deciding.
Wrapping up
That’s it! Hopefully you know what language to reach for in your next project.
Posted on November 8, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.