TypeScript Covariance and Contravariance

I want to share a small part of wikipedia description firstly then will continue with example codes.

Assume that we work with below types.

Animal is Supertype.

Dog and Cat are Subtype.

Covariance

Covariance means that Subtypes can be assigned to Supertypes. This is also known as Polymorphism or Subtyping. So Covariance = Polymorphism = Subtyping

In other words, Dog(Subtype) can be assigned to Animal(Subtype) or Dog(Subtype)

Generic types are also covariant.

Continue reading

TypeScript Narrowing Function Return Type Part 1

Narrowing return type of function is a little bit strange in TypeScript. So I share use cases below from bad to good 🙂

Use Cases

Usage 1 – Bad usage

Notice that even I call foo(0) in line 8, when I hover mouse on foo in line 8, popup shows val parameter type as string | number union type. But it should show as number. So it is not narrowing parameter and return type of function.

Usage 2 – Bad usage

This usage narrows parameter and return type of function as literal type. But we need to narrow to type of parameter not value of parameter as literal type. Popup shows generic parameter type as 0, that's why, parameter and return type becomes 0.

Usage 3 – Good usage (function overloads)

Continue reading

TypeScript Conditional Types Good Usage Part 2

The aim of this code is simply to show related fields in data field dependent on eventType.

When ChangePassword is selected in eventType filed, then userName, oldPassword and newPassword are becoming as required fields.

When ForgotPassword is selected in eventType field, then userName is just becoming required.

const msg1 = publishMessage?.({
    eventType: 'ChangePassword',
    data: {
        userName: '',
        oldPassword: '',
        newPassword: '',
    },
    subject: '',
    topic: '',
});

const msg2 = publishMessage?.({
    eventType: 'ChangePassword_Success',
    data: {
        emailSubject: 'Password Verification code is ######',
        emailMessage: '',
    },
    subject: '',
    topic: '',
});

const msg3 = publishMessage?.({
    eventType: 'ForgotPassword',
    data: {
        userName: ''
    },
    subject: '',
    topic: '',
});

const msg4 = publishMessage?.({
    eventType: 'ForgotPassword_Success',
    data: {
        resetPasswordVerificationCode: '',
        emailSubject: '',
        emailMessage: ''
    },
    subject: '',
    topic: '',
});
Continue reading

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;