🔷 TypeScript Q12 / 101

What is typeof in TypeScript?

AI-Powered Answer ✓ Answered

In TypeScript, the `typeof` keyword serves a dual purpose: it acts as the familiar JavaScript runtime operator to inspect the type of a value at runtime, and it also functions as a powerful type operator at compile-time to infer and extract the type of a variable or property. This dual nature allows TypeScript to build robust type systems based on existing values.

JavaScript's Runtime `typeof` Operator

At its core, typeof is a JavaScript operator that returns a string indicating the type of its unevaluated operand. This operation occurs at runtime, meaning when your code is actually executing. It's useful for conditional logic based on the actual type of a value during execution.

typescript
let myString = "hello";
let myNumber = 123;
let myObject = { a: 1 };
let myFunction = () => {};

console.log(typeof myString);   // "string"
console.log(typeof myNumber);   // "number"
console.log(typeof myObject);   // "object"
console.log(typeof myFunction); // "function"
console.log(typeof undefined);  // "undefined"
console.log(typeof null);       // "object" (a historical quirk of JavaScript)

TypeScript's Compile-Time `typeof` Type Operator

Beyond its runtime role, TypeScript extends typeof as a type operator that can be used in type contexts. When used in a type annotation or definition, typeof allows you to extract the type of a variable, property, or even an imported module, at compile-time. This is incredibly useful for ensuring type safety when working with existing values and avoiding redundant type definitions.

Inferring Types from Variables

You can use typeof to get the type of a variable and then assign that type to another variable, ensuring they are compatible.

typescript
const greeting = "Hello, TypeScript!";
type GreetingType = typeof greeting; // GreetingType is now 'string'

let anotherGreeting: GreetingType; // 'anotherGreeting' is type 'string'
anotherGreeting = "World"; // OK
// anotherGreeting = 123; // Error: Type 'number' is not assignable to type 'string'.

Inferring Types from Functions (Return Types)

When applied to a function, typeof extracts the function's type signature, including its parameters and return type. To get just the return type of a function, you often combine typeof with the ReturnType<T> utility type.

typescript
function createPoint(x: number, y: number) {
  return { x, y };
}

type PointCreator = typeof createPoint; // PointCreator is type '(x: number, y: number) => { x: number; y: number; }'

type Point = ReturnType<typeof createPoint>; // Point is type '{ x: number; y: number; }'

const myPoint: Point = { x: 10, y: 20 }; // OK
// const invalidPoint: Point = { x: 10, y: '20' }; // Error

Key Differences and Use Cases

  • Runtime vs. Compile-Time: The JavaScript typeof operates at runtime and yields a string value. TypeScript's typeof operates at compile-time and yields a type.
  • Type Inference: TypeScript's typeof is crucial for advanced type inference, allowing types to be derived from existing JavaScript values or expressions without manually declaring them.
  • Creating New Types: It enables the creation of new types based on the shape of existing variables, objects, or even entire modules, promoting type reuse and reducing duplication.
  • Working with Modules/Imports: You can use typeof to get the type of an imported module, which is particularly useful for namespaces or when you want to re-export types from another module.

Example Scenario: Configuration Object

Imagine you have a default configuration object. Instead of defining an interface manually for it, you can use typeof to automatically derive its type, ensuring consistency.

typescript
const defaultConfig = {
  port: 3000,
  host: "localhost",
  debugMode: false,
  timeout: 5000
};

type AppConfig = typeof defaultConfig;

function initializeApp(config: AppConfig) {
  console.log(`Starting app on ${config.host}:${config.port}`);
  if (config.debugMode) {
    console.log('Debug mode is ON');
  }
}

const customConfig: AppConfig = {
  port: 8080,
  host: "192.168.1.1",
  debugMode: true,
  timeout: 10000
};

initializeApp(customConfig);
// initializeApp({ port: '80', host: 'test' }); // Error: Type 'string' is not assignable to type 'number' for 'port'.