Codectionary / Developer documentation / TypeScript

Narrowing with the in Operator

The in operator checks whether a property exists on an object or its prototype chain.

What it is and why to use it

The in operator checks whether a property exists on an object or its prototype chain. TypeScript can use that check to narrow a union of object types. This is useful when different alternatives have distinguishing fields and the application needs to access the matching field safely.

Common mistakes and limitations

Optional properties can appear on both sides of an in check, so presence does not always identify a single alternative. The operator requires an object and also sees inherited properties. Check null and primitive inputs separately at untrusted boundaries.

Run this example

Use TypeScript 5.8 or later with strict checking and an ES2022 target. Save each complete example independently as example.ts, compile it, then run the generated JavaScript with a compatible Node.js installation. Compiler-only options below affect checking; they do not add runtime validation.

Terminal — compile and run

npx tsc example.ts --strict --target ES2022 --module commonjs
node example.js

Syntax

if ("message" in value) { /* narrowed value */ }

Examples

Narrowing with the in Operator

Standalone TypeScript 5.8+ example; expected output is explained below.

type Reply = { message: string } | { retryAfter: number };
function describe(reply: Reply): string {
  if ("message" in reply) return reply.message;
  return "Retry in " + reply.retryAfter;
}
console.log(describe({ message: "Saved" }));
console.log(describe({ retryAfter: 5 }));
  1. The presence of message selects the first object alternative.
  2. The other branch can read retryAfter from the remaining alternative.
  3. Both branches return strings, giving a caller one predictable result type. A tagged discriminant is often clearer when designing a larger protocol.
  4. Expected output: Saved Retry in 5

Best practices

  • Optional properties can appear on both sides of an in check, so presence does not always identify a single alternative. The operator requires an object and also sees inherited properties. Check null and primitive inputs separately at untrusted boundaries.
  • Keep external-input validation separate from compile-time type descriptions.

Runtime & versions

Compiled to JavaScript. Feature availability depends on the language and runtime version used by your project; consult the official reference below.

TypeScript Handbook

Specifications & further reading