TypeScript Basics #1: Getting Started and Setup

8 min read

This series is for those who already know some JavaScript but are new to TypeScript. It spans 7 posts.

  • #1 Getting Started and Setup ← this post
  • #2 Basic Types
  • #3 interface and type alias
  • #4 Union / Literal / Narrowing
  • #5 Function Types
  • #6 Generics in Depth
  • #7 Utility Types and tsconfig

This post pins down “why TypeScript” and takes you from writing your first piece of code all the way to compiling and running it.

What is TypeScript? #

TypeScript is JavaScript with a type system added on top. Microsoft created it in 2012, and today it has become the de facto standard for JavaScript-based projects. Almost every large React/Vue/Node.js project is written in TypeScript.

In one line:

JavaScript + type annotations = TypeScript

Any code you can write in JavaScript also runs in TypeScript. TypeScript adds syntax on top that lets you express information like “this variable is a string” or “this function takes a number and returns a boolean”.

Why annotate types? #

JavaScript is a dynamically typed language, so you can only know a variable’s type by running the code. That’s not a big deal in small programs, but as the codebase grows, things like the following start to happen often.

The trap of dynamic typing
function getDiscount(price, percent) {
  return price * percent / 100;
}

getDiscount(1000, 10);     // 100 — fine
getDiscount('1000', 10);   // 1000... wait, a string was passed but it still works
getDiscount(1000, '10');   // 100 — works, but was that the intent?
getDiscount(1000);         // NaN — percent is undefined

JavaScript happily converts types or fills in undefined and just runs the code anyway. The bug surfaces much later down the line.

If you annotate the same function in TypeScript, it catches the bad call as you write the code.

TypeScript version
function getDiscount(price: number, percent: number): number {
  return price * percent / 100;
}

getDiscount(1000, 10);     // OK
getDiscount('1000', 10);   // Error: '1000' is not a number
getDiscount(1000, '10');   // Error
getDiscount(1000);         // Error: not enough arguments

The errors show up at compile time. A red squiggly appears in your editor before you even run anything, and the build is blocked. Bugs get caught before they reach the user.

TypeScript’s value, in one line #

  • Editor autocomplete and inference — the IDE knows exactly which properties an object has and what a function returns
  • Refactoring safety — change a function signature and every call site lights up red
  • Acts as documentation — the type is the function’s manual
  • Bugs blocked up front — compilation rejects common traps like undefined/null

It feels like overhead in small toy projects, but for code that several people work on together, or code you’ll need to revisit later, TypeScript’s value really shines.

TypeScript vs JavaScript — the execution model #

One important fact: browsers and Node.js cannot run TypeScript directly. They don’t understand type annotations.

So the .ts files we write must be converted (compiled/transpiled) to JavaScript before they run. The tools that do this conversion are tsc (the TypeScript Compiler), or build tools like esbuild, swc, and Vite.

Execution flow
.ts file (you write)
   ↓ (compile)
.js file (runnable)
browser / Node.js

One interesting point: the compiled .js file has all type information stripped away. Types only matter at development time; at runtime it’s just plain JavaScript. TypeScript is therefore a tool that gives you “development-time safety with no extra runtime cost”.

First setup — the simplest path #

There are several ways to set things up. To start, I recommend the two simplest options.

Method 1. Use tsc directly (for learning) #

This is best for learning TypeScript itself in its purest form.

Create a new folder
mkdir ts-learn
cd ts-learn
npm init -y
npm install --save-dev typescript
npx tsc --init

What each command does:

  • npm init -y — creates package.json (turns the folder into a Node.js project)
  • npm install --save-dev typescript — installs the TypeScript compiler
  • npx tsc --init — generates a default tsconfig.json

tsconfig.json is the file that holds the compiler options. We’ll cover the options in detail in #7; for now, leave it as it was generated.

Method 2. Use Vite for speed (closer to real-world usage) #

For web development contexts, Vite handles compilation and the dev server in one go, which is convenient.

Vite + TS project
npm create vite@latest ts-learn
# Framework: Vanilla
# Variant: TypeScript
cd ts-learn
npm install
npm run dev

This series uses Method 1. Compiling directly with tsc gives you the clearest picture of what TypeScript is doing. The same principles still apply once you move to Vite/Webpack later.

Your first piece of code #

In the ts-learn/ folder, create a file named index.ts.

index.ts
function greet(name: string): string {
  return `Hello, ${name}!`;
}

const message: string = greet('Alice');
console.log(message);

There are three places where TypeScript-only syntax appears.

  • name: string — annotates name as a string type
  • (): string — annotates that the function returns a string
  • const message: string = ... — annotates the variable message as a string

Compiling and running #

Compile
npx tsc

Running the command produces an index.js file in the same folder (or possibly elsewhere, depending on your tsconfig.json settings). Open that file.

Compiled index.js
function greet(name) {
    return "Hello, ".concat(name, "!");
}
var message = greet('Alice');
console.log(message);

The type annotations (: string) are all gone. You’ll also notice that ES6 syntax (const, template literals) has been converted to slightly older JavaScript (default behavior for legacy browser support — adjustable via the target option in tsconfig.json, covered in #7).

Let’s run it with Node.

Run
node index.js
Output
Hello, Alice!

Make a mistake on purpose — see a type error firsthand #

The fastest way to appreciate TypeScript’s value is to make mistakes yourself. Change index.ts like this.

index.ts (broken version)
function greet(name: string): string {
  return `Hello, ${name}!`;
}

const message: string = greet(42);  // passing a number
console.log(message);

Run npx tsc again:

Compile error
index.ts:5:31 - error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.

5 const message: string = greet(42);
                                ~~

Found 1 error in index.ts:5

TypeScript pinpoints the exact location — line 5, column 31. The .js file isn’t even produced (under default settings; adjustable via options). It stops bad code from reaching the execution stage.

In an editor like VSCode, the red squiggly appears immediately, before you even run npx tsc. You get real-time feedback on mistakes as you type.

Automatic inference — types you don’t have to write #

You don’t need to annotate every variable. TypeScript is clever enough to infer types.

Automatic inference
const age = 30;             // age: number — inferred automatically
const name = 'Alice';       // name: string
const isAdmin = true;       // isAdmin: boolean
const items = [1, 2, 3];    // items: number[]

age = 'hello';              // Error: can't assign string to number

Write const age = 30 and TypeScript automatically infers age: number. Any later attempt to assign a different type — like age = 'hello' — gets caught.

You typically use explicit type annotations in the following situations.

  • Function parameters and return types (inference is weaker here)
  • To express clear intent (something like const id: string = computeId() for readability)
  • When pre-declaring an empty variable’s type (let user: User | null = null)

You’ll develop a feel for this balance over the rest of the posts.

Watch mode — skip running compile every time #

If running npx tsc every time gets tedious, you can use watch mode.

Auto-recompile
npx tsc --watch

The compiler reruns automatically every time you save a file, giving you instant feedback. Leaving this mode running while you learn is convenient.

Common traps and myths #

“TypeScript is hard” #

The first few days feel awkward, but after just a week or two you’ll feel uneasy going back to plain JavaScript. The value of autocomplete, safe refactoring, and instant error feedback is huge.

“TypeScript is slow” #

At runtime it’s identical to JavaScript — zero overhead. It does add compile time, but with incremental builds you barely notice it.

“Can’t I just use any?” #

any is essentially an escape hatch that turns the type system off. Using it too often defeats the purpose of TypeScript. Use it only when truly necessary; usually, finding a more precise type (like the union types in #4) is better.

“JSDoc can do almost the same thing” #

True. You can express some types with JSDoc comments. But TypeScript is far more powerful in expressiveness and tooling support.

Wrap-up #

Summary of what we covered:

  • TypeScript = JavaScript + type annotations
  • Catches type errors at compile time; at runtime it’s just plain JavaScript
  • Compile with npx tsc, auto-recompile with watch mode
  • Annotate function signatures first; let inference handle most variables
  • The fastest way to learn is to write broken code on purpose and see the compile errors

In the next post, “TypeScript Basics #2 Basic Types”, we’ll work through the basic types you’ll use every day — string/number/boolean, then array, tuple, object, enum, and more.

X