-
Notifications
You must be signed in to change notification settings - Fork 332
Open
Description
The following properties could be used to narrow down event types:
hasValue
isNext
isInitial
isError
isEnd
Rather than this:
myStream$.subscribe(event => {
if (event.hasValue) {
const {prop1, prop2} = (event as Value<{prop1: string, prop2: boolean}>).value
} else if (event.isError) {
const {error} = (event as Error)
}
})
it would be nice to do this instead:
myStream$.subscribe(event => {
if (event.hasValue) {
const {prop1, prop2} = event.value
} else if (event.isError) {
const {error} = event
}
})
Here's some working skeleton code (only implements the distinguishing features of various event types):
interface INextEvent<V> {
readonly hasValue: true
readonly isNext: true
readonly isInitial: false
readonly isError: false
readonly isEnd: false
readonly value: V
}
interface IInitialEvent<V> {
readonly hasValue: true
readonly isNext: false
readonly isInitial: true
readonly isError: false
readonly isEnd: false
readonly value: V
}
interface IErrorEvent {
readonly hasValue: false
readonly isNext: false
readonly isInitial: false
readonly isError: true
readonly isEnd: false
readonly error: unknown
}
interface IEndEvent {
readonly hasValue: false
readonly isNext: false
readonly isInitial: false
readonly isError: false
readonly isEnd: true
}
type IEvent<V> = INextEvent<V> | IInitialEvent<V> | IErrorEvent | IEndEvent
abstract class AbstractEvent<
HasValue extends boolean,
IsNext extends boolean,
IsInitial extends boolean,
IsError extends boolean,
IsEnd extends boolean
> {
constructor(
public readonly hasValue: HasValue,
public readonly isNext: IsNext,
public readonly isInitial: IsInitial,
public readonly isError: IsError,
public readonly isEnd: IsEnd
) {}
}
class NextEvent<V> extends AbstractEvent<true, true, false, false, false> implements INextEvent<V> {
constructor(public readonly value: V) {
super(true, true, false, false, false)
}
}
class InitialEvent<V> extends AbstractEvent<true, false, true, false, false> implements IInitialEvent<V> {
constructor(public readonly value: V) {
super(true, false, true, false, false)
}
}
class ErrorEvent extends AbstractEvent<false, false, false, true, false> implements IErrorEvent {
constructor(public readonly error: unknown) {
super(false, false, false, true, false)
}
}
class EndEvent extends AbstractEvent<false, false, false, false, true> implements IEndEvent {
constructor() {
super(false, false, false, false, true)
}
}
The key is to declare events as being of type IEvent<V>
. Although not all event types have a value, the nice part about this is that when either hasValue
, isNext
or isInitial
are true
, value
doesn't need to be cast to V
.
My own Bacon alternative (work in progress) doesn't use event classes at all. Events are created by factories and there's no need for weird prefixed type names such as IEvent
.
Metadata
Metadata
Assignees
Labels
No labels