TypeScript Basics
Type Declarations
Variable Type Declarations:
// Explicit type declarations
let name: string = 'Alice'
let age: number = 30
let isActive: boolean = true
// Array types
let numbers: number[] = [1, 2, 3]
let names: Array<string> = ['Alice', 'Bob'] // Generic syntax
// Tuple type
let user: [string, number] = ['Alice', 30]
// Enum type
enum Color {
Red = 'RED',
Green = 'GREEN',
Blue = 'BLUE'
}
let favoriteColor: Color = Color.Red
Function Type Declarations:
// Parameter and return type
function greet(name: string): string {
return `Hello, ${name}`
}
// Optional parameter
function greet(name: string, title?: string): string {
return title ? `Hello, ${title} ${name}` : `Hello, ${name}`
}
// Default parameter
function greet(name: string, title: string = 'Mr.'): string {
return `Hello, ${title} ${name}`
}
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((acc, curr) => acc + curr, 0)
}
Type Guards
Type Guards:
// typeof type guard
function printValue(value: string | number) {
if (typeof value === 'string') {
console.log(value.toUpperCase()) // value is string here
} else {
console.log(value.toFixed(2)) // value is number here
}
}
// instanceof type guard
class Animal {}
class Dog extends Animal {}
function handleAnimal(animal: Animal) {
if (animal instanceof Dog) {
console.log('It\'s a dog!')
} else {
console.log('It\'s some other animal')
}
}
// Custom type guard
interface Bird {
fly(): void
}
interface Fish {
swim(): void
}
function isBird(pet: Bird | Fish): pet is Bird {
return 'fly' in pet
}
function movePet(pet: Bird | Fish) {
if (isBird(pet)) {
pet.fly()
} else {
pet.swim()
}
}
TS Compilation
tsconfig.json Configuration:
{
"compilerOptions": {
"target": "ES2020", // Compilation target version
"module": "CommonJS", // Module system
"outDir": "./dist", // Output directory
"rootDir": "./src", // Source code directory
"strict": true, // Enable all strict type checking
"esModuleInterop": true, // Compatibility with CommonJS/ES modules
"skipLibCheck": true, // Skip type checking for library files
"forceConsistentCasingInFileNames": true // Enforce consistent file name casing
},
"include": ["src/**/*"], // Files to include
"exclude": ["node_modules"] // Files to exclude
}
Compilation Commands:
# Compile entire project
tsc
# Watch mode (auto-recompile)
tsc --watch
# Compile specific file
tsc src/index.ts
# Generate declaration files (.d.ts)
tsc --declaration
Values and Types
Value Types
Basic Types:
// Primitive types
let isDone: boolean = false
let decimal: number = 6
let hex: number = 0xf00d
let binary: number = 0b1010
let octal: number = 0o744
let color: string = "blue"
let sentence: string = `Hello, ${color}`
// Special types
let u: undefined = undefined
let n: null = null
let big: bigint = 100n
let sym: symbol = Symbol('key')
Object Types:
// Object type
let user: { name: string; age: number } = {
name: 'Alice',
age: 30
}
// Interface defining object type
interface Person {
name: string
age: number
readonly id: number // Read-only property
}
let person: Person = {
name: 'Bob',
age: 25,
id: 12345
}
// person.id = 67890 // Error: Cannot assign to 'id' because it is a read-only property
Union Types
Using Union Types:
// String or number
let id: string | number
id = 'abc123'
id = 12345
// Function parameter union type
function printId(id: number | string) {
if (typeof id === 'string') {
console.log(id.toUpperCase())
} else {
console.log(id.toFixed(2))
}
}
// Union type array
let arr: (number | string)[]
arr = [1, 'two', 3, 'four']
// Class type union
class Dog {
bark() {
console.log('Woof!')
}
}
class Cat {
meow() {
console.log('Meow!')
}
}
let pet: Dog | Cat
pet = new Dog()
pet.bark() // Works
// pet.meow() // Error: Property 'meow' does not exist on type 'Dog'
if ('meow' in pet) {
(pet as Cat).meow() // Type assertion
}
Intersection Types
Merging Intersection Types:
// Base interfaces
interface Person {
name: string
age: number
}
interface Employee {
employeeId: number
department: string
}
// Intersection type
type EmployeePerson = Person & Employee
let employee: EmployeePerson = {
name: 'Alice',
age: 30,
employeeId: 12345,
department: 'Engineering'
}
// Function returning intersection type
function createEmployee(name: string, age: number, employeeId: number, department: string): Person & Employee {
return {
name,
age,
employeeId,
department
}
}
Type System
Primitive Types and Wrapper Objects
Primitive Types vs Wrapper Objects:
// Primitive types
let num: number = 123
let str: string = 'hello'
let bool: boolean = true
// Wrapper objects (not recommended in TS)
let numObj: Number = new Number(123) // Not recommended
let strObj: String = new String('hello') // Not recommended
let boolObj: Boolean = new Boolean(true) // Not recommended
// Type conversion
let numFromString: number = Number('123') // Recommended
let strFromNum: string = String(123) // Recommended
let boolFromStr: boolean = Boolean('true') // Recommended
Object Type vs object Type
Object vs object:
// Object type (parent type of all objects)
let obj1: Object = { name: 'Alice' }
obj1 = new Date() // Can assign any object
obj1 = [1, 2, 3] // Can assign arrays
// object type (non-primitive types)
let obj2: object = { name: 'Bob' }
// obj2 = 123 // Error: Type 'number' is not assignable to type 'object'
// obj2 = 'hello' // Error: Type 'string' is not assignable to type 'object'
obj2 = new Date() // Can assign any object
// Best practice: Use specific types instead of Object/object
interface User {
name: string
age: number
}
let user: User = { name: 'Charlie', age: 30 }
Value Types vs Reference Types
Differences Between Value and Reference Types:
// Value types (primitive types)
let a: number = 10
let b: number = a
b = 20
console.log(a) // Still 10
// Reference types (objects)
let objA: { value: number } = { value: 10 }
let objB: { value: number } = objA
objB.value = 20
console.log(objA.value) // Also 20
// Solving reference type sharing issues
let objC: { value: number } = { value: 10 }
let objD: { value: number } = { ...objC } // Shallow copy
objD.value = 20
console.log(objC.value) // Still 10
Union Types and Intersection Types
Advanced Union Type Usage:
// Function overloading (implemented with union types)
function makeDate(timestamp: number): Date
function makeDate(year: number, month: number, day: number): Date
function makeDate(overload1: number, overload2?: number, overload3?: number): Date {
if (overload2 !== undefined && overload3 !== undefined) {
return new Date(overload1, overload2, overload3)
} else {
return new Date(overload1)
}
}
// Type alias union
type ID = number | string
let userId: ID = 123
userId = 'abc123'
// Conditional types (based on union types)
type NonNullable<T> = T extends null | undefined ? never : T
type T1 = NonNullable<string | null | undefined> // string
Advanced Intersection Type Usage:
// Mixin types
type Serializable = { serialize(): string }
type Deserializable = { deserialize(data: string): void }
type SerializableAndDeserializable = Serializable & Deserializable
class User implements SerializableAndDeserializable {
constructor(public name: string, public age: number) {}
serialize(): string {
return JSON.stringify(this)
}
deserialize(data: string): void {
const parsed = JSON.parse(data)
this.name = parsed.name
this.age = parsed.age
}
}
// Utility type combination
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
interface User {
id: number
name: string
age: number
}
type PartialUser = PartialBy<User, 'id' | 'age'>
// Equivalent to { id?: number; name: string; age?: number }
typeof Operator
typeof Type Queries:
// JavaScript typeof
let num = 123
console.log(typeof num) // "number"
// TypeScript typeof type query
let person = { name: 'Alice', age: 30 }
type PersonType = typeof person // { name: string; age: number }
function greet(p: typeof person) {
console.log(`Hello, ${p.name}`)
}
// Combining with generics
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
}
let user = { name: 'Bob', age: 25 }
let userName = getProperty(user, 'name') // Type-safe
// let userAge = getProperty(user, 'gender') // Error: 'gender' is not a property of 'user'
type Command and Type Aliases
Using Type Aliases (type):
// Basic type aliases
type ID = number | string
type Point = {
x: number
y: number
}
// Function type alias
type GreetFunction = (name: string) => string
// Complex type alias
type Tree<T> = {
value: T
left?: Tree<T>
right?: Tree<T>
}
// Using type aliases
let userId: ID = 123
userId = 'abc123'
let point: Point = { x: 10, y: 20 }
let greet: GreetFunction = (name) => `Hello, ${name}`
let tree: Tree<number> = {
value: 1,
left: {
value: 2
},
right: {
value: 3
}
}
Block-Level Type Declarations
Block-Level Scope Types:
{
// Block-level type declaration
type LocalType = string
let localVar: LocalType = 'hello'
// This type is only valid in the current block
interface LocalInterface {
name: string
}
let localObj: LocalInterface = { name: 'Alice' }
}
// console.log(typeof LocalType) // Error: Cannot find name 'LocalType'
// let x: LocalType = 'world' // Error: Cannot find name 'LocalType'
Type Compatibility
Type Compatibility Rules:
// Structural type system (duck typing)
interface Person {
name: string
age: number
}
class Employee {
constructor(public name: string, public age: number) {}
}
let p: Person = { name: 'Alice', age: 30 }
p = new Employee('Bob', 25) // Can assign because structure matches
// Function compatibility
type Handler = (a: number, b: number) => void
let handler1: Handler = (a: number, b: number) => {}
let handler2: Handler = (a: number) => {} // Works, fewer parameters
// let handler3: Handler = (a: number, b: number, c: number) => {} // Error: Too many parameters
// Optional and rest parameters
let handler4: Handler = (a: number, b?: number) => {} // Works, b is optional
let handler5: Handler = (...args: number[]) => {} // Works, rest parameters
// Enum compatibility
enum Status { Ready, Waiting }
enum Color { Red, Blue, Green }
let s: Status = Status.Ready
// s = Color.Red // Error: Type 'Color' is not assignable to type 'Status'
// Class compatibility (only instance members are compared)
class Animal {
feet: number
constructor(name: string, numFeet: number) {}
}
class Size {
feet: number
constructor(numFeet: number) {}
}
let a: Animal = new Size(4) // Works, instance members match
// Note: Different constructor parameters do not affect compatibility
Advanced Type Compatibility:
// Generic compatibility
interface Empty<T> {}
let x: Empty<number>
let y: Empty<string>
x = y // Works, because type parameter T is not used
interface NotEmpty<T> {
data: T
}
let x1: NotEmpty<number>
let y1: NotEmpty<string>
// x1 = y1 // Error: Type 'NotEmpty<string>' is not assignable to type 'NotEmpty<number>'
// Function parameter bivariance
type EventHandler = (event: string | number) => void
let handler: EventHandler = (event: string) => {} // Works
let handler2: EventHandler = (event: number) => {} // Works
// Return type contravariance
type Creator<T> = () => T
let creator: Creator<string | number> = () => 'hello' // Works
let creator2: Creator<string | number> = () => 123 // Works
Basic Types and Type Annotations
Primitive Types
Primitive Type Declarations and Usage:
// String type
let name: string = 'Alice'
name = 'Bob' // Correct
// name = 123 // Error: Type 'number' is not assignable to type 'string'
// Number type
let age: number = 30
age = 30.5 // Correct (supports floating-point numbers)
// age = '30' // Error: Type 'string' is not assignable to type 'number'
// Boolean type
let isActive: boolean = true
isActive = false // Correct
// isActive = 'true' // Error: Type 'string' is not assignable to type 'boolean'
// Special primitive types
let nothing: null = null
let notDefined: undefined = undefined
let bigIntValue: bigint = 100n
let symbolValue: symbol = Symbol('unique')
// Usage example
function greet(name: string, age: number): string {
return `Hello ${name}, you are ${age} years old`
}
Object Types
Object Type Declaration Methods:
// Object type (parent type of all objects)
let obj1: Object = { name: 'Alice' }
obj1 = new Date() // Can assign any object
// obj1.name // Error: Property 'name' does not exist on type 'Object'
// {} type (empty object type)
let obj2: {} = {} // Can only assign empty object
// obj2 = { name: 'Bob' } // Error: Type '{ name: string; }' is not assignable to type '{}'
// Interface defining object type (recommended)
interface Person {
name: string
age: number
readonly id: number // Read-only property
}
let person: Person = {
name: 'Alice',
age: 30,
id: 12345
}
// person.id = 67890 // Error: Cannot assign to 'id' because it is a read-only property
// Class implementing interface
class Employee implements Person {
constructor(
public name: string,
public age: number,
public id: number
) {}
}
let employee: Person = new Employee('Bob', 25, 54321)
Arrays and Tuples
Array Type Declarations:
// Array types (two syntaxes)
let numbers: number[] = [1, 2, 3]
let strings: Array<string> = ['a', 'b', 'c'] // Generic syntax
// Mixed type array
let mixed: (number | string)[] = [1, 'two', 3, 'four']
// Tuple type (fixed-length and typed array)
let user: [string, number] = ['Alice', 30]
// user = [30, 'Alice'] // Error: Type mismatch
// user.push('admin') // Can add elements (but loses type safety)
// Tuple with optional elements
let userWithOptional: [string, number?, boolean?] = ['Alice']
userWithOptional = ['Bob', 25]
userWithOptional = ['Charlie', 30, true]
// Accessing tuple elements
const [userName, userAge] = user // Destructuring assignment
console.log(userName, userAge)
Enum Types
Enum Type Definition and Usage:
// Numeric enum (starts from 0 by default)
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
let dir: Direction = Direction.Up
console.log(dir) // 0
// String enum
enum LogLevel {
ERROR = 'ERROR',
WARN = 'WARN',
INFO = 'INFO',
DEBUG = 'DEBUG'
}
let logLevel: LogLevel = LogLevel.INFO
console.log(logLevel) // 'INFO'
// Heterogeneous enum (mixing numbers and strings)
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = 'YES'
}
// Const enum (inlined at compile time)
const enum Size {
Small = 1,
Medium = 2,
Large = 3
}
let size: Size = Size.Medium
// Compiled to: let size = 2;
// Enum practical tips
enum Status {
Pending = 'PENDING',
Approved = 'APPROVED',
Rejected = 'REJECTED'
}
function handleStatus(status: Status) {
switch (status) {
case Status.Pending:
console.log('Pending')
break
case Status.Approved:
console.log('Approved')
break
case Status.Rejected:
console.log('Rejected')
break
}
}
Type Inference and Type Compatibility
Type Inference Rules
Implicit Type Inference Examples:
// Variable initialization inference
let x = 10 // Inferred as number
x = 'hello' // Error: Type 'string' is not assignable to type 'number'
// Function return type inference
function add(a: number, b: number) {
return a + b // Inferred to return number
}
// Best common type inference
let arr = [0, 1, null] // Inferred as (number | null)[]
// Contextual type inference
window.onmousedown = function(mouseEvent) {
console.log(mouseEvent.button) // Correctly inferred as MouseEvent
}
Type Compatibility
Structural Type System Examples:
interface Named {
name: string
}
class Person {
constructor(public name: string) {}
}
let p: Named
p = new Person('Alice') // Can assign because structure matches
// Function compatibility
type Handler = (a: number, b: number) => void
let handler1: Handler = (a: number, b: number) => {}
let handler2: Handler = (a: number) => {} // Works, fewer parameters
// let handler3: Handler = (a: number, b: number, c: number) => {} // Error: Too many parameters
// Optional and rest parameters
let handler4: Handler = (a: number, b?: number) => {} // Works, b is optional
let handler5: Handler = (...args: number[]) => {} // Works, rest parameters
// Enum compatibility
enum Status { Ready, Waiting }
enum Color { Red, Blue, Green }
let s: Status = Status.Ready
// s = Color.Red // Error: Type 'Color' is not assignable to type 'Status'
Type Assertions
Type Assertion Syntax:
// Angle-bracket syntax (not recommended in .tsx files)
let someValue: any = 'this is a string'
let strLength: number = (<string>someValue).length
// as syntax (recommended)
let someValue: any = 'this is a string'
let strLength: number = (someValue as string).length
// Non-null assertion operator (!)
function liveDangerously(x?: number | null) {
console.log(x!.toFixed(2)) // Tells compiler x is not null/undefined
}
// Best practices for type assertions
interface Cat {
meow(): void
}
interface Dog {
bark(): void
}
function isCat(pet: Cat | Dog): pet is Cat {
return 'meow' in pet
}
function handlePet(pet: Cat | Dog) {
if (isCat(pet)) {
pet.meow() // Type-safe
} else {
pet.bark() // Type-safe
}
// Alternative: Type assertion (not recommended unless type is certain)
// (pet as Cat).meow() // Dangerous: Runtime error if pet is actually Dog
}
Type Guards
Custom Type Guard Implementation:
// typeof type guard
function printValue(value: string | number) {
if (typeof value === 'string') {
console.log(value.toUpperCase()) // value is string here
} else {
console.log(value.toFixed(2)) // value is number here
}
}
// instanceof type guard
class Animal {}
class Dog extends Animal {}
function handleAnimal(animal: Animal) {
if (animal instanceof Dog) {
console.log('It\'s a dog!')
} else {
console.log('It\'s some other animal')
}
}
// Custom type guard
interface Bird {
fly(): void
}
interface Fish {
swim(): void
}
function isBird(pet: Bird | Fish): pet is Bird {
return 'fly' in pet
}
function movePet(pet: Bird | Fish) {
if (isBird(pet)) {
pet.fly()
} else {
pet.swim()
}
}
// in operator type guard
type Square = { kind: 'square'; size: number }
type Rectangle = { kind: 'rectangle'; width: number; height: number }
function area(shape: Square | Rectangle): number {
if ('size' in shape) {
return shape.size * shape.size
} else {
return shape.width * shape.height
}
}
Index Types and Mapped Types Basics
Index Type Operations:
// Indexed access types
interface Person {
name: string
age: number
}
type NameType = Person['name'] // string
// Index signature
interface StringDictionary {
[key: string]: string
}
let dict: StringDictionary = {
name: 'Alice',
email: 'alice@example.com'
}
// dict.age = 30 // Error: Type 'number' is not assignable to type 'string'
// Mapped type basics
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
type Partial<T> = {
[P in keyof T]?: T[P]
}
interface User {
id: number
name: string
}
type ReadonlyUser = Readonly<User>
type PartialUser = Partial<User>
Functions and Classes
Function Type Annotations
Function Type Declarations:
// Basic function type
function greet(name: string): string {
return `Hello, ${name}`
}
// Function type variable
let greetFunc: (name: string) => string = greet
// Optional parameter
function greet(name: string, title?: string): string {
return title ? `Hello, ${title} ${name}` : `Hello, ${name}`
}
// Default parameter
function greet(name: string, title: string = 'Mr.'): string {
return `Hello, ${title} ${name}`
}
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((acc, curr) => acc + curr, 0)
}
// Function type alias
type MathOperation = (a: number, b: number) => number
const add: MathOperation = (x, y) => x + y
const subtract: MathOperation = (x, y) => x - y
Optional Parameters and Default Parameters
Parameter Handling Techniques:
// Combining optional and default parameters
function createUser(
name: string,
age?: number,
isAdmin: boolean = false
): { name: string; age?: number; isAdmin: boolean } {
return { name, age, isAdmin }
}
// Parameter destructuring with defaults
function drawChart({
size = 'big',
coords = { x: 0, y: 0 },
radius = 25
} = {}) {
console.log(size, coords, radius)
}
drawChart() // Uses all defaults
drawChart({ size: 'small' }) // Overrides size
// Rest parameters with defaults
function connect(options: {
timeout?: number
retries?: number
[key: string]: any
} = {}) {
// ...
}
// Function overloading (handling different parameter types)
function makeDate(timestamp: number): Date
function makeDate(year: number, month: number, day: number): Date
function makeDate(overload1: number, overload2?: number, overload3?: number): Date {
if (overload2 !== undefined && overload3 !== undefined) {
return new Date(overload1, overload2, overload3)
} else {
return new Date(overload1)
}
}
Rest Parameters and Spread Operator
Parameter Spread Operations:
// Rest parameter collection
function sum(...numbers: number[]): number {
return numbers.reduce((acc, curr) => acc + curr, 0)
}
// Spread array as parameters
let numbers = [1, 2, 3]
console.log(sum(...numbers)) // Equivalent to sum(1, 2, 3)
// Object spread
let defaults = { food: 'spicy', price: '$$', ambiance: 'noisy' }
let search = { ...defaults, food: 'rich' } // Overrides food property
// Function parameter spread
function logValues(a: number, b: number, c: number) {
console.log(a, b, c)
}
let nums = [1, 2, 3]
logValues(...nums) // Equivalent to logValues(1, 2, 3)
// Generic function with spread
function mergeObjects<T extends object, U extends object>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 }
}
let merged = mergeObjects({ name: 'Alice' }, { age: 30 })
Class Basics
Class Definition and Instantiation:
// Basic class definition
class Person {
// Constructor
constructor(public name: string, public age: number) {}
// Method
greet() {
return `Hello, my name is ${this.name}`
}
}
let person = new Person('Alice', 30)
console.log(person.greet())
// Inheritance
class Employee extends Person {
constructor(
name: string,
age: number,
public jobTitle: string
) {
super(name, age) // Call parent constructor
}
work() {
return `${this.name} is working as a ${this.jobTitle}`
}
}
let employee = new Employee('Bob', 25, 'Developer')
console.log(employee.work())
console.log(employee.greet()) // Inherited method
// Static members
class MathHelper {
static PI = 3.14159
static calculateCircleArea(radius: number): number {
return this.PI * radius * radius
}
}
console.log(MathHelper.calculateCircleArea(2))
Access Modifiers
Access Control Implementation:
// public (default)
class PublicExample {
public name: string // Explicit declaration
constructor(name: string) {
this.name = name // Accessible everywhere
}
}
// private (accessible only within class)
class PrivateExample {
private secret: string
constructor(secret: string) {
this.secret = secret
}
revealSecret() {
return this.secret // Accessible within class
}
}
let privateEx = new PrivateExample('my secret')
// privateEx.secret // Error: Property 'secret' is private
// protected (accessible within class and subclasses)
class ProtectedExample {
protected value: number
constructor(value: number) {
this.value = value
}
}
class ChildClass extends ProtectedExample {
showValue() {
return this.value // Can access protected member
}
}
let child = new ChildClass(42)
// child.value // Error: Property 'value' is protected
// readonly modifier
class ReadonlyExample {
readonly id: number
constructor(id: number) {
this.id = id
}
}
let example = new ReadonlyExample(123)
// example.id = 456 // Error: Cannot assign to 'id' because it is a read-only property
// Combining access modifiers
class BankAccount {
public accountNumber: string
private balance: number
protected owner: string
readonly accountType: string
constructor(
accountNumber: string,
initialBalance: number,
owner: string,
accountType: string
) {
this.accountNumber = accountNumber
this.balance = initialBalance
this.owner = owner
this.accountType = accountType
}
deposit(amount: number) {
if (amount > 0) {
this.balance += amount
}
}
getBalance() {
return this.balance
}
}
class SavingsAccount extends BankAccount {
calculateInterest() {
// Can access protected owner property
console.log(`Calculating interest for ${this.owner}`)
// Cannot access private balance property (must use getBalance)
return this.getBalance() * 0.05
}
}
Utilities and Compiler
ts-node Module
ts-node Usage Guide:
# Install ts-node
npm install -D ts-node
# Run TypeScript file directly
ts-node src/index.ts
# Use nodemon for auto-restart
npx nodemon --exec ts-node src/index.ts
# Compile and run (without generating files)
ts-node --transpile-only src/index.ts
# Use custom configuration file
ts-node -P tsconfig.dev.json src/index.ts
# Debugging support
node --inspect-brk -r ts-node/register src/index.ts
tsconfig.json Configuration Example:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"types": ["node"], // Specify global types
"baseUrl": ".", // Base directory for resolving non-relative module names
"paths": { // Module name to path mapping based on baseUrl
"@/*": ["src/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
Compiler Options Explained
Common Compiler Options:
{
"compilerOptions": {
// Target version
"target": "ES2020", // ES3, ES5, ES6/ES2015, ES2016, ES2017, ES2018, ES2019, ES2020, ESNext
// Module system
"module": "CommonJS", // CommonJS, UMD, AMD, System, ES6/ES2015, ES2020, ESNext, None
// Output directory
"outDir": "./dist", // Redirect output directory
// Source code directory
"rootDir": "./src", // Used to control output directory structure
// Strict type checking options
"strict": true, // Enable all strict type checking options
// Module resolution options
"moduleResolution": "node", // node, classic
// Type declaration files
"declaration": true, // Generate corresponding .d.ts files
// Source maps
"sourceMap": true, // Generate corresponding .map files
// Inline source maps
"inlineSourceMap": false, // Generate single sourcemaps file instead of separate ones
// Inline sources
"inlineSources": false, // Include source code with sourcemaps in a single file
// Experimental decorators
"experimentalDecorators": true, // Enable experimental ES decorators
// Emit decorator metadata
"emitDecoratorMetadata": true, // Add design type metadata to decorator declarations
// Other important options
"esModuleInterop": true, // Allow default imports from modules without default exports
"allowSyntheticDefaultImports": true, // Allow default imports from modules without default exports
"skipLibCheck": true, // Skip type checking of all declaration files (.d.ts)
"forceConsistentCasingInFileNames": true // Disallow inconsistent file name references
}
}
Best Practices
Project Structure Recommendations
Recommended Project Structure:
my-project/
├── src/ # Source code directory
│ ├── components/ # Reusable components
│ ├── services/ # Service layer
│ ├── utils/ # Utility functions
│ ├── models/ # Data models
│ ├── App.ts # Main application file
│ └── index.ts # Entry file
├── tests/ # Test files
├── dist/ # Compiled output directory (auto-generated)
├── node_modules/ # Dependencies (auto-generated)
├── package.json # Project configuration
├── tsconfig.json # TypeScript configuration
└── README.md # Project documentation
Type Safety Practices
Techniques for Enhancing Type Safety:
// 1. Use precise types instead of any
// Avoid:
function processData(data: any) { /* ... */ }
// Recommended:
interface Data {
id: number
name: string
value?: number
}
function processData(data: Data) { /* ... */ }
// 2. Use read-only types to prevent accidental modifications
interface Config {
apiUrl: string
timeout: number
}
const config: Readonly<Config> = {
apiUrl: 'https://api.example.com',
timeout: 5000
}
// config.apiUrl = 'new-url' // Error: Cannot assign to 'apiUrl' because it is a read-only property
// 3. Use type guards to narrow type ranges
function isString(value: unknown): value is string {
return typeof value === 'string'
}
function printLength(value: string | number) {
if (isString(value)) {
console.log(value.length) // value is string here
} else {
console.log(value.toFixed(2)) // value is number here
}
}
// 4. Use generics to increase code reusability
function identity<T>(arg: T): T {
return arg
}
let output1 = identity<string>('hello')
let output2 = identity<number>(123)
// 5. Use utility types to simplify complex type operations
interface User {
id: number
name: string
age: number
email: string
}
// Pick specific properties from User
type UserBasicInfo = Pick<User, 'id' | 'name'>
// Omit specific properties from User
type UserWithoutEmail = Omit<User, 'email'>
// Make some properties optional
type PartialUser = Partial<User>
// Make some properties required
type RequiredUser = Required<Pick<User, 'id' | 'name'>>
Performance Optimization Tips
TypeScript Performance Optimization:
- Avoid Overusing Types: Add type annotations only where necessary
- Use Interfaces Over Type Aliases: Interfaces are more flexible for merging
- Use Generics Judiciously: Avoid overly complex generic nesting
- Utilize Project References: Split large projects into sub-projects
- Enable Incremental Compilation:
"incremental": trueoption
Project References Example:
// tsconfig.json (root project)
{
"compilerOptions": {
"composite": true,
"declaration": true,
"declarationMap": true
},
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/utils" }
]
}
// packages/core/tsconfig.json
{
"compilerOptions": {
"composite": true,
"outDir": "../../dist/core"
},
"include": ["src"]
}
// packages/utils/tsconfig.json
{
"compilerOptions": {
"composite": true,
"outDir": "../../dist/utils"
},
"include": ["src"]
}



