Matthew Gong
Matthew Gong

Reputation: 53

Typescript refinement confusion

I am reading the book Programming TypeScript. I am confused by the refinement illustrated by the following example:

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 (1)
    // ...
    return;
  }
  event.value; // [number, number]
  event.target; // HTMLInputElement | HTMLElement (2)
}

I think the type of event.target in (1) and (2) should be HTMLInputElement and HTMLElement respectively. My understanding is that the type of event.target is definite if the type of event.value is definite. But it is HTMLInputElement | HTMLElement instead. Can someone explain it more clearly than the book? Thanks.

Upvotes: 2

Views: 213

Answers (1)

dege
dege

Reputation: 2939

You are only checking the type of the property unfortunately typescript will not assume the class type because of that. To do that you have to check that yourself with a method that infers the type.

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

type UserEvent = UserTextEvent | UserMouseEvent;

function isUserTextEvent(event: UserEvent): event is UserTextEvent {
  return typeof event.value === "string";
}

function handle(event: UserEvent) {
  if (isUserTextEvent(event)) {
    event.value; // string
    event.target; // HTMLInputElement  (1)
    // ...
    return;
  }
  event.value; // [number, number]
  event.target; // HTMLElement (2)
}

You can check here on the playground

when you verify

typeof event.value === "string"

Typescript can only infer that value is in fact a string and in the else can only be the [number, number]

But it cannot infer that value is HTMLInputElement or HTMLElement, so still considers HTMLInputElement | HTMLElement. You need to use a method to say that in fact event is UserTextEvent

Upvotes: 1

Related Questions