← Openai Interview Insights

Openai·Software Engineer·Technical Phone Screen·Senior

SeniorPrefer not to say
May 2026Remote

Summary

TypeScript type-system deep dive at OpenAI. The whole session basically revolved around conditional types and inference mechanics, which I thought I knew well enough until they started asking about edge cases.

Questions Asked (4)

Q1

Implement a utility type that extracts the return type of a given function type using the `infer` keyword, similar to how the built-in `ReturnType<T>` works.

Technical Trade-offsAPI & Integrations
Author's notes

I wrote out `type MyReturnType<T> = T extends (...args: any[]) => infer R ?

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by defining a generic type that uses a conditional type to check if the input extends a function signature, then use the infer keyword to capture the return type. Explain how this mirrors the built-in ReturnType<T> and discuss edge cases like overloads and any/never.

Pro tip: Mention that the built-in ReturnType<T> is implemented exactly this way in TypeScript's lib.es5.d.ts, showing you understand the standard library. Also, note that inferring from overloaded functions only captures the last overload, which is a common pitfall.

1. Define the generic type

Declare a type alias that takes a type parameter T, e.g., type MyReturnType<T> = ...

2. Use conditional type with infer

Write a conditional type: T extends (...args: any) => infer R ? R : any. This checks if T is a function and infers its return type as R.

3. Handle non-function types

Decide on the fallback type when T is not a function (e.g., any, never, or unknown) and explain your choice.

4. Test with examples

Demonstrate usage with simple functions, async functions (returning Promise), and overloaded functions to show understanding of edge cases.

5. Compare to built-in ReturnType

Explain that this is exactly how TypeScript's built-in ReturnType<T> works, and mention any differences (e.g., handling of overloads).

Key Points to Mention

  • The infer keyword can only be used within the extends clause of a conditional type.
  • The conditional type distributes over union types if T is a naked type parameter, which can affect behavior.
  • For overloaded functions, infer only captures the last overload signature.
  • The fallback type when T is not a function should be chosen carefully (any vs never vs unknown).
  • The built-in ReturnType<T> is defined in lib.es5.d.ts as T extends (...args: any) => infer R ? R : any.
  • This pattern is useful for creating higher-order types and utility types in TypeScript.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

How does TypeScript handle `ReturnType` when applied to overloaded functions, and what does the inference actually resolve to?

Technical Trade-offs
Author's notes

Blanked for a second.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Explain that TypeScript's `ReturnType` utility type resolves to the return type of the last overload signature, not a union of all overloads. Then clarify that this is because `ReturnType` uses conditional type inference against the function type, which only captures the final overload's signature.

Pro tip: Mention that you can manually create a union of return types by using a distributive conditional type over the overloads, but it requires extracting each signature—something `ReturnType` doesn't do out of the box.

1. Define overloaded functions

Briefly explain what an overloaded function is in TypeScript: multiple call signatures with a single implementation.

2. Explain ReturnType behavior

State that `ReturnType<T>` uses `T extends (...args: any) => infer R ? R : any`, which infers from the last overload signature.

3. Illustrate with an example

Provide a concrete example of an overloaded function and show what `ReturnType` resolves to, highlighting it's the last overload.

4. Discuss implications and workarounds

Explain why this matters (e.g., unexpected types) and how to get a union of all return types using custom conditional types.

Key Points to Mention

  • Overloaded functions have multiple call signatures but only one implementation signature.
  • `ReturnType` is defined as a conditional type that infers from the function type.
  • TypeScript picks the last overload signature when inferring from an overloaded function type.
  • The result is not a union of all possible return types.
  • To get a union, you need to manually extract each overload's return type.
  • This behavior is consistent with how TypeScript resolves other utility types like `Parameters`.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

What does your `ReturnType` utility produce when the input is a generic function, and what are the limitations of that inference?

Technical Trade-offsSystem Design
Author's notes

This one tripped me up more than I expected.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Explain that ReturnType extracts the return type of a function type, but for generic functions it resolves to the return type with type parameters replaced by their constraints (or unknown if unconstrained). Then discuss limitations such as loss of generic parameter relationships, inability to infer from a specific instantiation, and issues with overloaded functions.

Pro tip: Mention that you can often work around the limitation by using a helper type that captures the generic parameters, or by using instantiation expressions (TypeScript 4.7+) to get a more precise return type. This shows deep practical knowledge.

1. Define ReturnType

State that ReturnType<T> is a built-in utility type that extracts the return type of a function type T. It is defined as T extends (...args: any) => infer R ? R : any.

2. Behavior with generic functions

Explain that when T is a generic function, TypeScript infers R by substituting the type parameters with their constraints (or unknown if no constraint). For example, ReturnType<typeof identity> where identity<T>(x: T): T yields unknown if T is unconstrained.

3. Limitations of inference

Discuss that the inferred return type loses the relationship between input and output types; it becomes a concrete type (like unknown) rather than a generic one. Also, it cannot capture conditional return types that depend on the generic parameter.

4. Workarounds and alternatives

Mention that to get a more precise return type for a specific instantiation, you can use instantiation expressions (e.g., ReturnType<typeof identity<string>>) or create a custom utility type that preserves generics.

5. Practical implications

Conclude that while ReturnType is useful for non-generic functions, for generic functions it may produce overly broad types, so it's important to be aware of these limitations when designing type-safe APIs.

Key Points to Mention

  • ReturnType is defined using conditional types and infer.
  • For generic functions, type parameters are replaced by their constraints or unknown.
  • The inferred return type loses generic parameter relationships.
  • Conditional return types depending on generics are not preserved.
  • Overloaded functions: ReturnType picks the last overload signature.
  • Workarounds: instantiation expressions or custom utility types.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

If you apply your `ReturnType` utility to an async function, what type do you get back, and how would you unwrap the inner type?

Technical Trade-offsAPI & Integrations
Author's notes

Easy relative to the others.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying that ReturnType extracts the return type of a function, so for an async function it yields Promise<T>. Then explain that to get the inner type T, you can use the Awaited utility type (TypeScript 4.5+) or manually infer it with a conditional type. Emphasize that understanding this is crucial for typing async operations correctly.

Pro tip: Mention that using Awaited is the modern, recommended approach and that it also handles nested promises and thenables, which is important for robust type definitions in real-world code.

1. Define ReturnType behavior

Explain that ReturnType<T> extracts the return type of a function type T. For an async function, the return type is always a Promise, so ReturnType<typeof asyncFn> gives Promise<InnerType>.

2. Identify the inner type

State that the inner type is the resolved value of the promise, which is what the async function returns when awaited.

3. Introduce Awaited utility

Describe that TypeScript 4.5 introduced the Awaited<T> utility type, which recursively unwraps promises and thenables to get the final resolved type.

4. Show manual unwrapping

If needed, demonstrate how to manually unwrap using conditional types: type Unwrap<T> = T extends Promise<infer U> ? U : T; and apply it to ReturnType.

5. Discuss practical implications

Highlight why this matters: correctly typing async function results avoids type errors and ensures proper handling of resolved values in code.

Key Points to Mention

  • ReturnType<typeof asyncFunction> yields Promise<T>
  • The inner type T is the resolved value of the promise
  • Awaited<T> is the built-in utility to unwrap promises (TypeScript 4.5+)
  • Manual unwrapping with conditional types and infer
  • Awaited handles nested promises and thenables recursively
  • Proper typing of async results improves code safety and developer experience

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.