Learn/TypeScript Deep Dives

TypeScript Deep Dives · Part 2

TypeScript `satisfies` vs `as`: stop silencing the compiler

`as` tells TypeScript to trust you and stop checking. `satisfies` validates a value against a type without widening it. Learn when each is correct, and why `"3000" as number` ships bugs.

7 min read
TypeScriptsatisfiesType safety

as isn’t a conversion

When developers write as, they often think they are converting a value. They are not.

as is a promise to the compiler: trust me, stop checking. Nothing changes at runtime. The only thing that changes is whether TypeScript is allowed to complain.

That is why this compiles:

ts
const port = "3000" as number;

…and then fails the moment you treat port like a number.

The bug as hides

Assertions are especially dangerous in config objects, API payloads, and “temporary” escapes that stay forever.

ts
type Config = { port: number };
 
// Compiles. Lies.
const bad = { port: "3000" } as Config;
 
// Errors at compile time, as it should
const good = { port: "3000" } satisfies Config;

satisfies validates the value against Config without forcing the value into a wider or incorrect shape.

as also erases what you know

Even when the assertion is “true enough,” it often widens or flattens the type you just carefully wrote, so you lose autocomplete and exact keys.

ts
type Route = { path: string; auth: boolean };
 
const routesAs = {
  home: { path: "/", auth: false },
  admin: { path: "/admin", auth: true },
} as Record<string, Route>;
// routesAs.home.path is fine… but key autocomplete is gone
 
const routes = {
  home: { path: "/", auth: false },
  admin: { path: "/admin", auth: true },
} satisfies Record<string, Route>;
// Checked against Route *and* keeps exact keys: "home" | "admin"

That combination, validated + precise, is why satisfies belongs in most places teams currently reach for as.

A practical rule set

  1. Default to inference, let TypeScript see the literal you wrote
  2. Use satisfies when you want a value checked against a contract without widening
  3. Use type guards / narrowing when runtime truth matters
  4. Use as const for immutable literal collections
  5. Use as last, only when you can prove something the checker cannot

Swap one keyword

ts
- const config = { port: "3000" } as Config;
+ const config = { port: 3000 } satisfies Config;

You keep the exact type. You get real validation. You stop shipping “it type-checks, then it crashes.”

If your codebase is full of as, treat every occurrence as a smell until proven otherwise, especially in shared config and domain models.

FAQ

What is the difference between `as` and `satisfies` in TypeScript?+

`as` asserts that a value is a type and suppresses checking. `satisfies` checks that a value is assignable to a type, then keeps the value’s more precise inferred type (including literal types).

When is `as` still OK?+

When you truly know more than the checker can prove, for example narrowing a DOM result after a runtime check, or using `as const` for immutable literal tuples/objects. Prefer type guards and `satisfies` first.

Does `satisfies` change runtime behavior?+

No. Like `as`, it is erased at compile time. The difference is only what the type checker validates and what type remains for later use.

TypeScript workshops for teams

On-site, hands-on sessions where we replace “trust me” casts with patterns your whole team can use on the real codebase.

View workshops →

Keep learning