TypeScript Operators
Optional Chaining Operator (?.)
// Without optional chaining
const streetName = user && user.address && user.address.street;
// With optional chaining
const streetName = user?.address?.street;
// With method calls
const result = obj.method?.();
// With array elements
const item = arr?.[0];
Nullish Coalescing Operator (??)
// Returns right-hand value only if left-hand is null or undefined
const value = someValue ?? defaultValue;
// Different from OR operator
const count = 0 || 5; // Returns 5
const count = 0 ?? 5; // Returns 0
// Combining with optional chaining
const value = obj?.prop ?? defaultValue;
Non-null Assertion Operator (!)
// Tells TypeScript that a value cannot be null/undefined
function process(value: string | null) {
// TypeScript knows value! is string
const length = value!.length;
}
// Common with DOM elements
const element = document.querySelector('.my-class')!;
// With class properties
class User {
name!: string; // Will be initialized after constructor
}
Satisfies Operator
// Ensures type safety while preserving literal types
type RGB = [red: number, green: number, blue: number];
type Color = RGB | string;
// Without satisfies
const palette = {
red: [255, 0, 0],
blue: '#0000FF'
} as const;
// Type is lost for specific arrays/strings
// With satisfies
const palette2 = {
red: [255, 0, 0],
blue: '#0000FF'
} satisfies Record<string, Color>;
// Type information is preserved
Type Operators
// keyof operator
interface User {
name: string;
age: number;
}
type UserKeys = keyof User; // "name" | "age"
// typeof operator
const user = { name: "John", age: 30 };
type UserType = typeof user; // { name: string; age: number; }
// instanceof operator with type guards
class Dog {
bark() {}
}
class Cat {
meow() {}
}
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark();
}
}
Template Literal Types
// Basic template literal type
type Greeting = `Hello ${string}`;
let greeting: Greeting = 'Hello World'; // OK
let invalid: Greeting = 'Hi World'; // Error
// With unions
type Color = 'red' | 'blue';
type Size = 'small' | 'large';
type Product = `${Size}-${Color}`; // "small-red" | "small-blue" | "large-red" | "large-blue"
// With template literal modification
type GetterName<T extends string> = `get${Capitalize<T>}`;
type SetterName<T extends string> = `set${Capitalize<T>}`;
Index Operators
// Index access types
type Person = { age: number; name: string; alive: boolean };
type Age = Person['age']; // number
// Accessing multiple properties
type NameAndAge = Person['name' | 'age']; // string | number
// With arrays
const MyArray = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
];
type Person = typeof MyArray[number]; // { name: string; age: number }
Logical Operators in Types
// Conditional types with logical operators
type IsString<T> = T extends string ? true : false;
// And condition
type And<A, B> = A extends true
? B extends true
? true
: false
: false;
// Or condition
type Or<A, B> = A extends true
? true
: B extends true
? true
: false;
// Not condition
type Not<T> = T extends true ? false : true;
in Operator
// Type narrowing with 'in'
interface Admin {
id: number;
role: string;
}
interface User {
id: number;
email: string;
}
function redirect(user: Admin | User) {
if ('role' in user) {
// TypeScript knows this is Admin
console.log(user.role);
} else {
// TypeScript knows this is User
console.log(user.email);
}
}
These operators make TypeScript more expressive and help in writing safer, more concise code. The operators provide ways to:
- Handle nullable values safely (?., ??, !)
- Ensure type safety while preserving literal types (satisfies)
- Create complex type relationships (template literals, index operators)
- Narrow types in control flow (instanceof, in)