Skip to content
TypeScript ts getting-started 3 min read

Your First TypeScript Program

With TypeScript installed, the fastest way to understand it is to write a small program, compile it to JavaScript, and watch the type checker reject a deliberate mistake. This page walks through creating a .ts file, compiling it with tsc, running the output, and then iterating with watch mode and direct execution tools. By the end you will have seen the full loop — write, check, emit, run — that every TypeScript workflow is built on.

Write a hello-world file

Create a file called hello.ts. Add a typed function and call it. Notice the : string annotations describing the parameter and return type.

// hello.ts
function greet(name: string): string {
  return `Hello, ${name}!`;
}

const message = greet("DevCraftly");
console.log(message);

TypeScript infers that message is a string from greet’s return type, so you do not need to annotate it. Inference handles most cases; you add annotations mainly at the boundaries — function parameters and public APIs.

Compile with tsc

Run the compiler on your file. It type-checks the code and, if there are no errors, emits a JavaScript file next to it.

npx tsc hello.ts

This produces hello.js. By default tsc targets a modern JavaScript version and writes the output in the same directory:

// hello.js
function greet(name) {
  return `Hello, ${name}!`;
}
const message = greet("DevCraftly");
console.log(message);

The type annotations are gone — exactly the “type erasure” behavior that lets the result run in any JavaScript engine.

Run the output

Execute the compiled JavaScript with Node.js.

node hello.js
# Hello, DevCraftly!

That is the complete classic loop: write .ts, compile to .js, run with node. Next we will see what happens when the code is wrong.

Watch the type checker catch a bug

Change the call to pass a number instead of a string and recompile. The compiler refuses to proceed.

const message = greet(42);
// ❌ Error: Argument of type 'number' is not assignable to
//    parameter of type 'string'.
npx tsc hello.ts
# hello.ts:6:23 - error TS2345: Argument of type 'number' is not
# assignable to parameter of type 'string'.

This is the entire value proposition in miniature: a bug that JavaScript would have happily run (producing "Hello, 42!" or worse in a real program) is stopped at compile time with a precise, located message.

Iterate faster with watch mode

Recompiling by hand gets tedious. Watch mode recompiles automatically whenever you save, giving instant feedback.

npx tsc hello.ts --watch
# [hh:mm:ss] Starting compilation in watch mode...
# [hh:mm:ss] Found 0 errors. Watching for file changes.

Leave it running in a terminal while you edit. Each save reruns the type check and re-emits the output.

Skip the build step with tsx

In development you often want to run TypeScript directly without a separate compile step. Tools like tsx (or ts-node) transpile on the fly.

npx tsx hello.ts
# Hello, DevCraftly!
CommandWhat it doesWhen to use
tsc hello.tsType-check + emit hello.jsProducing build output
tsc --watchRecompile on every saveActive development
tsx hello.tsRun .ts directly, no .js fileScripts, quick runs
node hello.jsRun the compiled outputProduction / CI

tsx and ts-node transpile but do not fully type-check while running (they are optimized for speed). Keep tsc --noEmit in your CI pipeline so real type errors still fail the build.

Best Practices

  • Annotate function parameters and return types; let inference handle local variables.
  • Use watch mode (tsc --watch) during development for instant feedback.
  • Reach for tsx to run scripts quickly without an intermediate build.
  • Always run tsc --noEmit in CI, since fast runners skip full type checking.
  • Read compiler error messages carefully — they include the file, line, and the exact mismatch.
  • Keep source (.ts) and output (.js) separated via outDir once a project grows beyond one file.
Last updated June 29, 2026
Was this helpful?