Setting Up and Running Your First File
Getting from nothing to a running, type-checked file: the runtime, the compiler, a real project folder, and what each generated file is for.
Getting from nothing to a running, type-checked file: the runtime, the compiler, a real project folder, and what each generated file is for.
You have a file called hello.ts and no idea how to make
anything happen with it. Double-clicking does nothing useful.
The instructions you found online mention four tools, assume you
already have three of them, and produce an error about a module
system nobody explained.
Setup is the step that stops more beginners than any concept. By the end of this lesson you will have a working project on your own machine, a program you wrote running in a terminal, and an understanding of what each file that appeared is for — because a folder full of unexplained files is its own obstacle.
Node runs the finished JavaScript. It also brings npm, a tool for installing packages, which is how you will get everything else.
Get it from nodejs.org and take the version marked LTS —
long-term support, meaning the stable one. Then check:
node --version # v22.11.0 or similar
npm --version # 10.9.0 or similarTwo numbers means you are ready. If a command is "not found", close the terminal and open a new one — an installer adds itself to the terminal's list of known commands, and a terminal already open does not see the change.
TypeScript itself you will install into your project rather than onto your machine, and the difference is worth stating now because most instructions online get it wrong.
One version, for your whole machine.
Two projects cannot use two different versions, and a colleague who clones your project gets whatever they happened to install — which is how "it compiles on my machine" starts.
A version per project, recorded in the project.
The version is written into package.json, so anyone who
clones and runs npm install gets exactly the one your code
was checked against.
Five commands. Run them in an empty folder.
mkdir photo-tools && cd photo-tools
npm init -y # creates package.json
npm install --save-dev typescript tsx @types/node
npx tsc --init # creates tsconfig.json
mkdir srcNow the folder contains things you did not write, and each one has a job:
package.json what this project is and what it needs
package-lock.json the exact versions installed
node_modules/ the installed packages themselves
tsconfig.json how to check and compile your code
src/ your code goes herepackage.json is the project's description — its name, and
a list of the packages it depends on. You will edit this one.
package-lock.json records the exact version of everything
that was installed, including the packages your packages needed.
Committing it means a colleague gets identical versions rather
than merely compatible ones. Never edit it by hand.
node_modules/ holds the packages. It is large, it is
rebuilt from the two files above with npm install, and it must
never go into version control.
tsconfig.json configures the compiler. It arrives full of
commented-out options; you will meet the ones that matter in a
later lesson.
What you installed:
typescript the compiler
tsx runs a .ts file directly, without a build step
@types/node type descriptions for Node's own featuresThat last one needs a word. Node is not written in TypeScript,
so the checker does not know what console or a file-reading
function looks like. @types/node is a set of descriptions that
tells it. Packages beginning @types/ are exactly that —
descriptions of something that has none of its own.
Create src/hello.ts:
const name: string = "Ana";
console.log(`Hello, ${name}. Nothing is on fire.`);Run it:
npx tsx src/hello.tsHello, Ana. Nothing is on fire.Three things are new. const gives a value a name — the next
lesson covers it properly. console.log prints to the terminal
and is how you find out what your program thinks. And the
backticks with ${...} inside drop a value into a piece of
text, which is covered in the strings lesson.
npx runs a tool from node_modules without installing it
globally. tsx checks nothing and just runs the file, which is
what you want while experimenting.
This is the distinction that confuses people, and it is worth meeting immediately. Change the file to something wrong:
const price: number = "12.99";
console.log(price * 100);npx tsx src/hello.ts12.99100No error. tsx strips the types and runs the result, and
the running language happily joins text to a number rather than
multiplying. You got nonsense with no complaint.
Now ask the checker:
npx tsc --noEmitsrc/hello.ts:1:7 - error TS2322: Type 'string' is not
assignable to type 'number'.--noEmit means "check, produce nothing". This is the command
that answers "is my code correct?", and it is separate from
running.
Bad — relying on the program running to tell you it is right.
npx tsx src/index.ts # it ran, so it must be fineGood — checking, then running.
npx tsc --noEmit && npx tsx src/index.tsThe two commands answer different questions, and only one of them is about correctness.
Runs it. Checks nothing.
Strips the types and executes the result, so a wrong type produces nonsense rather than an error.
It exercises only the paths your input happened to take.
Checks it. Produces nothing.
Examines every line, including the branch for an error that happens twice a year — which is the branch nobody tests and the one most likely to be wrong.
Silence means everything checked out. That silence is the whole benefit of the language.
Your editor runs the checker continuously, which is why errors appear as you type. The command-line version is what you run before committing, and later in CI.
Typing those is tedious. package.json has a scripts section
for exactly this:
{
"scripts": {
"check": "tsc --noEmit",
"start": "tsx src/index.ts",
"build": "tsc"
}
}npm run check
npm run start
npm run buildNow the commands are recorded in the project. A colleague
cloning it runs npm install and then npm run check, without
being told anything — which is the real point.
npm run build runs tsc with no --noEmit, so it writes
JavaScript. Where it goes is set in tsconfig.json:
{
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
}
}npm run build
node dist/hello.jsLook inside dist/hello.js. It is your program with the types
gone — the lesson from before, now visible in a file you can
open. That output is what you would deploy, and node runs it
without TypeScript installed at all.
While learning, use tsx and skip the build entirely. Reach for
it when you need to ship something.
INSTALL ONCE
nodejs.org, the LTS version
node --version npm --version
"not found"? open a NEW terminal
A NEW PROJECT
npm init -y
npm install --save-dev typescript tsx @types/node
npx tsc --init
mkdir src
.gitignore: node_modules/ and dist/
WHAT THE FILES ARE
package.json the project and what it depends on
package-lock.json exact versions - commit it, never edit it
node_modules/ the packages - never commit
tsconfig.json how to check and compile
src/ your code
dist/ the compiled output
WHAT YOU INSTALLED
typescript the compiler
tsx runs a .ts file directly, no build
@types/node descriptions of Node, so the checker knows it
@types/... descriptions for a package that has none
THE TWO COMMANDS - they are different
npx tsc --noEmit CHECK. no output means correct
npx tsx src/x.ts RUN. strips types, does not check
running is not checking: it only exercises the paths your
input took. check first, then run
SCRIPTS - record them in package.json
"check": "tsc --noEmit"
"start": "tsx src/index.ts"
"build": "tsc"
npm run checkYou have a project, a program that runs, and — more importantly — the distinction between running your code and checking it. That separation is the thing to hold on to: the errors that matter appear from a command that prints nothing when all is well.
Next is Values, Variables, and Basic Types, where the course stops being about your machine and starts being about the language. It begins with what every program does before anything else: holding on to a piece of information and giving it a name.
Before you move on, break your program on purpose. Assign text
to a number, run it with tsx and see it produce nonsense, then
run tsc --noEmit and read the error. Doing that once fixes the
difference between the two commands permanently, and it is the
difference that makes this language worth using.