1

I'm trying to put together some data, that's an array of union types, and process it accrodingly. I don't understand why typescript can't narrow this union types down:

interface BaseEvent {
    id: number;
}

interface EventA extends BaseEvent {
    attrA: string;
}

interface EventB extends BaseEvent {
    attrB: string;
}

type Events = (EventA | EventB)[];

const events: Events = [{ id: 1, attrA: "A" }, { id: 2, attrB: "B" }]

events.forEach(event => { // type of event is already: (parameter) event: EventA | EventB
    if (typeof event === "EventB") { // <- it doesn't work 

        const eventId = event.id // can only infer id here
    }
}) 

I've tried to use intersections suggested by this answer, but it does not seems to work either. What's the right approach here?

TS playground

3
  • the answer below should solve your problem. typeof includes just the js values listed here: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…. You may use instanceof for more specific typing. Commented Oct 20, 2022 at 4:17
  • typescriptlang.org/docs/handbook/advanced-types.html this explains that typeof X === 'xyz' that is not of the default javascript typeof return types is not recognized as a type guard. So to allow type narrowing, you'd need to use a different method. Perhaps a discriminated union (a shared field has differing values that differentiates its type) or a unique attribute. Commented Oct 20, 2022 at 4:18
  • Thank you so much for the addtional resource! I'll look it up. Commented Oct 20, 2022 at 4:20

1 Answer 1

3

Since your events have differing properties, you can use in:

if ("attrA" in event) { // event is now EventA

Playground

Sign up to request clarification or add additional context in comments.

6 Comments

Ah, I totally forgot in keyword is a thing in JS, I'll accept after the timmer's up.
@Enfieldli yeah in is a "standard js thing" but the key is that TS also uses "in" as type guards. It's weird what TS will infer as a type guard and what not. Like until recently, a constant condition defined outside the if statement would not be a type guard.
@Yuji 'Tomita' Tomita Sometimes it just works without me knowing how :-p, can you link to that change you memtioned, I want to look more into it : )
@Enfieldli took me a while to find this; typescript is only learned through github issues lol: github.com/microsoft/TypeScript/issues/12184 Control flow analysis of aliased conditional expressions and discriminants #44730 -- aliased conditional expressions i.e. const myCondition = foo.bar === 123; if (myCondition) would not have been a type guard until this patch
@Enfieldli and for the record, I think it's "most of the time, it works without us knowing how". It's what trips up so many people myself included about TS until we understand, it's not as deterministic as a typical programming language. It infers things and sometimes can, sometimes can't, infer typings, narrowings, etc. and we hope it does it most of the time.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.