TypeScript Basics

Table of Contents

  1. The Problem TypeScript Solves
  2. Install and Run the Compiler
  3. Annotations and Inference
  4. Basic Types
  5. Arrays and Tuples
  6. Object Types: type and interface
  7. Unions and Literal Types
  8. unknown and any
  9. Typing Functions
  10. A Taste of Generics
  11. tsconfig.json and strict
  12. Reading Compiler Errors
  13. Try It Yourself

1. The Problem TypeScript Solves

In Errors and Debugging you met TypeError: Cannot read properties of undefined. JavaScript only finds that kind of mistake when the line runs, and maybe only on a user's phone. JavaScript lets you pass a string where a number is expected, or misspell a property name, and says nothing until it breaks.

TypeScript is JavaScript with an extra layer: you describe the types of your values, and a tool called the compiler checks your code before it runs.

function double(n: number) {
  return n * 2;
}

double("5"); // error found while you type, not when a user clicks

Three facts to remember:

  • TypeScript is a superset of JavaScript. All valid JavaScript is valid TypeScript, so you can learn it gradually.
  • Browsers and Node cannot run .ts files directly. The compiler turns them into plain .js files.
  • Types exist only while you write and compile. They disappear from the final JavaScript, so they cost nothing at runtime, and they also cannot protect you at runtime. You will see why in the next chapter.

2. Install and Run the Compiler

TypeScript is installed with npm (from Modules, Node, and npm). In Dara's project folder:

npm init -y
npm i -D typescript
npx tsc --version

-D means a dev dependency: a tool you need while building, not code the browser loads. npx runs a program installed in the project.

Create hello.ts:

const message: string = "Hello, Dara";
console.log(message);

Compile it:

npx tsc hello.ts
node hello.js

You now have a hello.js next to hello.ts, and Node runs the JavaScript one. For the rest of the chapter, you can check examples with npx tsc --noEmit --strict file.ts, which only checks the code and writes no files. Editors such as VS Code do the same check as you type and show red underlines.

3. Annotations and Inference

A type annotation is a colon and a type after a name:

let title: string = "Portfolio";

Often you do not need it. TypeScript works out the type from the value. This is called inference:

let year = 2025;    // TypeScript infers: number
year = 2026;        // fine
year = "2026";      // Error: Type 'string' is not assignable to type 'number'.

A practical rule:

WhereAnnotate?
Variables with an obvious starting valueNo, let inference work
Function parametersYes, always
Function return typesOptional, but helpful for public functions
Variables that start empty or with nullYes

Hover over any variable in VS Code to see the type TypeScript chose.

4. Basic Types

These match the values you learned in Values, Variables, and Types:

const title: string = "Portfolio";
const year: number = 2025;
const isDone: boolean = false;
const nothing: null = null;
const missing: undefined = undefined;
const big: bigint = 10n;

Notice that the type names are lowercase: string, number, boolean. The capitalized String and Number are different, older objects. Do not use them as types.

5. Arrays and Tuples

An array type is the item type followed by []:

const tags: string[] = ["html", "css"];
const scores: Array<number> = [90, 85];

tags.push(42); // Error: Argument of type 'number' is not assignable to parameter of type 'string'.

string[] and Array<string> mean the same thing; most people prefer string[].

A tuple is an array with a fixed length where each position has its own type:

const pair: [string, number] = ["Dara", 25];
const [who, age] = pair;   // who: string, age: number

Use tuples for small, fixed groups. For anything with named parts, use an object.

6. Object Types: type and interface

To describe an object, list its properties and their types. This is the shape of one of Dara's projects:

type Project = {
  title: string;
  year: number;
  url?: string;   // the ? makes it optional
};

const p: Project = { title: "Quiz App", year: 2025 };

The ? means the property may be missing. Its type becomes string | undefined, so TypeScript will make you handle the missing case before you use it:

p.url.toUpperCase();    // Error: 'p.url' is possibly 'undefined'.
p.url?.toUpperCase();   // fine: optional chaining

There is a second way to name an object shape, interface:

interface Person {
  name: string;
  age: number;
}

interface Student extends Person {
  school: string;
}
typeinterface
Object shapesYesYes
Unions, tuples, simple aliasesYesNo
Extending& (intersection)extends

For everyday work they are almost the same. A simple choice: use type everywhere, and use interface if a team or library already does. This series uses type.

7. Unions and Literal Types

A union means "one of these types," written with |:

type ID = string | number;

function format(value: ID): string {
  return `ID: ${value}`;
}

A literal type is a type that allows exactly one value, such as "css". Combine literals in a union and you get a precise list of allowed values:

type Tag = "html" | "css" | "javascript";

let tag: Tag = "css";
tag = "js";   // Error: Type '"js"' is not assignable to type 'Tag'.

This is one of the most useful features for beginners. A typo like "js" is caught immediately, and your editor suggests the three valid values as you type.

8. unknown and any

Sometimes you do not know a value's type, for example data from JSON.parse. TypeScript has two ways to say so:

let anything: any = "hi";
anything.foo.bar;           // no error from TypeScript, crashes when run

let safe: unknown = "hi";
safe.toUpperCase();         // Error: 'safe' is of type 'unknown'.
if (typeof safe === "string") {
  console.log(safe.toUpperCase());   // fine: checked first
}
anyunknown
Meaning"Turn the type checker off""I don't know yet, so check first"
Can you use it directly?Yes, with no checksNo, you must check its type
Safe?NoYes

Avoid any. It hides exactly the bugs TypeScript exists to catch. When a type is truly unknown, use unknown and narrow it (next chapter).

9. Typing Functions

Annotate parameters and, if you like, the return value:

function add(a: number, b: number): number {
  return a + b;
}

const double = (n: number): number => n * 2;

add(1);        // Error: Expected 2 arguments, but got 1.
add("1", 2);   // Error: Argument of type 'string' is not assignable to parameter of type 'number'.

Optional parameters use ? and default values work as usual:

function greet(name: string, greeting = "Hello", suffix?: string): string {
  return `${greeting}, ${name}${suffix ?? ""}`;
}

A function that returns nothing has the return type void. You can also write the type of a function value, such as a callback:

let callback: (message: string) => void;
callback = (m) => console.log(m);   // m is inferred as string

Read (message: string) => void as "a function that takes a string and returns nothing."

10. A Taste of Generics

You have already used a generic type: Array<number>. The part in angle brackets is a type parameter, a placeholder that is filled in later. Here is a function that works for any kind of array:

function first<T>(items: T[]): T | undefined {
  return items[0];
}

const a = first([1, 2, 3]);   // a: number | undefined
const b = first(["x", "y"]);  // b: string | undefined

T stands for "whatever type the array holds." TypeScript infers it from the argument, so you rarely write <number> yourself. You will mostly use generics from libraries (Promise<string>, Array<Project>) before you write them.

11. tsconfig.json and strict

Instead of command-line options every time, a tsconfig.json file in the project root holds the settings. Create one:

npx tsc --init

The generated file has many commented options. A small, clean version looks like this:

{
  "compilerOptions": {
    "target": "ES2023",
    "module": "ES2022",
    "moduleResolution": "bundler",
    "strict": true,
    "rootDir": "src",
    "outDir": "dist"
  },
  "include": ["src"]
}
OptionMeaning
targetWhich JavaScript version to produce
moduleUse import/export in the output
strictTurn on all the strictest checks
rootDir / outDirRead from src/, write to dist/
includeWhich files to compile

Always keep "strict": true. It makes TypeScript complain about implicit any and about null/undefined where they are not expected. It feels stricter at first, but those are exactly the mistakes that cause real bugs. With a tsconfig.json present, run plain npx tsc and it uses the settings.

12. Reading Compiler Errors

Compiler errors look long, but they follow a pattern. Here is a file with several mistakes:

let year = 2025;
year = "2026";

const p = { title: "x", year: 1 };
p.titel;

function f(x) { return x; }

const el = null;
el.foo;

Running npx tsc --noEmit prints:

bad.ts(2,1): error TS2322: Type 'string' is not assignable to type 'number'.
bad.ts(5,3): error TS2551: Property 'titel' does not exist on type '{ title: string; year: number; }'. Did you mean 'title'?
bad.ts(7,12): error TS7006: Parameter 'x' implicitly has an 'any' type.
bad.ts(10,1): error TS18047: 'el' is possibly 'null'.

Break one down:

bad.ts(5,3): error TS2551: Property 'titel' does not exist ...
└─┬──┘└─┬─┘        └──┬──┘ └──────────────┬─────────────────
file   line,column   code   message
  • Read the message first. It names the value and the problem.
  • Messages like "X is not assignable to Y" mean "you gave me X where Y was required."
  • Look for suggestions such as Did you mean 'title'?
  • For long errors, read the first and last lines; the middle usually repeats details.
  • The error code (like TS2322) is handy for searching the web.

Fix errors from the top; one early mistake often causes several later ones.

13. Try It Yourself

  1. Install and compile. In an empty folder run npm init -y and npm i -D typescript. Write hello.ts, compile it with npx tsc, and run the output with node.
  2. Inference or annotation? Write let count = 0; and hover over it. Then try count = "one" and read the error.
  3. Describe a project. Write a Project type with title, year, and an optional url. Create two projects, one with a URL and one without, and print url?.length for both.
  4. Restrict values. Create type Tag = "html" | "css" | "javascript" and a function describeTag(tag: Tag): string. Try calling it with "python" and read the error.
  5. A generic function. Write last<T>(items: T[]): T | undefined. Call it with numbers and with strings, and hover over each result.
  6. Turn on the checks. Create a tsconfig.json with "strict": true. Write a function with an untyped parameter and fix the error it reports.

You now know enough TypeScript to use it on a real project. In the last chapter, we convert Dara's page and see where types help and where they stop: TypeScript in Practice.

Buy Me a Coffee

Found this tutorial helpful? Consider buying me a coffee to support more free content!