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.
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:
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.
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.
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
- Default to inference, let TypeScript see the literal you wrote
- Use
satisfieswhen you want a value checked against a contract without widening - Use type guards / narrowing when runtime truth matters
- Use
as constfor immutable literal collections - Use
aslast, only when you can prove something the checker cannot
Swap one keyword
- 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
TypeScript 7 is ~10× faster. Why your CI often only gets ~28%
The 10× headline is real on the right hardware. On shared CI, structure still wins: config, types, and project layout.
TypeScript generics that stop fighting you
Good generics constrain and infer. Callers pass values and get precise types back, with no `<T>` by hand.
Make TypeScript check your strings: template literal & mapped types
Model routes, events, and keys as types, not `string`, so typos become red squiggles before runtime.