Skip to Content
Course content

226: Common Interview Questions on Type Inference

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've sat through plenty of technical interviews where the candidate claims to "know" type inference, but then they hit a wall the moment I ask them why a specific string literal is being treated as a generic string. It's a classic trap. Many developers assume that if they assign a value to a variable, TypeScript will automatically lock that variable to the most specific type possible.

The Myth: "TypeScript always infers the most specific literal"

You might think that if you write let status = "success", TypeScript sees exactly what's happening and decides that status can only ever be the string "success". It feels intuitive. After all, the compiler sees the value right there.


let status = "success"; 
// You might think the type is "success"
// But TypeScript actually infers it as: string

status = "error"; // This works perfectly fine.

If TypeScript inferred the literal "success", the line status = "error" would trigger a compiler error. But it doesn't. This is because TypeScript assumes that if you're using let, you probably intend to change that value later. It "widens" the type from the specific literal "success" to the general type string to give you flexibility.

The Reality: Type Widening and the as const Escape Hatch

In an interview, the "senior" answer isn't just knowing that widening happens, but knowing how to prevent it. When you're building something like a configuration object or a set of fixed API endpoints, you don't want widening. You want the literal.

I usually recommend using as const (const assertions) when you need to tell the compiler, "I know this looks like a string, but I want you to treat it as this exact, immutable value."


const CONFIG = {
  endpoint: "https://api.myapp.com",
  timeout: 5000
} as const;

// Now, CONFIG.endpoint is typed as "https://api.myapp.com", NOT string.
// If you try to change it, TypeScript will throw an error because it's now readonly.

Notice the difference? By adding as const, you've changed the inference engine's behavior. This is a frequent talking point in interviews because it demonstrates you understand the trade-off between flexibility (widening) and precision (narrowing).

Handling the "Contextual Typing" Curveball

There's another side to inference that often catches people off guard: Contextual Typing. This is when TypeScript doesn't infer the type from the value (the right side), but from the location (the left side).

Take a look at this common pattern with event listeners. You don't explicitly type the event parameter, yet you get full autocomplete for event.target.


window.addEventListener("click", (event) => {
  console.log(event.clientX); // TS knows this is a MouseEvent
});

I've seen candidates struggle to explain this. The trick is to realize that addEventListener is already defined in the TypeScript standard library. Because the first argument is "click", TypeScript knows that the callback function must receive a MouseEvent. It pushes the type "down" into the callback. If you were to move that arrow function to a separate variable declaration, you'd lose that inference unless you explicitly typed the variable.




📋 Practical Task

Tightening the State Machine Transition Types

You are working on a state machine for a payment flow. Currently, the transitions object is being widened to string, which is causing the updateState function to lose type safety. It's allowing any string to be passed in, even if it's not a valid state.

Your Goal: Use a const assertion to ensure the transitions object maintains its literal types, and update the updateState function to use a type derived from that object so that invalid states cause a compile-time error.


const transitions = {
  IDLE: "PROCESSING",
  PROCESSING: "SUCCESS",
  SUCCESS: "IDLE",
  ERROR: "IDLE"
};

function updateState(currentState: string) {
  const nextState = transitions[currentState];
  console.log(`Moving from ${currentState} to ${nextState}`);
}

// This currently works, but it SHOULD be a type error 
// because "UNKNOWN" isn't a key in our transitions.
updateState("UNKNOWN"); 
Rating
0 0

There are no comments for now.

to be the first to leave a comment.