Topic 0.4
Your First TypeScript Project
In one line
Install TypeScript in a folder with npm, create a tsconfig.json with tsc --init, write a .ts file, then check it with tsc and run it with Node, which can run TypeScript files directly by stripping the types.
Think of it like this
Setting up a new kitchen. You bring in the cooker (Node.js), buy one special tool for this kitchen only (TypeScript, installed in the project), pin the house rules on the wall (tsconfig.json), then start cooking (write .ts files) and tasting as you go (tsc checks). A TypeScript project is a folder with those four things.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Node.js
- A program that runs JavaScript (and now TypeScript, by stripping types) outside the browser.
- npm
- The package manager that comes with Node.js. It downloads packages and records them in package.json.
- Package
- A bundle of ready-made code published to the npm registry, like
typescript. - Dev dependency
- A package needed while you build the project (like TypeScript) but not by the finished program. Installed with
-D. - npx
- Runs a command from the project's own installed packages, e.g.
npx tsc. - tsconfig.json
- The project's TypeScript settings file. Its presence marks the folder as a TypeScript project.
- Watch mode
tsc --watch: keep running and re-check every time a file is saved.- Type stripping
- Deleting type syntax to get runnable JavaScript, without checking anything. Node.js does this for
.tsfiles.
Step by step
01Make a project folder
Create an empty folder and open a terminal in it. npm init -y writes a package.json with default values (-y answers yes to every question).
02Install TypeScript for this project
-D saves it as a dev dependency. npm also downloads the small native compiler binary for your operating system.
03Create tsconfig.json
Check the version, then let TypeScript write a starting configuration.
04What --init wrote
This is the real file (TypeScript 7.0.2), with its comments. Strict checks are on, plus two extra-safe options, noUncheckedIndexedAccess (Topic 1.4) and exactOptionalPropertyTypes (Topic 1.5). This course's live editor uses the same set.
{
// Visit https://aka.ms/tsconfig to read more about this file
"compilerOptions": {
// File Layout
// "rootDir": "./src",
// "outDir": "./dist",
// Environment Settings
// See also https://aka.ms/tsconfig/module
"module": "nodenext",
"target": "esnext",
"types": [],
// For nodejs:
// "lib": ["esnext"],
// "types": ["node"],
// and npm install -D @types/node
// Other Outputs
"sourceMap": true,
"declaration": true,
"declarationMap": true,
// Stricter Typechecking Options
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
// Style Options
// "noImplicitReturns": true,
// "noImplicitOverride": true,
// "noUnusedLocals": true,
// "noUnusedParameters": true,
// "noFallthroughCasesInSwitch": true,
// "noPropertyAccessFromIndexSignature": true,
// Recommended Options
"strict": true,
"jsx": "react-jsx",
"verbatimModuleSyntax": true,
"isolatedModules": true,
"noUncheckedSideEffectImports": true,
"moduleDetection": "force",
"skipLibCheck": true,
}
}05Write, check, run
Create index.ts. Run npx tsc: no output means no errors. It wrote index.js, plus index.js.map (source map), index.d.ts and index.d.ts.map (type declarations for other code to use, Topic 11.3), because the config turned those on.
Run the JavaScript with node index.js, or skip the build and run the TypeScript with node index.ts.
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(greet("Asha"));06See an error in the terminal
Change the call to greet(42). tsc --noEmit --pretty false prints the same one-line form as this course's Types panel; the exit code is 1. Node still runs the file, because it never checks types.
07Keep it checking: watch mode
--watch clears the screen and re-checks on every save. Leave it running in a second terminal while you work. Stop it with Ctrl+C.
Try it yourself
- 1
Ask for more types
In the first example, add a line
const loud = message.toUpperCase();and a// ^?line under it with the caret underloud. Predict the answer, then Run. - 2
Break the return type
In the second example, change the return line to
return name.length;. The function promised astringand now returns anumber: read where the error appears (thereturn, not the call). - 3
On your computer
Follow the walkthrough in a real folder. Then add
enum Color { Red, Green }andconsole.log(Color.Green);toindex.ts.npx tsc && node index.jsprints1, butnode index.tsfails: enums need generated code, which Node's type stripping doesn't do.
Code & diagrams
Same program as the terminal walkthrough. The ^? lines ask for the types: greet returns string, inferred from the template string, so message is a string too.
Console
(nothing was printed)Expected console output
Hello, Asha!Exactly tsc's line from the walkthrough. Like node index.ts, the editor still runs the code.
Console
(nothing was printed)Expected console output
Hello, 42!Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Run an enum with Node's type stripping
Put enum Color { Red, Green } and console.log(Color.Green); in enum.ts and run node enum.ts (Node 24.11).
Break #2
Name a file when a tsconfig.json exists
Run npx tsc index.ts in the project folder.
Myth vs fact
Myth
Install TypeScript globally once with npm install -g typescript.
Fact
Install it in each project (-D) so every person and the CI use the same version; run it with npx tsc. A global install drifts out of sync.
Myth
node index.ts checks my types.
Fact
Node only strips types and runs what's left. Errors are invisible to it. Run tsc --noEmit (or watch mode) to check.
Myth
You must compile before you can run TypeScript.
Fact
Modern Node runs .ts files directly, and bundlers like Vite strip types on the fly. Compiling with tsc is one option; checking with tsc is the part you can't skip.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
module: nodenextmakes TypeScript follow Node's rules exactly: whether a file is ESM or CommonJS depends onpackage.json"type"and the file extension (.mts/.cts), and relative ESM imports need file extensions. That's why--initpicks it for Node projects, and why browser/bundler projects usebundlerinstead (Topic 11.2). - ▸
Node's type stripping replaces type syntax with whitespace, so line and column numbers in stack traces match your source with no source maps. It ignores
tsconfig.jsonentirely, and type-only imports must useimport type(otherwise Node tries to import a value that doesn't exist);verbatimModuleSyntaxin the tsconfig makes TypeScript enforce that. - ▸
For CI,
tsc --noEmitexits non-zero on any error and is enough on its own; projects with references usetsc --build --noEmit(Topic 12.4). With 7.0,--checkerscan be raised on big CI machines for more speed.
Remember this
- 1
The problem this solves: Starting TypeScript with a wrong setup, such as a global install or a different version on each machine, gives results that differ between people. A project-local setup makes everyone's checks match. You need Node.js, the program that runs JavaScript outside the browser and comes with npm, the tool that downloads packages (ready-made code) from the npm registry. Check both with
node --versionandnpm --version. The outputs in this topic are real, made with Node 24.11 and TypeScript 7.0.2. - 2
TypeScript is installed per project, as a dev dependency (a package needed while developing, not by the finished program):
npm install -D typescript. It goes into the project'snode_modulesfolder and is recorded inpackage.json, so everyone on the team gets the same version. You run it with **npx tsc**:npxruns a command from the project's ownnode_modules. - 3
**
npx tsc --initwrites atsconfig.json**, the project's settings file. Its presence tellstsc"this folder is a TypeScript project", which files belong to it, and how strict to be. TypeScript 7's--initwrites modern, strict settings with comments (shown below). Phase 12 goes through every option; for now, know thatstrictis on andmodule: nodenextmeans "follow Node's own rules for modules". - 4
With a
tsconfig.json, running **npx tsc** with no file names checks every.tsfile in the folder and writes.jsfiles next to them. **npx tsc --noEmitonly checks.npx tsc --watch** keeps running and re-checks every time you save. Note: with atsconfig.jsonpresent,tsc index.ts(naming a file) is an error,TS5112; plaintscis the normal way. - 5
**Node.js can run
.tsfiles directly**:node index.ts. It doesn't check types; it strips them (deletes the type syntax and runs what's left), so it's fast. According to Node's docs this is on by default since v22.18.0 and v23.6.0, without an experimental warning since v22.18.0 and v24.3.0, and stable since v24.12.0 and v25.2.0. Because it only deletes, syntax that needs to *generate* JavaScript (enum, namespaces with values, parameter properties) fails in Node withERR_UNSUPPORTED_TYPESCRIPT_SYNTAX. The usual workflow: Node or a bundler to run,tsc --noEmitto check (Topic 12.5). - 6
Your editor is the other half. VS Code understands TypeScript out of the box and shows errors as red squiggles while you type; hovering a name shows its type (that's what
// ^?imitates here). For TypeScript 7's faster language server, VS Code has the "TypeScript 7" extension (TypeScriptTeam.native-preview). Other editors (WebStorm, Zed, Neovim) use the same language server. - 7
How this course's live editor works: each example is one file,
index.ts, with the same strict settings as atsc --initproject (adjusted for the browser:module: esnextwithmoduleResolution: bundler). The Types panel shows exactly whattsc --pretty falseprints, then the answers to your// ^?queries. Then the JavaScript runs and the Console shows what it prints. Edit freely: Run (or Ctrl+Enter) re-checks; the reset button brings the original back.
Explain it without notes
Walk through creating a TypeScript project from an empty folder.
What's the difference between tsc, tsc --noEmit and node index.ts?
Why is TypeScript installed per project instead of globally?
Practice
Your teammate's script runs tsc src/index.ts and now fails with TS5112. Rewrite the command so it checks the whole project with its tsconfig and writes nothing.
Write the scripts section of a package.json with check (type-check only), watch (re-check on save) and start (run index.ts with Node).
Trade-offs
- ↔
Running
.tsdirectly with Node (or a bundler) is fastest to start but checks nothing;tscchecks but is a separate step. Most projects do both: strip to run,tsc --noEmitto check. - ↔
tsc --init's strict settings catch more bugs but feel noisy at first. Learning with them on (as this course does) is much easier than switching them on in a big codebase later.
Done when you can
I can create a TypeScript project:
npm init,npm install -D typescript,npx tsc --init.I can check a project with
npx tsc --noEmitand keep it checking with--watch.I can run a
.tsfile with Node and explain what type stripping can't run.I can read the same error in the terminal and in this course's Types panel.
I can use the live editor: edit, Run, read the Types panel, use
// ^?.