TypeScript Fundamentals: Types, Generics and Real-World Patterns · TypeScript Basics: Types, Narrowing and Functions

Hello, TypeScript: Types, Inference and Strict Mode

Write your first typed program, see how inference removes most annotations, use unknown instead of any, and learn how the compiler reports mistakes before the code runs.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

A small distance converter. Along the way you'll annotate a function, rely on type inference, handle an unknown value safely and see the compiler reject a wrong call.

The idea

TypeScript is JavaScript with a type system. You describe what values are allowed, and the compiler checks your code before it runs. The types are erased when the code is compiled, so the output is plain JavaScript that runs in browsers, Node.js, Deno and Bun.

Set up a project and compile:

npm install --save-dev typescript
npx tsc --init          # creates tsconfig.json
npx tsc                 # type-checks and compiles to JavaScript
npx tsc --noEmit        # type-check only (a common CI step)

Turn on "strict": true in tsconfig.json. It enables the checks that catch the most bugs, including null and undefined safety, and every example in this course uses it.

Three ideas to learn first:

  • Annotations state a type: const label: string = "Marathon";, function convert(distance: number): number.
  • Inference works the type out for you: const trip = 42.195; is already a number. Annotate function parameters and return values; let inference handle most variables.
  • unknown vs any. any switches the checker off for a value. unknown means "could be anything", and you must check it before use. Prefer unknown.

The code

A distance converter with annotations, inference and unknownTry it yourself
type Unit = "km" | "mi";

function convert(distance: number, from: Unit): number {
  return from === "km" ? distance * 0.621371 : distance / 0.621371;
}

const trip = 42.195;            // inferred: number
const label: string = "Marathon";
console.log(`${label}: ${trip} km = ${convert(trip, "km").toFixed(2)} mi`);

// The compiler rejects wrong calls. @ts-expect-error documents an error we expect here.
// @ts-expect-error: a string is not a number
convert("42", "km");

// unknown must be checked before use
function describe(value: unknown): string {
  if (typeof value === "number") return `a number: ${value.toFixed(1)}`;
  if (typeof value === "string") return `a string: ${value.toUpperCase()}`;
  return "something else";
}

console.log(describe(3.14159));
console.log(describe("hello"));
console.log(describe([1, 2]));

Output

Marathon: 42.195 km = 26.22 mi
a number: 3.1
a string: HELLO
something else

Remove the @ts-expect-error line in your own editor and the call convert("42", "km") gets a red underline: Argument of type 'string' is not assignable to parameter of type 'number'. That message is the whole point of TypeScript.

Common mistakes

  • Turning strict mode off to silence errors. Fix the code instead; the errors are real bugs waiting to happen.
  • Annotating everything. const count: number = 0; adds noise. Annotate function signatures and exported values; let the compiler infer locals.
  • Using any as an escape hatch. It spreads: one any makes everything derived from it unchecked. Use unknown and narrow.
  • Assuming types exist at runtime. They are erased, so you cannot test typeof x === "Unit". Runtime checks use JavaScript values (typeof, in, instanceof).
  • Forgetting to compile. Browsers and Node.js run JavaScript. Use tsc, a bundler, or a runner such as tsx; recent Node.js versions can also run .ts files that use only erasable syntax.

Key points

  • TypeScript checks types at compile time and erases them, producing plain JavaScript.
  • Enable "strict": true. Annotate function parameters and returns, and let inference do the rest.
  • Use unknown for values of uncertain type and narrow them with typeof before use; avoid any.
  • A string-literal union such as "km" | "mi" restricts a value to specific strings.

Further reading: The TypeScript Handbook: The Basics.

Practice questions

What is the difference between `any` and `unknown`?

Answer

any turns type checking off for that value, so anything is allowed and the unsafety spreads. unknown also accepts any value but forces you to narrow it (for example with typeof) before you can use it.

Why can't you write `if (x instanceof Unit)` when `Unit` is a type alias?

Answer

Types are erased at compile time, so they do not exist when the program runs. Runtime checks must use JavaScript values such as typeof, in, instanceof with a class, or comparisons.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?