as (type assertion)

Tells the compiler to treat an expression as a different type, with no runtime conversion at all.

Signature
expr as Type
  • Added in TypeScript 1.6

Parameters

ParameterTypeDescription
$exprany expressionThe expression whose static type you want to replace. Its runtime value is untouched.
$TypetypeThe type the checker should use from here on. Must be a more specific or less specific version of the expression's type.

Return value

TypeThe same value at runtime, re-typed as Type for the checker only.

Errors

Never at runtime — the operator is erased. tsc raises "Conversion of type 'X' to type 'Y' may be a mistake" when neither type sufficiently overlaps the other.

Changelog

  • PHP 1.6The `expr as Type` form was added alongside the `.tsx` extension; the older `<Type>expr` form became a syntax conflict with JSX and is not allowed in `.tsx` files.
  • PHP 3.0`unknown` was added, giving a safer bridge type for double assertions than `any`.
  • PHP 4.9`satisfies` arrived as the non-lying alternative for the common case of checking a value against a type without widening or replacing its inferred type.

A type assertion replaces what the checker believes about an expression. It is not a cast: nothing is converted, validated, or copied. The handbook is blunt about it — assertions are removed at compile time, so there won't be an exception or null generated if the type assertion is wrong. You are taking responsibility for a claim the compiler cannot verify.

When to reach for it

  • You know more than the checker about a value that crosses an untyped boundary — JSON.parse, a message payload, a DOM lookup.
  • You are narrowing a deliberately wide type (unknown, a union) at a point where you have already proved the shape some other way.

What trips people up

TypeScript only permits assertions between types that overlap — to a more specific or less specific version of the same type. "hello" as number is rejected. The documented escape hatch is two steps, expr as any as T or expr as unknown as T, and the moment you write it you have switched off every check on that value. Treat a double assertion as a comment saying "I have validated this elsewhere."

The angle-bracket form <Type>expr is equivalent but is a syntax error in .tsx, where <Type> parses as a JSX tag. That is why as is the form nearly every codebase standardises on.

If you only want to check a value against a type without changing its inferred type, reach for satisfies instead.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Typing the result of JSON.parse before handing it to code that expects a specific shape.
  • Narrowing a caught error from unknown so you can read err.message.
  • Re-typing a value through unknown when two types do not overlap enough for a single assertion.

Frequently asked questions

Does `as` convert the value at runtime?

No. Type assertions are erased by the compiler and emit no JavaScript, so `"42" as unknown as number` is still the string "42" when the code runs. Use `Number()`, `String()` or a parser for an actual conversion.

Why can't I write `<Type>value` in my .tsx file?

Because `<Type>` is parsed as the opening of a JSX element there. The `as` form was introduced in TypeScript 1.6 along with the `.tsx` extension precisely to remove that ambiguity, and it is the only assertion syntax available in `.tsx`.

When should I use `satisfies` instead of `as`?

Use `satisfies` when you want the value checked against a type but still want its narrower inferred type. `as` replaces the type and suppresses the check; `satisfies` verifies the value conforms and leaves the literal types intact.

More examples

Assertion versus a type guard

Both branches print, because the payload really does have the fields. The difference is what happens when it does not: the asserted path prints `undefined` and carries a broken object onward, while the guard takes the else branch. Output is `[email protected]` then `validated: 7`.

Example · typescript
type User = { id: number; email: string };

function isUser(v: unknown): v is User {
  return typeof v === "object" && v !== null && "id" in v && "email" in v;
}

const payload: unknown = JSON.parse('{"id":7,"email":"[email protected]"}');

// Asserted: trusted, unchecked, zero runtime cost.
const asserted = payload as User;
console.log(asserted.email);

// Guarded: the same narrowing, but the condition actually runs.
if (isUser(payload)) {
  console.log("validated:", payload.id);
} else {
  console.log("payload did not match User");
}

The overlap rule and the two-step escape hatch

Prints `42.0`, then `undefined`, then `string`. The last two lines are the point: after the double assertion the checker treats `bad` as a number and would let you call `bad.toFixed(2)`, but at runtime it is still the string `"hello"` and `toFixed` does not exist on it.

Example · typescript
// Angle-bracket form. Legal in .ts, a syntax error in .tsx,
// where <number> is parsed as the start of a JSX element.
const wide: unknown = 42;
const n = <number>wide;
console.log(n.toFixed(1));

// tsc rejects this outright:
//   Conversion of type 'string' to type 'number' may be a mistake
//   because neither type sufficiently overlaps with the other.
// const bad = "hello" as number;

// Going through unknown is accepted — and completely untrue.
const bad = "hello" as unknown as number;
console.log(bad.toFixed);
console.log(typeof bad);

Discussion

  • Be the first to comment on this lesson.