TypeScript Conditional Types Good Usage Part 1

Example 1

I try to understand eventType field from Event object. But in order to use createEventGridMessage in other TypeScript file, you need to import StartEvent, InfoEvent and EndEvent types. So next example of this case looks better to me 🙂

import {v4 as uuidv4} from 'uuid';

export type BaseEvent = {
    timeStamp?: number,
    componentName: string,
    componentVersion: string,
    operationId: string,
    correlationId: string,
    data: any,
}

export type StartEvent = Partial<BaseEvent> & {
    eventType: 'LogStart',
    source: string,
    trigger: string,
};

export type EndEvent = Partial<BaseEvent> & {
    eventType: 'LogEnd'
    destination: 'Event Grid' | 'Http Response' | 'Database' | string,
};

export type InfoEvent = Pick<EndEvent, keyof Omit<EndEvent, 'eventType'>> & { eventType: 'LogInfo' };

export type Event = StartEvent | EndEvent | InfoEvent;


export type EventGridMessageParameters<T extends Event> = Pick<EventGridMessage<T>, 'data' | 'eventType' | 'subject' | 'topic'>;

export type EventType<T extends Event> = T extends StartEvent ? 'LogStart' : T extends EndEvent ? 'LogEnd' : T extends InfoEvent ? 'LogInfo' : 'UNKNOWN EVENT TYPE';

export interface EventGridMessage<T extends Event> {
    id: string;
    topic: string;
    subject: string;
    eventType: EventType<T>;
    eventTime: Date;
    data: T;
    dataVersion: string;
    metadataVersion: string;
}

export const createEventGridMessage = <T extends Event>({
                                                            data,
                                                            eventType,
                                                            subject = "New message from unknown source",
                                                            topic = ""
                                                        }: EventGridMessageParameters<T>): EventGridMessage<T> =>
    ({
        id: uuidv4(),
        topic,
        subject,
        eventType,
        eventTime: new Date(),
        data: {...data, timeStamp: data.timeStamp || new Date().getTime()},
        dataVersion: "1.0",
        metadataVersion: "1.0"
    });


const startLogMessage = createEventGridMessage<StartEvent>({
    eventType: 'LogStart',
    data: {
        eventType: 'LogStart',
        trigger: '',
        source: '',
        correlationId: '',
        componentVersion: '',
        componentName: '',
        operationId: ''
    },
    subject: '',
    topic: ''
});
console.log(startLogMessage);


const infoLogMessage = createEventGridMessage<InfoEvent>({
    eventType: 'LogInfo',
    data: {
        eventType: 'LogInfo',
        correlationId: '',
        componentVersion: '',
        componentName: '',
        operationId: '',
        destination: '',
    },
    subject: '',
    topic: ''
});
console.log(infoLogMessage);


const endLogMessage = createEventGridMessage<EndEvent>({
    eventType: 'LogEnd',
    data: {
        eventType: 'LogEnd',
        timeStamp: new Date().getTime(),
        correlationId: '',
        componentVersion: '',
        componentName: '',
        operationId: '',
        destination: '',
    },
    subject: '',
    topic: ''
});
console.log(endLogMessage);

Example 1 – Updated

import {v4 as uuidv4} from 'uuid';

export type BaseEvent = {
    timeStamp?: number,
    componentName: string,
    componentVersion: string,
    operationId: string,
    correlationId: string,
    data: any,
}

export type StartEvent = Partial<BaseEvent> & {
    source: string,
    trigger: string,
};

export type EndEvent = Partial<BaseEvent> & {
    destination: 'Event Grid' | 'Http Response' | 'Database' | string
};

export type InfoEvent = Pick<EndEvent, keyof EndEvent>;

export type Event = StartEvent | EndEvent | InfoEvent;


export type EventType = 'LogStart' | 'LogInfo' | 'LogEnd';

export type EventDataType<T extends EventType> = T extends 'LogStart' ? StartEvent : T extends 'LogInfo' ? InfoEvent : T extends 'LogEnd' ? EndEvent : unknown;

export interface EventGridMessage<TEventType extends EventType> {
    id: string;
    topic: string;
    subject: string;
    eventType: TEventType;
    eventTime: Date;
    data: EventDataType<TEventType>;
    dataVersion: string;
    metadataVersion: string;
}


export type EventGridMessageParameters<TEventType extends EventType> = Pick<EventGridMessage<TEventType>, 'data' | 'eventType' | 'subject' | 'topic'>;


export const createEventGridMessage = <TEventType extends EventType>({
                                                                         data,
                                                                         eventType,
                                                                         subject = "New message from unknown source",
                                                                         topic = ""
                                                                     }: EventGridMessageParameters<TEventType>): EventGridMessage<TEventType> =>
    ({
        id: uuidv4(),
        topic,
        subject,
        eventType,
        eventTime: new Date(),
        data: {...data, timeStamp: data.timeStamp || new Date().getTime()},
        dataVersion: "1.0",
        metadataVersion: "1.0"
    });

const startLogMessage = createEventGridMessage({
    eventType: 'LogStart',
    data: {
        trigger: '',
        source: '',
        correlationId: '',
        componentVersion: '',
        componentName: '',
        operationId: ''
    },
    subject: '',
    topic: ''
});
console.log(startLogMessage);


const infoLogMessage = createEventGridMessage({
    eventType: 'LogInfo',
    data: {
        correlationId: '',
        componentVersion: '',
        componentName: '',
        operationId: '',
        destination: '',
    },
    subject: '',
    topic: ''
});
console.log(infoLogMessage);


const endLogMessage = createEventGridMessage({
    eventType: 'LogEnd',
    data: {
        correlationId: '',
        componentVersion: '',
        componentName: '',
        operationId: '',
        destination: '',
    },
    subject: '',
    topic: ''
});
console.log(endLogMessage);

Leave a Reply