TypeScript Creating Union Type from a Union Type

type TodoAction =
    | { type: 'INIT' }
    | { type: 'ADD_TODO', text: string }
    | { type: 'REMOVE_TODO', id: string }
    | { type: 'SET_COMPLETED', id: string };
type KeysOfUnion<T extends { type: any }> = 
   T extends { type: infer K } ? {} extends Omit<Extract<T, { type: K }>, 'type'> ? never : K : never;

function dispatch(actionType: KeysOfUnion<TodoAction>) { throw new Error('Not implemented') };

dispatch('ADD_TODO');
Continue reading

TypeScript Separate Union Type Fields

My aim is to create a function which has two parameters. First one is union value of type field in Action and second parameter should force developer to use specific object in Action.

For example, assume that we have this function function dispatch(actionType, payload){}

dispatch('SIGN_OUT', { type: 'SIGN_OUT', userName: '' });

or

dispatch('SIGN_IN_FAILURE', { type: 'SIGN_IN_FAILURE', error: '' });

type Action =
    | { type: 'INIT' }
    | { type: 'SYNC' }
    | { type: 'SIGN_UP', userName: string, password: string, attributeList: Array<any> }
    | { type: 'SIGN_IN', userName: string, pasword: string }
    | { type: 'SIGN_IN_SUCCESS', accessToken: string }
    | { type: 'SIGN_IN_FAILURE', error: string }
    | { type: 'SIGN_OUT', userName: string }
    | { type: 'FORGOT_PASSWORD', userName: string }
    | { type: 'FORGOT_PASSWORD_SUCCESS', verificationCode: string }
    | { type: 'CHANGE_PASSWORD', userName: string, oldPassword: string, newPassword: string }
    | { type: 'CONFIRM_REGISTRATION', userName: string, confirmationCode: string }
    | { type: 'RESEND_CONFIRMATION_CODE', userName: string }
    | { type: 'DELETE_USER', userName: string }
    | { type: 'GET_SESSION', userName: string }
    | { type: 'GET_SESSION_SUCCESS', session: string };

type TodoAction =
    | { type: 'INIT' }
    | { type: 'ADD_TODO', text: string }
    | { type: 'REMOVE_TODO', id: string }
    | { type: 'SET_COMPLETED', id: string };
Continue reading

Azure Cosmos DB Emulator and Storage Emulator

Azurite emulator provides a free local environment for testing your Azure blob, queue storage, and table storage applications. Find more details in https://docs.microsoft.com/en-us/azure/storage/common/storage-use-azurite?tabs=docker-hub

Cosmosdb emulator provides a local environment that emulates the Azure Cosmos DB service for development purposes. Using the Azure Cosmos DB Emulator, you can develop and test your application locally. You can develop applications using Azure Cosmos DB Emulator with the SQLCassandraMongoDBGremlin/Graph, and Table API accounts. Currently the data explorer in the emulator fully supports viewing SQL data only; the data created using MongoDB, Gremlin/Graph and Cassandra client applications it is not viewable at this time. 

Creating docker-compose.yml file

version: '3.4'

services:
  cosmosdb:
    container_name: cosmosdb
    image: "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator"
    tty: true
    restart: always
    mem_limit: 3g
    cpu_count: 2
    environment:
      - AZURE_COSMOS_EMULATOR_PARTITION_COUNT=10
      - AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE=true
      - AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE=${HOST_IP}
    ports:
        - "8081:8081"
        - "10251:10251"
        - "10252:10252"
        - "10253:10253"
        - "10254:10254"
    volumes:
        - vol_cosmos:/data/db

  azurite:
    container_name: azurite
    image: "mcr.microsoft.com/azure-storage/azurite"
    restart: always
    ports:
        - "10000:10000"
        - "10001:10001"
        - "10002:10002"
    volumes:
        - vol_cosmos:/data

volumes:
  vol_cosmos:
Continue reading

TypeScript listen property value changes

Creating makeWatchedObject function with overloading

type PropEventSource<T> = {
    on<K extends string & keyof T>(eventName: `${K}Changed`, callback: (newValue: T[K]) => void): void;
};

type ReturnType<T1> = T1 & PropEventSource<T1>;

function makeWatchedObject<T1>(obj1: T1): ReturnType<T1>
function makeWatchedObject<T1, T2>(obj1: T1, obj2: T2): ReturnType<T1 & T2>
function makeWatchedObject<T1, T2, T3>(obj1: T1, obj2: T2, obj3: T3): ReturnType<T1 & T2 & T3>
function makeWatchedObject<T1, T2, T3>(obj1: T1, obj2?: T2, obj3?: T3): any {

    const events = new Map<string, (a: any) => void>();

    const on_: PropEventSource<T1 & T2 & T3> = {
        on(eventName: string, callback: (newValue: any) => void): void {

            events.set(eventName, callback);
        }
    }
    const obj = { ...obj1, ...obj2, ...obj3, ...on_ };

    const proxyObj = new Proxy(obj, {
        set: function (target, property, value, receiver) {

            const event = events.get(`${property as string}Changed`);

            if (event) {
                event(value);
                return true;
            }

            return Reflect.set(target, property, value, receiver);
        }
    });

    return proxyObj;
}

Usage

const personInfo = { id: 1, firstName: 'kenan', lastName: 'hancer', age: 36 };
const personAddress = { country: 'UK', city: 'London' };
const personContact = { email: 'kh@kh.com', phone: '077111-33-33' };

let person = makeWatchedObject(personInfo, personAddress, personContact);


// works! 'newName' is typed as 'string'
person.on("firstNameChanged", (newName: string) => {

    console.log(`new name is ${newName.toUpperCase()}`);
});


// works! 'newAge' is typed as 'number'
person.on("ageChanged", (newAge: number) => {
    if (newAge < 0) {
        console.log("warning! negative age");
    }
});

person.age = 34;

TypeScript Function Conditional Type Good Usage

Example 1

type MessageOf<T> = T extends { message: infer MessageType } ? MessageType : never;

const getMessage = <T extends { message: any }>(obj: T): MessageOf<T> => obj.message;



const sms = { phone: '07733838', message: ['Hello World', 'Merhaba Dunya'] };

const smsMessage = getMessage(sms); // string[]

const email = { from: 'kh@kh.com', to: 'kkkk@kkkk.com', message: 'Hello World' };

const emailMessage = getMessage(email); // string

Example 1.1

Notice that line 3 in below code, T extends { message: unknown } message field data type is unknown so returning value from function should be casted to any like obj.message as any

type MessageOf<T> = T extends { message: infer MessageType } ? MessageType : never;

const getMessage = <T extends { message: unknown }>(obj: T): MessageOf<T> => obj.message as any;



const sms = { phone: '07733838', message: ['Hello World', 'Merhaba Dunya'] };

const smsMessage = getMessage(sms); // string[]

const email = { from: 'kh@kh.com', to: 'kkkk@kkkk.com', message: 'Hello World' };

const emailMessage = getMessage(email); // string

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);
Continue reading

TypeScript Conditional Types

Conditional Type Expression

SomeType extends OtherType ? TrueType : FalseType;

Basic Usages

Example 1

type NumberOrNot<T> = T extends number ? string : never;

Example 2

type StringOrNot<T> = T extends string ? string : never;

Example 3

type BooleanOrNumberOnly<T> = T extends boolean | number ? T : never;

type NewType = StringOrNumberOnly<string | number | boolean>;

const a1: NewType = true; // boolean

const a2: NewType = 23; // number
Continue reading