Programming and digital skills TypeScript JavaScript static typing tsconfig narrowing programming

TypeScript for Beginners: Types, Errors, and Migration from JavaScript

Learn TypeScript from JavaScript: project setup, inference, type/interface, unions, unknown, strict mode, external validation, and a practical migration lab.

Two developers review code validations and errors during an introductory TypeScript practice session.
· Crezendo

TypeScript adds a static type system on top of JavaScript. It does not replace JavaScript or create a new runtime: the resulting program still runs as JavaScript, while TypeScript tries to detect incompatible assumptions before the code reaches the browser, Node.js, or another runtime.

This URL received 7 impressions during the reviewed 90-day window, including beginner TypeScript searches. Crezendo also had a separate introductory TypeScript page applied to JavaScript with almost identical content and no independent impressions. This guide therefore becomes the single introductory TypeScript pillar, and the duplicate page is consolidated here.

Learn enough JavaScript before TypeScript

The official TypeScript documentation is explicit about the relationship: TypeScript shares JavaScript syntax and runtime behavior. Learning TypeScript without understanding variables, functions, objects, arrays, modules, promises, and basic JavaScript behavior often turns compiler messages into rules you memorize without understanding the program that will actually execute.

You do not need to be an expert in JavaScript. You should be able to write and explain a small function before trying to type it.

What problem does TypeScript solve?

In JavaScript, this is valid:

function total(price, quantity) {
  return price * quantity;
}

Nothing in the function expresses which values are expected. If another part of the program calls total("10", 2), JavaScript applies its runtime coercion rules.

In TypeScript you can express the intent:

function total(price: number, quantity: number): number {
  return price * quantity;
}

The checker can now flag an incompatible call before execution.

That does not mean TypeScript proves the program is correct. It can catch many type inconsistencies, but it cannot by itself know whether a business formula is wrong, whether a server returned dishonest data, or whether credentials were handled securely.

Install TypeScript inside the project

The official setup guidance recommends project-level installation so each repository can declare the TypeScript version it uses.

With Node.js and npm:

npm install --save-dev typescript

Check the project compiler with:

npx tsc --version

Create an initial configuration with:

npx tsc --init

Avoid making a global installation the only source of truth for a shared project. A local dependency plus the package manager lockfile makes developer and CI environments more reproducible.

See TypeScript: Download for the current official setup options.

Use inference before annotating everything

TypeScript can infer many values:

const name = "Ana";
const attempts = 3;

There is usually no need to write:

const name: string = "Ana";
const attempts: number = 3;

when the annotation adds no information.

Annotations are especially useful at boundaries: function parameters, important return values, shared structures, module contracts, and values whose intended type would otherwise be ambiguous.

Objects: type and interface

You can describe the expected shape of an object:

type Product = {
  id: number;
  name: string;
  price: number;
  available: boolean;
};

function displayProduct(product: Product) {
  return `${product.name}: ${product.price}`;
}

You can also use interface:

interface User {
  id: number;
  email: string;
  active: boolean;
}

A beginner does not need to turn the difference between type and interface into a style war. First understand which structure you need to express and follow the convention of the project consistently.

Optional properties and unions

A property can be optional:

type Profile = {
  name: string;
  phone?: string;
};

And a variable can explicitly allow several states:

type Status = "pending" | "processing" | "completed" | "error";

Unions are useful when they force the code to account for real states. Do not turn them into giant unrelated lists merely to silence a compiler error.

Learn unknown before abusing any

any effectively tells the checker to stop helping in that portion of the program.

When you receive a value whose type is not yet known, unknown can be safer:

function printValue(value: unknown) {
  if (typeof value === "string") {
    console.log(value.toUpperCase());
  }
}

The typeof check narrows the value before string operations are used. This is called narrowing.

The official handbook has a dedicated section on Narrowing.

TypeScript does not automatically validate external data

This distinction is essential.

Suppose an API returns JSON. You can write:

const user = response as User;

but as User does not validate or transform the data at runtime. It only changes what TypeScript believes about the value.

At external boundaries—APIs, forms, files, environment variables, queue messages—runtime validation is still needed when the shape is not guaranteed.

Static types and data validation address related but different problems.

Enable stricter checking deliberately

For a new project, strict checking often exposes hidden assumptions before the codebase grows.

A central tsconfig.json option is:

{
  "compilerOptions": {
    "strict": true
  }
}

Turning every strict rule on in an old JavaScript codebase may expose hundreds or thousands of issues at once. An incremental migration can be more practical:

  1. establish a working baseline;
  2. add TypeScript as a project dependency;
  3. begin with modules that have clear boundaries;
  4. reduce any gradually;
  5. enable stronger checks;
  6. run tests after meaningful changes.

The official documentation also describes an incremental path for JavaScript projects using TypeScript.

Type functions before memorizing advanced utilities

A beginner gains more by understanding parameters, return values, and object shapes than by jumping immediately into complicated conditional types.

Start with:

function applyDiscount(price: number, percentage: number): number {
  return price * (1 - percentage / 100);
}

Then introduce a structure:

type Line = {
  price: number;
  quantity: number;
};

function subtotal(line: Line): number {
  return line.price * line.quantity;
}

Add arrays, optional properties, unions, and generics when the problem actually needs them.

A small migration lab

Take a simple JavaScript file that manages a product list.

Step 1: preserve a working baseline

Run the program first and save a few test cases.

Step 2: change .js to .ts

Let TypeScript infer what it can and observe the first errors.

Step 3: type the boundaries

Define Product, function parameters, and important return values.

Step 4: resolve ambiguous assumptions

If a product may not have a price, decide what that means. Do not simply add | undefined everywhere so the compiler becomes quiet.

Step 5: test incorrect data

Try a missing field, text where a number is expected, and an unknown status. Observe what TypeScript catches and what still requires runtime validation.

Step 6: document the difference

Write down which error would have reached runtime in JavaScript and which problems still need validation or tests even with TypeScript.

Common beginner mistakes

  • Using any for every error. The message disappears, but much of the type system's value disappears with it.
  • Using as Type to force the compiler. An assertion does not prove that external data has that shape.
  • Annotating every obvious variable. Use inference.
  • Learning TypeScript without JavaScript. Generated code still follows JavaScript runtime rules.
  • Confusing compilation with tests. A clean tsc run does not prove business logic.
  • Migrating a large repository at once. Keep changes small enough to verify.
  • Depending on a global compiler version. Prefer a project-local dependency for reproducible builds.

Do you need a framework to learn TypeScript?

No. You can begin with a small Node.js project or the official Playground. React, Angular, Vue, and other frameworks add their own concepts, which can make it harder to tell which behavior belongs to TypeScript and which belongs to the framework.

Learn types, functions, objects, unions, and narrowing first. Framework-specific TypeScript becomes easier afterward.

TypeScript and JavaScript belong in the same learning path

The official handbook emphasizes that TypeScript cannot be learned by ignoring JavaScript. Array methods, promises, closures, modules, and runtime behavior remain JavaScript.

Continue with the official TypeScript Handbook and TypeScript for JavaScript Programmers.

Training and Crezendo

Crezendo maintains programming and software-development training areas, but this page does not announce a permanent TypeScript course. Availability, level, duration, and delivery format must be confirmed.

Review the current workshops or ask about a specific need. If you already use JavaScript, describe the project you are working on and the kinds of errors or maintenance problems you want static typing to address.

Does your company need to solve this challenge?

Crezendo designs tailored workshops for companies, NGOs, and government bodies. Explore everything we can do for your organization or tell us what you need to receive a proposal and quote.

Request a proposal and quote View workshops for companies