How to Get a Variable's Type in TypeScript Using `typeof`

TypeScript's typeof operator functions as a compile-time type query that captures the static type of a variable, value, or class constructor, enabling type reuse without explicit manual definition.

In the microsoft/TypeScript codebase, the typeof operator is implemented as a type query mechanism distinct from JavaScript's runtime typeof expression. When used in a type position, it extracts static type information during compilation rather than emitting runtime checks.

Understanding the TypeScript typeof Type Query

The TypeScript typeof operator serves as a type query that evaluates exclusively at compile time. Unlike JavaScript's typeof which returns a string at runtime, TypeScript's version produces a type representation when placed in a type context, such as after the type keyword or within an interface.

The compiler resolves these queries in src/compiler/checker.ts, where the getTypeOfExpression function handles the resolution process. For identifiers, it delegates to getTypeOfVariable, while literal expressions route through getTypeOfLiteral.

Capturing Variable and Value Types

To extract the type of a variable or value, declare a type alias using typeof followed by the variable name.

const greeting = "hello";
type GreetingType = typeof greeting; // string

const config = {
  host: "localhost",
  port: 3000
};
type ConfigType = typeof config; // { host: string; port: number; }

The compiler infers the exact static type from the initialization value. For object literals, the checker constructs the type by analyzing property signatures stored in internal structures defined in src/compiler/types.ts.

Extracting Class Constructor Types with typeof

When applied to a class identifier, typeof captures the constructor function type rather than the instance type. This produces a type representing the new signature and static members.

class Point {
  constructor(public x: number, public y: number) {}
  static origin = new Point(0, 0);
}

type PointConstructor = typeof Point;
// new (x: number, y: number) => Point

This pattern is essential for factory functions and mixin patterns where you need to reference the class itself, not an instance of the class.

Deriving Object Key Unions Using keyof typeof

Combining keyof with typeof creates a union type of an object's keys. This technique requires the as const assertion for literal type inference or operates on the existing type of a const variable.

const colors = {
  red: "#ff0000",
  green: "#00ff00",
  blue: "#0000ff"
} as const;

type ColorName = keyof typeof colors; // "red" | "green" | "blue"
type ColorValue = typeof colors[keyof typeof colors]; // "#ff0000" | "#00ff00" | "#0000ff"

Under the hood, the compiler builds a MappedType (defined in src/compiler/types.ts) to represent the object shape before extracting keys, ensuring accurate results for keyof typeof operations.

Runtime Type Guards vs Compile-Time Queries

TypeScript typeof operates in two distinct contexts. In a type position, it performs compile-time type extraction. In an expression context, such as an if statement condition, it emits the standard JavaScript typeof operator for runtime type checking.

function isString(value: unknown): value is string {
  return typeof value === "string";
}

function processValue(value: string | number) {
  if (typeof value === "string") {
    // TypeScript narrows type to string here
    return value.toUpperCase();
  }
  return value.toFixed(2);
}

The compiler's control flow analysis recognizes these runtime checks to narrow types within conditional blocks, implemented through the same type-checking infrastructure in src/compiler/checker.ts.

Implementation Details in the TypeScript Compiler

The resolution of typeof type queries occurs primarily in src/compiler/checker.ts. Key functions include:

  • getTypeOfExpression: Entry point for resolving typeof queries in type positions.
  • getTypeOfVariable: Specialized handler for identifier lookups when querying variables.
  • getTypeOfLiteral: Processes literal values to determine their exact types.

For complex object literals, the system utilizes MappedType structures (declared in src/compiler/types.ts) to preserve property modifiers and optionality. Additional support utilities reside in src/compiler/utilities.ts, while public API surfaces export these capabilities through src/services/types.ts.

Summary

  • TypeScript typeof is a compile-time operator that extracts static types from values, distinct from JavaScript's runtime string-returning typeof.
  • Use typeof variable to capture value types and typeof ClassName to capture constructor function types.
  • Combine with keyof to generate unions of object keys: keyof typeof obj.
  • The compiler implements this in src/compiler/checker.ts via getTypeOfExpression and related functions.
  • In expression contexts, typeof emits standard JavaScript runtime checks that enable type narrowing.

Frequently Asked Questions

What is the difference between TypeScript typeof and JavaScript typeof?

TypeScript typeof operates at compile time in type positions to extract type information, producing a type representation rather than a runtime value. JavaScript typeof executes at runtime and returns a string describing the value's primitive type. When used inside expressions like if conditions, TypeScript emits JavaScript typeof while also using the result for type narrowing analysis.

Can TypeScript typeof extract types from class instances?

No, applying typeof to a class identifier captures the constructor type (the class itself), not the instance type. To get the instance type, use the InstanceType utility: type PointInstance = InstanceType<typeof Point>. Alternatively, typeof applied to an instance variable would capture that specific instance's type structure.

How does typeof interact with const assertions?

When a variable is declared with as const, typeof extracts the most specific literal types possible rather than widening to general primitives. For example, const x = "hello" as const followed by type T = typeof x yields the literal type "hello" instead of string. This enables precise type mapping for configuration objects and dispatch tables.

Why does my keyof typeof return string instead of literal keys?

This occurs when the object lacks a const assertion or when the variable is mutable and initialized with literal values. Without as const, TypeScript widens property types to their base primitives (e.g., string instead of "red" | "green"). Add as const to the object literal or declare the variable with const to preserve literal types for keyof typeof extraction.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s https://instagit.com/install.md

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client