TypeScript - DavidBK/shampoo GitHub Wiki
Estimation time: 1-2 Days
TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.
This is a basic introduction to Typescript.
Learning objectives:
At the end of this learning path, you'll be able to:
- Run TS project
- Write an efficient type-safe code in TypeScript
Send me back home
[[TOC]]
Learning note: all links in this path are reading tutorials. You can read them but you can watch a youtube crash course. if you find useful links, please share them with me.
Here is some examples links:
- Typescript official website
- Typescript handbook
- No BS TS Youtube Crash Course
- Total typescript interactive course
- Type Challenges Repo - A collection of TypeScript type challenges
You can start "playing" with typescript online in sandbox.
In order to run it locally we need to install typescript and then compile our code into executable javascript.
TypeScript is available as a package on the npm registry.
You can install it as dev-dependency in your project using:
npm install typescript --save-devif you want typescript support in node: imports and global node variables you need to install @types/node
npm install @types/node --save-devSide Note: Sometimes You will need types in the declaration file. If so install the types as dependencies. You can read this for more information.
Typescript compiler needs instructions to compile. We can specify the compiler options in config file called tsconfig.
You can initialize the compiler options using tsc cli --init flag:
npx tsc --initThis will create a basic tsconfig.json file.
My recommended tsconfig.json for modern node applications is:
compilerOptions:
-
"module": "NodeNext"- Tells TS that this project use ES Modules (And the module resolution is NodeNext) -
target": "ESNext"- Tells TS that it's okay to output JavaScript syntax with new features instead of embedding a polyfill. -
"strict": true- Tells TS to enables a wide range of type checking behavior that results in stronger guarantees of program correctness. -
"noUncheckedIndexedAccess": true- Tells TS to addundefinedto any declared via index signatures field in the type. -
skipLibCheck": true- Tells TS to skip type checking of all declaration files (*.d.ts). This can save time during compilation and solve incompatibly issues between libraries.
Here is a recommended tsconfig.json file:
{
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"outDir": "./dist",
"strict": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true,
"skipLibCheck": true,
},
"include": ["src/**/*"],
"exclude": ["node_modules"],
}You can run this script in your terminal to create the file:
curl https://gitlab.com/davidbk6/shampoo/-/raw/14055316e7a305253eef65ed430a169e1141d70a/tsconfig.json -o tsconfig.jsonHere is the full TSConfig Reference
You can use the tsc cli using npx:
npx tscOr you can create a npm script:
In your package.json:
"scripts": {
"build": "tsc"
},And then:
npm run buildTest it! create .ts file and run the compile .js.
tsx (TypeScript eXecute - do not confuse with React's TSX files which stand for TypeScript XML) is a TypeScript runtime that allows you to execute TypeScript files directly.
This tool is useful for running TypeScript files without the need to compile them to JavaScript first.
You can use it without any setup using npx:
npx tsx <your-file>.tsYou can read more in the tsx repo.
ts-node is a TypeScript execution engine and REPL for Node.js. It "Just In Time" (JIT) transforms TypeScript into JavaScript, enabling you to directly execute TypeScript on Node.js without precompiling.
Install it as a devDependency:
npm i -D ts-nodeAnd then run your file:
npx node-ts <your-file>.tsYou can add it to your npm scripts:
"dev": "ts-node <your-file>.ts"If you like to use esm (and you should) you can use it as a loader to your file:
"dev": "node --loader ts-node/esm <your-file>.ts"If you use ts-node I recommend to add the configuration to the tsconfig file as "ts-node".
"ts-node": {
"swc": true,
}You can read more in the ts-node docs.
Some modern JavaScript engines runtime come with built-in support for TypeScript:
-
Deno is a modern runtime for JavaScript and TypeScript that uses V8 and is built in Rust. Deno has "First-class" support for TS and its "Understand" (Using JIT) Typescript so you execute your
.tsusingdeno. -
Bun is a modern JavaScript runtime like Node or Deno. Bun uses the JavaScriptCore engine and was written in ZIG. Bun is designed as a drop-in replacement for your current JavaScript & TypeScript apps or scripts so you can run
.tsfiles directly.
You should be familiar with the following concepts:
You can read them all in the TypeScript Handbook. Especially the everyday-types
-
string,number, andboolean - Array
- Tuple
- Functions
- Object types
- Type Aliases (
type) - Interfaces
-
anyandunknown - Union Types (
|) - Intersection Types (
&) - Function type expression
- Parameter Destructuring in functions
- Passing Type Arguments (
<Type>) - Type Assertions (
as,!) -
typeoftype operator -
keyoftype operator -
null,undefined,void, andnever - Narrowing and Type guards
- Global utility Types (e.g
Omit,Record,Promise, etc.)
Here is a great tutorial for all the basics. Do it yourself 😄
-
Take the following code and paste it in new
test.jsconst echo = (arg) => arg; const greet = echo("Hello From World"); const veryGreet = greet.map((ch) => ch + "!"); console.log(veryGreet);
Run it using
node test.jsand see the error.- What is the problem?
- How typescript can help me?
- Create
test.tsfile and fix the code so it will not compile.
-
What is the problem? Fix it.
const productPrices = { apple: 1.2, banana: 0.5, orange: 0.8, }; const getPrice = (productName: string) => productPrices[productName];
If you can't see the problem paste the code to TS Playground and see the error.
- Write a little TS as you can! TS is very "smart" and most of the time inferred types will be more "specific". Also this will help your code readability.
- Don't declare types in variables
- Prefer to not declare return types of simple functions
- Don't use enums, prefer union types or
as constobjects. - Don't use
Function,Object,String,Number, andBoolean(Capital letters) andobject- they are not doing what you think they are doing. - Be careful with
.json()(infetchresponse) andJSON.parse()as they both returnany. You can cast them tounknown(or use ts-reset to do it for you) - I Prefer
typealiases over interfaces for "everyday" types. If you using Object Oriented Programming you can use interfaces but keep in mind the merge behavior of interfaces.
TypeScript is lived in "compile-time", which make its great for checking variables at the type level (developer "linting"). However, during "runtime", JavaScript is the only language that is executed. Therefore, it is necessary to validate user input against schema validation in JavaScript.
In many cases there is a strong corelation between types and runtime schemas, and duplicating type declarations in both schema and types can be cumbersome.
Zod is a TypeScript-first schema declaration and validation library.
The goal is to eliminate duplicate type declarations. With Zod, you declare a validator once and Zod will automatically infer the static TypeScript type. It also allows for easy composition of simpler types into complex data structures.
If you wish to use Zod with Fastify app, you can use fastify-type-provider-zod library.
You can do this tutorial which is a set of zod exercises for you to work through.
TypeBox is a JSON Schema Type Builder with Static Type Resolution for TypeScript.
With typebox you define your schema within your code and use them directly as types or schemas as you need them (like zod).
TypeBox is great for working with Fastify in TS because the validation in Fastify uses JSON Schema. Moreover, defining schemas for your Fastify can increase their throughput. To use it in your Fastify you will need fastify type-provider.
For more information you can read in the Fastify typebox doc.
- Generics
- Literal Types and
as const - Indexed Access Types (such as
T["key"],T[string]etc.) - Index Signatures (such as
[index: string]: number) -
extendskeyword
-
Fix the code so it will Error only in
errVeryStrconst echo = (arg) => arg; const greet = echo("Hello From Space"); const greetArr = echo(greet.split("")); // @ts-expect-error const errVeryGreet = greet.map((ch) => ch + "!"); const veryGreet = greetArr.map((ch) => ch + "!").join(""); console.log(veryGreet);
-
Fix the code so
addFullNamefunction will get any object that satisfiesUserShape:type UserShape = { firstName: string; lastName: string; }; const addFullName = (user: UserShape) => { return { ...user, fullName: `${user.firstName} ${user.lastName}`, }; }; const actorUser = { firstName: "John", lastName: "Malkovich", hobbies: ["acting", "directing"], }; const soldierUser = { firstName: "John", lastName: "Rambo", rank: "Sergeant", }; const notUser = { first_name: "I am not a user", lastName: "I told you I am not a user", }; const actor = addFullName(actorUser); const soldier = addFullName(soldierUser); // @ts-expect-error const doNot = addFullName(notUser); console.log(actor.hobbies); // Fix the ts error console.log(soldier.rank); // Fix the ts error // @ts-expect-error console.log(actor.rank);
-
What is the problem with this code? Can you fix it?
const satellitesCompanies = { skysat: "Planet", worldview: "Maxar", sentinel: "Sentinel", }; type SatellitesCompanies = typeof satellitesCompanies; type SkysatCompany = SatellitesCompanies["skysat"]; type WorldviewCompany = SatellitesCompanies["worldview"]; type SentinelCompany = SatellitesCompanies["sentinel"]; declare function freeOrder(company: SentinelCompany): void; declare function payedOrder(company: SkysatCompany | WorldviewCompany): void; freeOrder("Sentinel"); // @ts-expect-error freeOrder("Planet"); // @ts-expect-error payedOrder("Sentinel");
-
Change the
SatelliteNametype so it will error on the last line:const satellitesNames = [ "skysat", "skywalker", "skyscraper", "skyrim", "skype", ]; type SatelliteName = string; declare function isSatelliteGood(satName: SatelliteName): boolean; console.log(isSatelliteGood("skywalker")); // @ts-expect-error console.log(isSatelliteGood("skySat"));
-
Change the
SatellitesCompaniesNamestype so it will error on the last line:const satellitesCompanies = { skysat: "Planet", worldview: "Maxar", sentinel: "Sentinel", }; type SatellitesCompaniesNames = string; declare function isCompanyGood(company: SatellitesCompaniesNames): boolean; console.log(isCompanyGood("Planet")); // @ts-expect-error console.log(isCompanyGood("planet"));
Note: This section is advanced, you can ask your mentor what to learn from this section.
- Enums
- type predicates (
is) - Recursive Types
- Discriminated unions
- Exhaustiveness checking
- Function Overloads
-
inferkeyword -
satisfiesoperator - Conditional Types
- Distributive Conditional Types (
T extends Tvs[T] extends [T]) - Mapped Types (
in) - Template Literal Types
-
usingoperator - Decorators
-
Fix this code so it will error
type Route = { path: string; children?: Routes }; type Routes = Record<string, Route>; const routes: Routes = { AUTH: { path: "/auth", children: { LOGIN: { path: "/login", }, }, }, HOME: { path: "/", }, // @ts-expect-error mistake: true, }; // @ts-expect-error const { path } = routes.NotExist;
-
-
Create a type "function" which generate tuple types. For Example:
type TupleStr = Tuple<string, 3>; // TupleStr = [string, string, string] type Tuple2 = Tuple<number, 2>; // Tuple2 = [number, number]
-
Fix the type of this group function:
/** * Groups elements of an array into subarrays of a specified size. * If the array length is not a multiple of size, the array is truncated to fit. */ const group = <T>(arr: T[], size = 2) => Array.from({ length: arr.length / size }, (_, i) => arr.slice(i * size, i * size + size) ); const arr = [1, 2, 3, 4, 5, 6]; const res = group(arr); // Fix it to be [number, number][] const res2 = group(arr, 3); // Fix it to be [number, number, number][]
-
Fix the Tuple implementation so it will work with distribute over union types:
const num = Math.random() > 0.5 ? 2 : 3; const res3 = group(arr, num); // const res3: ([number, number] | [number, number, number])[]
-
-
Fix the types of this
curryhelper function:type Fn = (...args: any[]) => any; const curry = (fn: Fn) => { const curried = (...args: unknown[]) => args.length >= fn.length ? fn(...args) : (...args2: unknown[]) => curried(...args.concat(args2)); return curried; }; const sum = curry((a: number, b: number) => a + b); const add2 = sum(2); // any. Fix it! const test = curry((a: string, b: number, c: boolean) => true); // (...args: unknown[]) => any. Fix it! const trueFn = curry(() => true); // Fix it!
Talk with your mentor about the project.
These concepts are worth mentioning but don't learn them now.
- Declaration Files
- Mixins
- Module Resolution
- tsx
- tsconfig-paths
- pretty-ts-errors
- ts-reset
- tsup - Bundle your TypeScript library with no config, powered by esbuild.
- papr - lightweight library built around the MongoDB NodeJS driver, written in TypeScript