TypeScripttypeof Type Operator

The typeof Type Operator

TypeScript has two typeof operators. The runtime typeof (familiar from JavaScript) checks the type of a value at runtime. The type-level typeof (used in type positions) extracts the static type of a variable or property and lets you reuse it without writing the type twice. This page covers the type-level operator.

The Two typeof Operators

TS
// 1. Runtime typeof (JavaScript)
const x = 42;
console.log(typeof x); // "number" — a runtime string

// 2. Type-level typeof (TypeScript)
type XType = typeof x; // number — a compile-time type

// They look the same but live in different worlds:
// - Runtime typeof is evaluated by the JavaScript engine
// - Type-level typeof is erased by TypeScript and never runs
Note
The context determines which typeof is used: inside a type annotation or type alias it is the type-level operator; anywhere else it is the runtime JavaScript operator.
Basic Usage

TS
const user = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  role: 'admin' as const,
};

// Extract the type of the variable
type User = typeof user;
// {
//   id: number;
//   name: string;
//   email: string;
//   role: 'admin';
// }

function greet(u: User): string {
  return `Hello, ${u.name}!`;
}

// No need to define a separate interface — the type comes from the value
greet(user);           // ✅
greet({ id: 2, name: 'Bob', email: 'bob@example.com', role: 'admin' }); // ✅
typeof with Functions

typeof is very useful with functions — you can capture the full function signature (parameters and return type) and use it as a type elsewhere.

TS
function fetchUser(id: number, options?: { timeout: number }): Promise<User> {
  return Promise.resolve({ id, name: 'Alice', email: 'alice@example.com', role: 'admin' as const });
}

// Extract the type of the function
type FetchUserFn = typeof fetchUser;
// (id: number, options?: { timeout: number }) => Promise<User>

// Use it to type a mock or alternative implementation
const mockFetchUser: FetchUserFn = async (id) => ({
  id,
  name: 'Mock User',
  email: 'mock@example.com',
  role: 'admin',
});

// Extract just the return type using ReturnType<>
type FetchUserResult = ReturnType<typeof fetchUser>; // Promise<User>
type UserResolved    = Awaited<ReturnType<typeof fetchUser>>; // User

// Extract parameter types
type FetchUserParams = Parameters<typeof fetchUser>;
// [id: number, options?: { timeout: number }]
typeof with const Assertions

Combining typeof with as const is extremely powerful. Without as const, TypeScript widens literal types to their base types (e.g. "GET" widens to string). With as const, you preserve the exact literal values — and typeof then captures them precisely.

TS
// Without as const — values are widened
const httpMethods = ['GET', 'POST', 'PUT', 'DELETE'];
type HttpMethod = typeof httpMethods; // string[] — too broad

// With as const — values are narrowed to literals
const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE'] as const;
type HttpMethod2 = typeof HTTP_METHODS;         // readonly ["GET", "POST", "PUT", "DELETE"]
type HttpMethodStr = typeof HTTP_METHODS[number]; // "GET" | "POST" | "PUT" | "DELETE"

function request(url: string, method: HttpMethodStr): void {
  console.log(`${method} ${url}`);
}

request('/api/users', 'GET');   // ✅
// request('/api/users', 'PATCH'); // ❌

// Object const assertion
const CONFIG = {
  baseUrl: 'https://api.example.com',
  version: 'v2',
  timeout: 5000,
} as const;

type Config = typeof CONFIG;
// { readonly baseUrl: 'https://api.example.com'; readonly version: 'v2'; readonly timeout: 5000 }
Tip
The as const + typeof combo is the idiomatic TypeScript way to derive string union types from arrays. It replaces the common anti-pattern of duplicating values in both an array and a separate union type.
typeof for Deriving Types from Existing Values

When you already have a JavaScript value (a config object, an enum-like constant, a class instance), use typeof to derive the type instead of writing a parallel interface. This keeps things DRY.

TS
// Application configuration
const defaultConfig = {
  theme: 'light' as 'light' | 'dark',
  language: 'en',
  notifications: true,
  pageSize: 20,
  features: {
    darkMode: false,
    betaFeatures: false,
  },
};

// Type is derived — no separate interface needed
type AppConfig = typeof defaultConfig;

function applyConfig(config: Partial<AppConfig>): AppConfig {
  return { ...defaultConfig, ...config };
}

const myConfig = applyConfig({ theme: 'dark', pageSize: 50 });
// myConfig.theme — 'light' | 'dark' (not just 'dark')

// Derive from a class instance
class UserService {
  getUser(id: number): Promise<User> { return Promise.resolve({} as User); }
  createUser(data: Omit<User, 'id'>): Promise<User> { return Promise.resolve({} as User); }
  deleteUser(id: number): Promise<void> { return Promise.resolve(); }
}

type IUserService = typeof UserService.prototype;
// { getUser: (id: number) => Promise<User>; createUser: ...; deleteUser: ... }

interface User { id: number; name: string; email: string; role: 'admin' | 'user' }
typeof in Generic Constraints

TS
// Ensure a value has the same shape as a reference object
function assertShape<T>(reference: T, value: typeof reference): void {
  const refKeys = Object.keys(reference as object);
  const valKeys = Object.keys(value as object);
  const missing = refKeys.filter(k => !valKeys.includes(k));
  if (missing.length) {
    throw new Error(`Missing keys: ${missing.join(', ')}`);
  }
}

const schema = { id: 0, name: '', email: '' };
assertShape(schema, { id: 1, name: 'Alice', email: 'alice@example.com' }); // ✅
// assertShape(schema, { id: 1, name: 'Alice' }); // ❌ missing email
typeof with Enum-Like Patterns

TS
// Prefer const objects over enums for better type inference
const Status = {
  PENDING:   'PENDING',
  ACTIVE:    'ACTIVE',
  SUSPENDED: 'SUSPENDED',
  DELETED:   'DELETED',
} as const;

type StatusKey   = keyof typeof Status;   // "PENDING" | "ACTIVE" | "SUSPENDED" | "DELETED"
type StatusValue = typeof Status[StatusKey]; // "PENDING" | "ACTIVE" | "SUSPENDED" | "DELETED"
// (symmetric in this case since keys and values match)

function isValidStatus(s: string): s is StatusValue {
  return Object.values(Status).includes(s as StatusValue);
}

function setUserStatus(userId: number, status: StatusValue): void {
  console.log(`User ${userId} status: ${status}`);
}

setUserStatus(1, Status.ACTIVE);    // ✅
setUserStatus(2, 'ACTIVE');         // ✅ — literal string works too
// setUserStatus(3, 'ARCHIVED');    // ❌
Practical: ReturnType and Parameters

Two of the most useful built-in utility types — ReturnType and Parameters — rely on typeof at their call sites.

TS
// Extracting return and parameter types from existing functions
function createUser(name: string, email: string, age: number) {
  return { id: Math.random(), name, email, age, createdAt: new Date() };
}

// Infer the created user shape without writing it manually
type CreatedUser = ReturnType<typeof createUser>;
// { id: number; name: string; email: string; age: number; createdAt: Date }

type CreateUserParams = Parameters<typeof createUser>;
// [name: string, email: string, age: number]

// Use it to type a mock
function mockCreateUser(...args: CreateUserParams): CreatedUser {
  return {
    id: 999,
    name: args[0],
    email: args[1],
    age: args[2],
    createdAt: new Date(0),
  };
}

// Infer constructor parameter types
class DatabaseConnection {
  constructor(
    public host: string,
    public port: number,
    public database: string
  ) {}
}

type DBConstructorParams = ConstructorParameters<typeof DatabaseConnection>;
// [host: string, port: number, database: string]

type DBInstance = InstanceType<typeof DatabaseConnection>;
// DatabaseConnection
typeof in Narrowing (Runtime)

The runtime typeof operator narrows types in conditional branches. TypeScript understands these checks and adjusts the type of the variable accordingly.

TS
function process(input: string | number | boolean | null): string {
  if (typeof input === 'string') {
    return input.toUpperCase(); // input is string here
  }
  if (typeof input === 'number') {
    return input.toFixed(2);   // input is number here
  }
  if (typeof input === 'boolean') {
    return input ? 'yes' : 'no'; // input is boolean here
  }
  return 'null'; // input is null here
}

// Checking for functions
function maybeCall(value: unknown): void {
  if (typeof value === 'function') {
    value(); // value is narrowed to Function
  }
}

// Discriminating between primitive and object
function describe(value: unknown): string {
  if (value === null)               return 'null';
  if (typeof value === 'object')    return `object with keys: ${Object.keys(value).join(', ')}`;
  if (typeof value === 'function')  return `function: ${value.name}`;
  return `${typeof value}: ${value}`;
}
Common Combinations

Expression

Produces

Use case

typeof value

Type of a variable

DRY type reuse

keyof typeof obj

Union of object keys

Safe key access on runtime objects

ReturnType<typeof fn>

Return type of a function

Derive types from existing functions

Parameters<typeof fn>

Tuple of parameter types

Mock functions, argument forwarding

InstanceType<typeof Cls>

Instance type of a class

Factory functions, DI containers

Awaited<ReturnType<typeof fn>>

Resolved promise type

Async function return types

Using typeof to Avoid Interface Duplication

A very common smell in TypeScript codebases is defining both a value (object, function, class) and a separately written interface that mirrors it. typeof eliminates this duplication.

TS
// ❌ Duplication anti-pattern
interface ThemeConfig {
  colors: { primary: string; secondary: string; background: string };
  spacing: { small: number; medium: number; large: number };
  fontFamily: string;
}

const theme: ThemeConfig = {
  colors:  { primary: '#3b82f6', secondary: '#10b981', background: '#ffffff' },
  spacing: { small: 4, medium: 8, large: 16 },
  fontFamily: 'Inter, sans-serif',
};

// ✅ DRY: derive the interface from the value
const themeValue = {
  colors:  { primary: '#3b82f6', secondary: '#10b981', background: '#ffffff' },
  spacing: { small: 4, medium: 8, large: 16 },
  fontFamily: 'Inter, sans-serif',
} as const;

type Theme = typeof themeValue;
// Exact literal types preserved because of as const

// If you want to allow mutability, omit as const
const defaultTheme = {
  colors: { primary: '#3b82f6', secondary: '#10b981', background: '#ffffff' },
  fontFamily: 'Inter, sans-serif',
};

type MutableTheme = typeof defaultTheme;
// { colors: { primary: string; ... }; fontFamily: string }
typeof in Test Utilities

TS
// Spy/mock typing with typeof
function createMock<T extends object>(target: T): jest.Mocked<T>;
function createMock<T extends object>(target: T): T {
  return target; // simplified
}

class UserService {
  async getUser(id: number): Promise<{ id: number; name: string }> {
    return { id, name: 'Alice' };
  }
  async updateUser(id: number, data: Partial<{ name: string }>): Promise<void> {}
}

// The mock has the same type as UserService
type MockUserService = typeof UserService.prototype;

// Extract method return types for test assertions
type GetUserResult = Awaited<ReturnType<typeof UserService.prototype.getUser>>;
// { id: number; name: string }

// Type-safe test setup
function setupTest(): {
  service: UserService;
  mockGet: typeof UserService.prototype.getUser;
} {
  const service = new UserService();
  return { service, mockGet: service.getUser.bind(service) };
}
Pitfalls
  • typeof only works on identifiers and property accesses — not arbitrary expressions

  • typeof someArray gives 'T[]' which may be wider than you expect — use 'as const' to narrow

  • The type-level typeof cannot be used in runtime code — it is erased by the compiler

  • Using typeof on a class gives the constructor type, not the instance type — use InstanceType<> to get the instance

  • typeof on a function overload captures the last overload signature — use explicit type aliases for overloaded functions

Quick Reference
  • typeof variable — extract the type of any variable, property, or expression

  • keyof typeof obj — get keys of a runtime object as a literal union type

  • typeof arr[number] — get element type of a const array (for string unions)

  • ReturnType<typeof fn> — return type of a function

  • Parameters<typeof fn> — parameter tuple of a function

  • InstanceType<typeof Cls> — instance type of a class constructor

  • Awaited<ReturnType<typeof asyncFn>> — the resolved type of an async function

Success
You now understand both the type-level and runtime typeof operators and how to combine them with keyof, ReturnType, and Parameters. Next: indexed access types — drilling into nested type structures with the T[K] syntax.