Types and inference

TypeScript: any vs unknown vs never

In TypeScript, any disables checking, unknown makes you verify before use, and never marks impossible states. Learn the differences with examples.

TypeScript gives you three special types that sound similar but behave very differently: any, unknown, and never. Picking the wrong one either kills type safety or blocks valid code.

any turns off checking

any is a catch-all. Any value fits, and TypeScript stops checking what you do with it.

We touched on this in the TypeScript introduction. My advice is to avoid any when you can. It removes many benefits of type checking.

Here is the danger:

function parseConfig(raw: any) {
  return raw.host.toUpperCase()
}

parseConfig({ host: 3000 })

No compile error. At runtime you get a crash because 3000 has no toUpperCase method.

any is sometimes an easy way out. But you pay for it later.

unknown is the safe alternative

unknown also accepts any value. The difference is you must narrow it before you use it.

This is what you want for data you do not trust yet, like JSON from an API:

function parseJson(text: string): unknown {
  return JSON.parse(text)
}

const data = parseJson('{"name":"Flavio"}')

// data.name // error: Object is of type 'unknown'

if (typeof data === 'object' && data !== null && 'name' in data) {
  console.log((data as { name: string }).name)
}

unknown forces you to check first. You get safety without giving up flexibility.

If you come from JavaScript, how to check types covers the runtime checks that pair well with unknown.

never for impossible states

never means a value that should not exist.

Use it when a function should not return, like one that always throws:

function fail(message: string): never {
  throw new Error(message)
}

The bigger win is exhaustive checks in a switch:

type Shape =
  | { kind: 'circle', radius: number }
  | { kind: 'square', size: number }

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2
    case 'square':
      return shape.size ** 2
    default:
      const _exhaustive: never = shape
      return _exhaustive
  }
}

If you add a new shape to the union and forget a case, TypeScript errors on the never assignment. The compiler tells you that you missed a branch.

Quick comparison

  • any: no checking. Use rarely.
  • unknown: any value, but you must narrow before use. Default for untrusted data.
  • never: no value can exist here. Use for impossible states and exhaustive switches.

When you need reusable logic without any, generics are the better tool.

Lesson completed

Take this course offline

Get every free book, course edition, and software download.

Get the download library →