npm i -g typescript // global installation
or
npm i -D typescript
tsc --init
or
npx tsc --init // if typscript is installed locally
Example entries:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src"],
"exclude": ["node_modules"]
}
tsc --watch
or
npx tsc --watch
Without tsconfig.json
tsc src/index-ts --watch // file to compile
Normal setup with outDir: ./dist and rootDir: ./src
npm install -D typescript // Install typescript
npx tsc --init // Generate a tsconfig.json file
npm install -D @types/node // Install type definitions for node
Two terminals are needed
package.json
"scripts": {
"build": "tsc",
"watch": "tsc --watch",
"start": "node dist/main.js",
"dev": "nodemon --watch dist --exec 'node dist/main.js'"
}
To start, in terminal 1: npm run watch, in terminal 2: npm run dev
Only one terminal needed
npm install -D concurrently
package.json
"scripts": {
"build": "tsc",
"watch": "tsc --watch",
"start": "node dist/main.js",
"dev": "concurrently 'npm:watch' 'nodemon dist/main.js'"
}
Start with npm run dev
Runs typescript without compilation (it’s a newer alternative for ts-node, which has issues with node v20+)
npm install -D tsx
Create a tsconfig.json
npx tsc --init
package.json
"scripts": {
"build": "tsc",
"start": "node dist/main.js",
"dev": "tsc --noEmit && tsx ./main.ts"
}
Start with npm run dev
Explicit set data types if they can’t be inferred by TypeScript
const age: number;
const name: string;
const isAdmin: boolean;
const something: unknown; // will be defined later
const myArray: string[];
const myObj: {
name: string,
age: number,
phone?: string // "?" marks as not required
}
const myVar: any; // not recommended, allows any type
// union types
const age: string | number
const myArray: (string | number)[]
// non-null assertion operator (!)
// Tell the compiler the variable is not null or undefined even if typescript cannot infer from the code
const id!: number;
// Function with no return or undefined return
function logName(name:string):void {
console.log(name);
}
// Function with no return not even undefined
function logName(name:string):never {
throw new Error(`Function without a return`);
}
// Function with optional parameter
function logName(num1:number, num2?:number){ // return value is inferred
return num1;
}
// object
type userType = {
name: string,
age: number,
phone?: string,
theme: "dark" | "light"
}
myFunction(user:userType){
console.log(user.name);
}
// function
type myFunc = (a:number, b:string) => void;
const printOut: myFunc = (num, str)=>{
console.log(num + "times" + str);
}
Similar to types but can be extended
interface User {
name: string,
email: string,
age: number
}
interface Employee extends User {
employeeId: string
}
const employee: Employee = {
name: "John",
email: "john@work.com",
age: 50,
employeeId: "sskl1000"
}
Data changing the type based on what is returned
// Name inside of "<>" is random but often as "T"
function getFirstElement<T>(array: <T>[]){
return array[0];
}
// or as an arrow function
const getFirstElement = <T>(array:<T>[]) => array[0]
const firstNum = getFirstElement([1, 2, 3]);
const firstStr = getFirstElement(["a", "b", "c"]);
type ApiResponse<Data> = {
data: <Data>,
isError: boolean
}
const response: ApiResponse<{name: string, age: number}> = {
data: {
name: "John",
age: 40
},
isError: false
}
Record<K, V> is a more generic type that allows to dynamically define the structure of an object without explicitly listing all of the keys
K: The type of the keys of the object (often a string or number).V: The type of the values associated with those keys.Record can be used when a mapping object is created where the keys are of a certain type and the values are of a uniform type, but you don’t know or want to list all the keys in advance
const users: Record<string, { name: string; age: number }> = {
"user1": { name: "Alice", age: 25 },
"user2": { name: "Bob", age: 30 }
};
// Possible use case: Mapping User IDs to User Objects
interface User {
id: string;
name: string;
email: string;
}
const users: Record<string, User> = {
"user1": { id: "user1", name: "Alice", email: "alice@example.com" },
"user2": { id: "user2", name: "Bob", email: "bob@example.com" },
// More users can be added dynamically
};
Built-in types have a types or typings filed in in the package.json, pointing to a .d.ts file
"types": "index.d.ts"
npm install @types/express
Typical warning message: Could not find a declaration file for module ‘some-new-library’.
Solution: declare the module as any in a declaration file (e.g., global.d.ts):
declare module 'some-new-library';
Put types in a separate folder: src/types/types.ts and import them where needed
export type PostProps = {
id: number,
title: string,
body: string
}
// Props
const PostCard = ({title, body}:PostProps) => {...}
// Map
const data: PostProps = await getData();
{data.map((post)=>(
<PostCard key={post.id} {...post}/>
))}
Children Props
const Parent = ({children}:{children:React.ReactNode}) => {...}
Click Events (hover over “click” in event to see the type)
const handleClick = (event:React.MouseEvent<HTMLButtonElement>)=>{}
Example for event with target.id
// Here HTMLHeadingElement is used because the onClick is on a header (h2)
function handleClick(evt: React.MouseEvent<HTMLHeadingElement>){
const target = evt.target as HTMLHeadingElement; /use type assertion
console.log(target.id);
}
<h2 onClick={handleClick}>Title</h2>
Change Events (hover over “change” in event to see the type)
const handleChange = (event:React.ChangeEvent<HTMLInputElement>)=>{}
useState
type UserType = {sessionId:string, name:string);
const [user, setUser] = useState<UserType | null >(null);
Dependencies/Type Definitions:
npm install -D typescript @types/node @types/express @types/mongoose ts-node-dev
ts-node-dev: Automatically restarts the server on changes while compiling TypeScript.