Contents
Before I became a developer, I worked in tech recruitment, occasionally hiring for roles that required TypeScript experience.
At the time, I knew TypeScript as “kind of like JavaScript but newer and fancier.” This intrigued me, but without a solid grasp of JavaScript, TypeScript felt difficult to unpack—what did it offer that JavaScript didn’t?
With some Javascript projects under my belt, I wanted to explore Typescript. Why has its popularity surged (with 37% increase in global usage in 2023), making it the third most popular language on GitHub?
This beginner's guide will help you understand, install, and start using TypeScript to power both the frontend and backend of your next project. Having benefited from the wealth of online resources on TypeScript, I hope to pay it forward by offering my own perspective and guidance.
JavaScript is a powerful language, but it has its limitations, especially when building large, complex applications. JavaScript wasn’t originally designed for building systems at scale.
In the early 2000s, developer tools for JavaScript lacked key features like autocomplete, inline error detection, and easy refactoring. This made managing large projects harder – you'd have to manually track every function and ensure it was used correctly.
That’s where TypeScript comes in. Created by Anders Hejlsberg, the same person who developed C#, TypeScript was built to solve these problems and give developers a better experience with faster feedback on errors. It adds a type system on top of JavaScript, which helps prevent bugs before they happen.
Early error detection: TypeScript catches errors in your code as you write it, before you even run it in the browser. This saves time by reducing the number of bugs.
Better readability: Type annotations make your code clearer to other developers, making it easier for them to work with.
Stronger tooling: TypeScript enables features like autocomplete and inline errors, making the development experience smoother.
TypeScript files use the .ts extension, whereas JavaScript files use .js. These
.ts files are watched by something called the TypeScript Language Server, which powers the
helpful features in your code editor, like autocomplete and error checking as you type.
Unlike .js files, .ts files can’t be run directly in a browser or a runtime
like Node.js.
You first need to convert your TypeScript code into JavaScript using the TypeScript Compiler
(tsc). This process is called transpiling – turning your TypeScript into
regular JavaScript that browsers and Node can understand.
To get started, you'll need the following tools:
  1: An IDE (like VS Code): A code editor that supports TypeScript.
  2: A runtime (like Node.js or a web browser): To run the JavaScript that TypeScript compiles into.
  3: The TypeScript CLI: The command-line tool that transpiles TypeScript into JavaScript. You can install it globally by running:
npm install --global typescript
Type annotations allow you to specify the type of data a variable or function should hold.
This helps catch mistakes early. In JavaScript, you can assign any value to a variable, but in TypeScript, you can lock in a type:
let greeting: string = 'hello';
greeting = 42; // Error: Type 'number' is not assignable to type 'string'.
By setting types, TypeScript prevents issues where the wrong data type is passed into a function or assigned to a variable.
TypeScript checks something called assignability. This means it looks at whether a value of one type can be assigned to a variable expecting another type. For example, if a function expects a string and you give it a number, TypeScript will flag this as an error.
TypeScript has several basic, or "primitive," data types:
string: for text.
number: for numbers, integers and decimals.
boolean: for true or false values.
bigint: for very large numbers that go beyond the limits of the number type.
symbol: a unique, immutable identifier for object properties.
null and undefined: represent the absence of a value. Though
they're often treated as special types, they’re still important in type-checking.
When defining a new variable, you specify the type like so:
let age: number = 30;For functions, you can specify both inputs and output types:
function add(a: number, b: number): number {
return a + b;
}In addition to basic data types, TypeScript also supports more complex data structures like arrays and enums, which allow for greater flexibility in your code.
TypeScript also supports more complex types, like arrays and enums:
You can declare arrays in two ways:
let names: string[] = ['Oliver', 'Henry'];
let numbers: Array = [1, 2, 3]; These are useful when a variable can only take one of a few specific values.. An example of For example:
enum Direction {
North,
South,
East,
West
}
let heading: Direction = Direction.North;
Enums can be made up of string values or numbers values. An example of a string enum would be:
enum Direction {
North = "North",
South = "South",
East = "East",
West = "West" }
In TypeScript, functions that don’t return a value should have a void return type for
clarity:
function logMessage(message: string): void {
console.log(message);
}any,
and Pitfalls
In TypeScript, flexibility comes with responsibility. When you define your own types or work with tools to make your types more flexible, it's important to know when and why to customise types, and how to do it in a way that keeps your code safe and maintainable.
You can create custom types using type aliases to represent objects and other structures in your application. Custom types make your code easier to read and use consistently:
type User = {
name: string;
userId: number;
email: string;
}; Interfaces allow you to define the shape of objects and can be extended to build on existing structures. This reduces redundancy and makes your code more modular:
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
const mySquare: Square = { color: 'blue', sideLength: 10 };
In this example, the Square interface inherits the color property from the
Shape interface.
Omit allows you to exclude certain fields:
type UserWithoutEmail = Omit<User, 'email'>;This is useful when you want to reuse a type but don’t need every field.
Pick is the opposite of Omit. Instead of excluding properties,
Pick allows you to create a new type by selecting only the properties you want from an
existing type. This is particularly useful when you only need a subset of a larger type. For example:
type UserNameAndID = Pick<User, 'name' | 'userId'>;
Here, UserNameAndID will only have the name and userId properties
from the User type. It’s a great way to narrow down types when you only need certain pieces of data.
any Types: Use with Caution
You can make fields optional using a ? or allow any data type with any. But be
careful – using any too much defeats the purpose of TypeScript. It’s better to be specific
with your types, so your code is robust and easy to debug:
let username: any = "Oliver"; // Allows any type
let age?: number; // Optional field>;Too much flexibility can lead to unexpected bugs. If your types are too loose, TypeScript can’t catch potential issues early.
Be mindful of when and why you’re using tools like any, optional types, and flexible utility
types – they can solve problems quickly, but you might end up introducing more complexity in the long
run.
You can use TypeScript with React by writing your components in .tsx files. This helps when
defining the types for your component’s props and state. Here’s a simple example:
type GreetingProps = {
name: string;
};
const Greeting: React.FC = ({ name }) => {
return Hello, {name}!
;
}; In React, when handling events (like button clicks), you need to specify the event type:
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
console.log('Button clicked!', event);
};
For more information on React’s built-in types, check out this folder of the DefinitelyTyped documentation, and React's own Typescript documentation by following these links.