Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

269
Views
why typeof cannot narrow the union type

When using typeof to narrow the union type, it surprises me that on the premise that under the if condition we get the particular type of field value but cannot infer the type of field targetcorrectly. Why cannot infer that the type of target is HTMLInputElement when know the value is a string?

type UserTextEvent = { value: string, target: HTMLInputElement };
type UserMouseEvent = { value: [number, number], target: HTMLElement };

type UserEvent = UserTextEvent | UserMouseEvent 


function handle(event: UserEvent) {
  if (typeof event.value === 'string') {
    event.value  // string
                 //---------------------------------------------------------------
    event.target // HTMLInputElement | HTMLElement but not HTMLInputElement, why ?
                 //
    return
  }


}
over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

You could somewhat rehydrate the original types with a type predicate:

interface UserTextEvent { value: string, target: HTMLInputElement };
interface UserMouseEvent { value: [number, number], target: HTMLElement };

type UserEvent = UserTextEvent | UserMouseEvent 


function handle(event: UserEvent) {
  if (isUserTextEvent(event)) {
    event.value  // string
                 //---------------------------------------------------------------
    event.target // HTMLInputElement
                 //
    return
  } else {
      event.value // [number, number]

      event.target // HTMLElement
  }


}

function isUserTextEvent(userEvent: UserEvent): userEvent is UserTextEvent {
    return typeof userEvent === 'string';
}

TypeScript playground

over 4 years ago · Santiago Trujillo Report

0

The type of narrowing you're looking for I believe falls under the category discriminated unions.

🚩 I say "I believe" because I'm not 100% certain of the definition of "discriminated unions". I know the values of a property shared by all member types of a type union act as discriminants (e.g. o.kind: 'circle' vs o.kind: 'square'). I'm not sure if type inference based on the types of shared properties (e.g. typeof o.value === 'string' vs Array.isArray(o.value)) is considered the same or something else. But if both property values and types are considered discriminants vis-a-vis "discriminated unions", my explanation below answers your question.

Discriminated Unions currently support discriminant property values, not types.

Here are the types of discriminant properties supported when discriminated unions support was added in Typescript 2.0:

A discriminant property type guard is an expression of the form x.p == v, x.p === v, x.p != v, or x.p !== v, where p and v are a property and an expression of a string literal type or a union of string literal types. The discriminant property type guard narrows the type of x to those constituent types of x that have a discriminant property p with one of the possible values of v.

Note that we currently only support discriminant properties of string literal types. We intend to later add support for boolean and numeric literal types.

Typescript 3.2 expanded that support:

Common properties of unions are now considered discriminants as long as they contain some singleton type (e.g. a string literal, null, or undefined), and they contain no generics.

As a result, TypeScript 3.2 considers the error property in the following example to be a discriminant, whereas before it wouldn’t since Error isn’t a singleton type. Thanks to this, narrowing works correctly in the body of the unwrap function.

type Result<T> = { error: Error; data: null } | { error: null; data: T };
function unwrap<T>(result: Result<T>) {
  if (result.error) {
    // Here 'error' is non-null
    throw result.error;
  }
  // Now 'data' is non-null
  return result.data;
}

Typescript 4.5 added support for "Template String Types as Discriminants".

TypeScript 4.5 now can narrow values that have template string types, and also recognizes template string types as discriminants.

As an example, the following used to fail, but now successfully type-checks in TypeScript 4.5.

export interface Success {
    type: `${string}Success`;
    body: string;
}
 
export interface Error {
    type: `${string}Error`;
    message: string
}
 
export function handler(r: Success | Error) {
    if (r.type === "HttpSuccess") {
        const token = r.body;
                     
(parameter) r: Success
    }
}Try

In all cases, it's the value of the discriminant property that is used to discriminate, not its type.

In other words, as of the current version of Typescript, 4.5, I think you're out of luck. It's just not supported (yet).

🌶 I searched Typescript Issues and couldn't find a mention. Perhaps open a new Issue pointing to this SO question?

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!