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
// 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
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
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.
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.
// 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 }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.
// 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
// 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 emailtypeof with Enum-Like Patterns
// 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.
// 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>;
// DatabaseConnectiontypeof in Narrowing (Runtime)
The runtime typeof operator narrows types in conditional branches. TypeScript understands these checks and adjusts the type of the variable accordingly.
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.
// ❌ 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
// 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
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.