That Typescript ADT example is heavily outdated (or explicitly obtuse)
class Bar { tag="bar" as const; constructor(public value:string){} }
class Baz { tag="baz" as const; constructor(public value:boolean){} }
type Foo = Bar|Baz;
That's true if pattern matching specifically is what you want, in practice we don't constructs ADT's for the sake of it and rolls with interfaces that also meshes well with data imported from other sources (ie JSON).
Also while pattern matching isn't an explicit thing in TS you get most benefits from compiler checked accesses that can recognize type-tests in your code and derive types and do property access checks.
class Bar { tag="bar" as const; constructor(public value:string){} } class Baz { tag="baz" as const; constructor(public value:boolean){} } type Foo = Bar|Baz;