TypeScript
So apparently most companies that are hiring for backend devs are obsessed with Typescript (and Nest).
I was very okay with JavaScript but looking at the market condition I thought maybe I should learn TypeScript, not because I really want to but because the market demands it. How annoying could it be ? Spoiler Alert: Very Annoying
I wont go describing my journey because nobody reads these things except for myself when I need to refer back to things. This is a short note on the start of TypeScript and what I learnt. I can be wrong on many things but I am improving....and also I use Gemini to learn TS.
What is TS ?
TS is JS with types and some extra features which are just decoration for JS code.
Example:
This is a valid JS code:
let x = 10;
and this is a valid TS code:
let x: number = 10;
So I just say the type of variable x and now its a TS code....GREAT !
Now coming from C/C++ background I like this, no seriously I have no problem with this at all. TypeScript compiles to JavaScript ! Therefore any valid JS code is a valid TS code.
main.ts -> Compiles -> main.js
Compiler
tsc is the compiler for TS, its responsible for compiling TS codes into JS.
To install the compiler we can do:
npm i typescript
or
npx tsc
All good but from here and now I encountered quite a lot of BS...
Cannot Redeclare Block-Scoped Variable
This was the issue I first encountered and didn't understood why was this the error. When I compiled my TS file to a JS file I got this error in my TS file with red squiggly lines over the variable.
My code looked this simple
let x: number = 1;
console.log(x);
Then upon asking Gemini and doing a bit more coding I understood that the TypeScript Language Server (TSServer) scans my whole directory. It treats every .js and .ts files as part of a same huge global scope ! As in 2 .js files added to a .html file using <script> tag and both the JS files have the same variable names.
Therefore long story short - Everything inside a project is in a big global scope by default as if it were a browser !!!
i.e. everything there is treated as a script. So when I compiled my TS file and it generated a JS file both my TS file and JS file had the same variable declarations in them and both are put in the same global scope as the whole project hence the error of cannot redeclare block-scoped variable !
If I were to make every file a module so that the scope remains file specific I must add something like export {} <- This marks the file as module ! Or import something like a library like express...An import or export marks the file as module !
I was stunned with this crazy aah nature!!
Still have to run JavaScript !
TS compiles to JS so to run the code I still need a runtime like Node to run the actual code !
tsconfig.json
This is a configuration file that is used to apply various rules for the TS compiler to follow. It lives at the root of our project !
Here is an example
{
"compilerOptions": {
"target": "ES6",
"module": "commonJS",
"rootDir": "./src",
"outDir": "./dist",
"esModuleInterop": true,
"strict": true
}
}
I wont explain because I can just google or ask AI what each of them are...
NOTE: If we compile a specific file using tsc like tsc main.ts it will generate a JS file but ignore the config totally ! Therefore the rule is to run just run tsc and let it handle everything !
CommonJS vs ES6
This is not exactly a commonJS(require) vs ES6(import) debate. But how TS handles it !
In an ES6(import) scenario if I were to import a custom module it might look like this
import { something } from "./something.ts";
This...this wont work and here is why: TS assumes that at runtime, the .ts files wont exist.
So when compiled to .js the JS file will say "./something.ts" and the browser or node will not be able to find the .ts file and crash.
So to solve this either we go with commonJS(require) or we write something like:
import { something } from "./something.js"
Even though we are writing it in a TS file !
Or use allowImportingTsExtensions in the config file. But then I also have to add noEmit set as true and there will be no JS files and we will need to use other tools like esbuild to get the JS files. (I didn't test esbuild, I just took Gemini's word for it !)
@types/
So we might need to install extra type definitions for libraries for them to work with TS. Like for using express we need to do:
npm i express @types/express
Which will install express - the base library and its type definitions which are present in @types/express !
Many libraries comes with inbuilt type definitions like nanoid so they work out of the box !
Basic Idea of a TS directory Structure and Execution
This part from all the learnings I wrote a basic express server with a tsconfig.json and try to explain the directory structure and how to run the server and what TS generates...
Directory Structure:
/ (root)
|
|-> node_modules/ (folder)
|-> dist/ (folder)
|-> src/ (folder)
| |->server.ts
|
|-> package.json
|-> package-lock.json
|-> tsconfig.json
- src: Every TS file and source sub directory lives here.
- dist: The destination where the JS files where be generated into.
- Initialize the project
npm init
Set the entry point as dist/server.js
- Install packages
npm i express @types/express -D typescript
We install typescript as a dev dependency.
- Modify
package.jsonto add scripts
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"dev": "node dist/server.js"
},
Add build and devscript.
- The
buildscript is responsible for generating the JS files (goes intodistfolder). - The
devscript is used to run the server (the JS files).
- Write the server code
src/server.ts is our main server file there we write the most basic express code
import express, { Request, Response } from "express";
const app = express();
app.listen(3000, function(){
console.log("TS server active on port 3000");
});
app.get("/", function(req: Request, res: Response){
try{
res.status(200).json({
message: "Hi from TS server !!"
});
}
catch(error){
console.log(error);
res.status(500).json({
message: "Some Error Occured"
});
}
});
This is one of the most simple express server code with 1 API endpoint !
- Build the project
npm run build
This will generate JS files and put them inside the dist folder (as mentioned in the tsconfig.json ("outDir": "./dist")).
- Run the server
Run the server using the script
npm run dev
Server will be running at port 3000 !
Build Flow
tsc takes every file from source (as mentioned in tsconfig.json) compiles it using the rules and emits JS files and put them inside the dist folder (as mentioned in tsconfig.json). TypeScript's part is done here the actual running server is still a JS Node Server, the entry point of that server is still a JS file ! Nothing Fancy !
Compile Time Only
TS is compile time only ! IT CANNOT DO ANYTHING AT RUNTIME !!
TypeScript is just for type checking and other fancy features just to write safer JavaScript Code ! It still generated JavaScript and we still run JavaScript ! Its just a guard rail wrapper nothing else !
Everything that is added is at compile time, every error shown is at compile time...TypeScript doesn't ensure runtime safety at all ! ...And that is a point to be noted very clearly !
This was my first One Hour Against TypeScript !
I hate it but the industry doesn't so I have to learn it but at the end of the day its just a tool which can help me make my life a bit easier even if annoying !