Operators#

TypeScript inherits every JavaScript operator (see Operators) and adds a small set of type-level operators that work on types instead of values. Use these to narrow, to derive types from runtime objects, and to assert conformance without widening.

This page covers only the TypeScript-specific operators. For arithmetic, equality, logical, nullish, spread, and the rest of the JavaScript surface, see the JavaScript page.

typeof#

In a value position typeof x is a runtime expression returning a string. In a type position typeof x is the type of a runtime value.

const config = {host: "0.0.0.0", port: 80};
type Config = typeof config;          // {host: string; port: number}

const COLORS = ["red", "green", "blue"] as const;
type Color = (typeof COLORS)[number]; // "red" | "green" | "blue"

if (typeof x === "string") { /* x narrowed to string */ }

keyof#

keyof T is the union of T’s property keys.

type User = {id: string; name: string; email?: string};
type K = keyof User;                  // "id" | "name" | "email"

function pluck<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

For Record-shaped types (string index, number index), keyof returns string / number.

in (type position)#

In a value position, "key" in obj is the JavaScript in operator. In a type position, [K in U] is the mapped-type syntax (see Types).

In a control-flow check, in narrows a union.

type A = {kind: "a"; foo: string};
type B = {kind: "b"; bar: number};

function go(x: A | B) {
  if ("foo" in x) x.foo;          // narrowed to A
  else            x.bar;          // narrowed to B
}

instanceof#

x instanceof C narrows the type to C inside the truthy branch.

function handle(e: Error | HTTPError) {
  if (e instanceof HTTPError) return e.status;
  return 500;
}

For type-discrimination on non-class structures, prefer a discriminated union over instanceof.

as (type assertion)#

x as T tells the compiler to treat x as T. Compiles when T is a sub- or super-type of x; needs a double assertion (x as unknown as T) otherwise.

const el = document.getElementById("root") as HTMLDivElement;

const port = (process.env.PORT ?? "8080") as `${number}`;

Use sparingly. A bad cast is silent until the runtime catches it; zod or a hand-written type guard is the safer pattern.

as const#

x as const freezes x as a literal type and makes every nested member readonly.

const opts = {mode: "fast", retries: 3} as const;
//    ^? { readonly mode: "fast"; readonly retries: 3 }

const tuple = [1, 2, 3] as const;     // readonly [1, 2, 3]

satisfies#

x satisfies T checks that x conforms to T without widening x’s inferred type. Useful when you want both the conformance check and the precise literal type.

const handlers = {
  get:  (req) => req.path,
  post: (req) => req.body,
} satisfies Record<string, (req: Req) => unknown>;

handlers.get;     // (req: Req) => string  -- precise
handlers.post;    // (req: Req) => Body

A plain annotation would widen handlers.get to (req: Req) => unknown.

Non-null (!)#

The non-null assertion x! is x as NonNullable<typeof x>. Strips null and undefined from the type.

const found = items.find(x => x.id === id);
process(found!);                      // operator knows it exists

The compiler does not check the runtime claim; the operator takes responsibility.

Definite assignment (!)#

! on a class field declares it as definitely assigned later (injected by a framework, set in a separate init call).

class Service {
  private client!: HttpClient;        // initialised by setup()
  setup(c: HttpClient) { this.client = c; }
}

Optional (?)#

? marks a property or parameter as optional (its type includes undefined).

interface Config { host: string; port?: number; }
function listen(opts: {host: string; port?: number}) { /* … */ }

Optional chaining ?. is the JavaScript runtime operator; TypeScript narrows the resulting type to include undefined.

readonly#

Marks a property or array immutable at the type level.

interface Config {
  readonly host: string;              // cannot reassign config.host
}

function sum(xs: readonly number[]): number {
  return xs.reduce((a, b) => a + b, 0);
}

const cfg: Readonly<Config> = {host: "0.0.0.0"};

References#