Run TypeScript in Deno Without a Separate Build Step
Deno runs TypeScript directly, but type stripping is not type checking. Learn when to skip tsc and when to keep deno check.
08 Aug 2026, 23:50 UTC

The problem: waiting on tsc for every change
Your Node service has a ninety-second TypeScript build. CI fails on a type error in a file nobody touched, and the application still crashes at runtime because a type assertion hid a null value. You want to remove the compile step, but you do not want to trade build delays for silent type failures.
Useful takeaway: Deno executes TypeScript files directly with no separate tsc step. However, the runtime strips types before execution; it does not enforce them. You still need an explicit type-checking step in development or CI.
How Deno runs TypeScript
Deno includes a TypeScript compiler internally. When you run deno run, Deno parses the TypeScript, removes type annotations, and executes the resulting JavaScript in memory. There is no package.json or node_modules directory. Imports are file paths or URLs, and Deno caches remote modules in a global cache directory.
Type stripping is not type checking. Deno will run code that contains type errors unless you explicitly check it first.
Worked example: a small HTTP service
Create a file named server.ts:
import { serve } from "https://deno.land/std/http/server.ts";
const port = 8000;
console.log(`Listening on http://localhost:${port}`);
for await (const req of serve(port)) {
req.respond({ body: "Hello from Deno\n" });
}
Run it from your terminal:
deno run --allow-net --allow-read server.ts
- Where to run: any terminal with Deno installed.
- Permissions: --allow-net permits outbound network connections; --allow-read permits filesystem reads. Include only the flags your code needs.
- Placeholders: replace server.ts with your file path and 8000 with your preferred port.
- Expected check: confirm the service starts and responds to a request such as curl http://localhost:8000.
- Risk: --allow-net grants broad network access. Use narrower flags in production.
Add type checking to your workflow:
deno check server.ts
This command reports type errors without executing the file. Run it in CI or as a pre-commit hook. A clean exit means no type errors; a non-zero exit means fix the types before deploying.
Pin remote imports with a lockfile so a changed remote module cannot break your build:
deno run --lock=deno.lock --allow-net server.ts
Generate deno.lock with Deno's cache tooling before committing it. The lockfile records the versions or hashes of remote modules.
Trade-offs and limitations
- Runtime type errors are still possible. deno run does not fail on type mismatches by default.
- Remote imports can change. Use a lockfile or vendor modules locally to avoid surprise updates.
- Node.js compatibility is not automatic. CommonJS modules often require the node: prefix or compatibility flags, and some npm packages do not work out of the box.
- Team tooling. Developers need Deno installed and IDEs configured for Deno's TypeScript language service.
How to verify the result
After you deploy, run deno check on the exact files in CI. If it passes and the service starts with the correct permission flags, the native TypeScript execution path is working. If you see runtime errors that deno check did not catch, that confirms types were stripped and not enforced.
Closing decision
Remove the separate build step if your team can adopt deno check in CI and your dependencies work with Deno's module system. Start with a single service, measure the build time difference, and keep the type-checking gate so you do not trade speed for undetected type errors.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.