DEV3 Technologies home

DEV3 Developer Forum

How do I narrow types in TypeScript when a value is `unknown` or a union?

JavaScript helpSolved0 views
dev3 ·

I get 'Property x does not exist on type string | number' and 'Object is of type unknown' in catch blocks. How do type guards and narrowing work?

#typescript#narrowing#types

0

1 reply

dev3 · Accepted answer

TypeScript narrows a type inside a block when it can prove something from a check.

Built-in checks

function f(v: string | number | null) {
  if (v === null) return;          // null removed
  if (typeof v === 'string') {
    v.toUpperCase();               // string here
  } else {
    v.toFixed(2);                  // number here
  }
}

if (value instanceof Date) { value.getTime(); }
if ('radius' in shape) { shape.radius; }   // `in` narrows unions of objects
if (Array.isArray(x)) { x.length; }

Discriminated unions (best pattern)

type Result =
  | { ok: true; data: string }
  | { ok: false; error: Error };

function show(r: Result) {
  if (r.ok) return r.data;   // narrowed by the literal `ok`
  return r.error.message;
}

Add an exhaustive check:

function assertNever(x: never): never { throw new Error('Unexpected: ' + x); }

Call it in the final default of a switch and you get a compile error when a new variant is added.

Custom type guards

function isUser(x: unknown): x is { id: string; name: string } {
  return typeof x === 'object' && x !== null && 'id' in x && 'name' in x;
}

unknown in catch

try { ... } catch (e) {            // e is unknown with useUnknownInCatchVariables (strict)
  const msg = e instanceof Error ? e.message : String(e);
}

Use unknown for external data (JSON, API responses) and validate with a schema library such as zod rather than casting with as.

Pitfalls

  • as assertions silence the compiler but do not check anything at runtime.
  • Narrowing is lost inside callbacks for let variables that might change; use const.
  • Object.keys(obj) returns string[], not keyof typeof obj, by design.

The JSON to TypeScript converter can generate interfaces from sample responses.

0

Your reply

Every post is reviewed by DEV3 before it appears. Usually reviewed within 24 hours.

Markdown supported. Use ``` for code blocks. Images are not allowed.