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

Jest Mocking Default Instance

Example 1

const mockedPutObject = jest.fn();

jest.mock('@aws-sdk/client-s3', () => {
    return {
        S3: jest.fn(() => ({
            putObject: () => mockedPutObject()
        }))
    }
});

Example 2

jest.mock('uuid', () => ({ v4: () => '00000000-0000-0000-0000-000000000000' }));

Example 3

jest.mock('uuid');

const mockedUuidv4 = uuidv4 as jest.Mock;

mockedUuidv4.mockReturnValue('00000000-0000-0000-0000-000000000000');

Example 4

const mockedAjvValidate = jest.fn().mockReturnValue(true);

jest.mock('ajv', () => {
    return jest.fn(() => ({
        compile: () => () => mockedAjvValidate()
    }))
});

Example 5

jest.mock('moment', () => () => ({ format: () => '30-12-2021' }));

Example 6

import { v4 as uuid } from 'uuid';

export interface IEvent {
    id?: string;
    eventType: string;
    data: any;
}

export interface IEventRepository {
    getEvent(id: string): IEvent | undefined;
    setEvent(event: IEvent): IEvent;
}

const createEvent = (event: IEvent): IEvent => ({ id: uuid(), ...event });

export class EventRedisRepository implements IEventRepository {

    constructor(private events: Array<IEvent> = []) { }

    getEvent(id: string): IEvent | undefined {
        return this.events.find(item => item.id === id);
    }
    setEvent(event: IEvent): IEvent {

        const newEvent: IEvent = createEvent(event);

        this.events.push(newEvent);

        return newEvent;
    }
}

export class EventCosmosDbRepository implements IEventRepository {

    constructor(private events: Array<IEvent> = []) { }

    getEvent(id: string): IEvent | undefined {
        return this.events.find(item => item.id === id);
    }
    setEvent(event: IEvent): IEvent {

        const newEvent: IEvent = createEvent(event);

        this.events.push(newEvent);

        return newEvent;
    }
}

import { Persister } from '../src';
import { EventRedisRepository, EventCosmosDbRepository, IEventRepository, IEvent } from './sample';

const mockedRedisGetEvent = jest.fn();

const mockedRedisSetEvent = jest.fn();

const mockedCosmosdbGetEvent = jest.fn();

const mockedCosmosdbSetEvent = jest.fn();

jest.mock('./sample', () => {
    return {
        EventRedisRepository: jest.fn(() => ({
            getEvent: () => mockedRedisGetEvent(),
            setEvent: () => mockedRedisSetEvent(),
        })),
        EventCosmosDbRepository: jest.fn(() => ({
            getEvent: () => mockedCosmosdbGetEvent(),
            setEvent: () => mockedCosmosdbSetEvent(),
        })),
    }
});

const eventRedisRepository = new EventRedisRepository();

const eventCosmosdbRepository = new EventCosmosDbRepository();

const module_name = `${Persister.name}`;

describe(`${module_name} Test`, () => {
    it('should call setEvent function in both persisters', () => {

        const eventPersister = new Persister<IEventRepository>([eventRedisRepository, eventCosmosdbRepository]);

        const eventId = '4e0c71bb-eedf-43dd-b9a1-911a7ed6d74f';
        const newEvent: IEvent = { id: eventId, eventType: 'Submission', data: {} };

        eventPersister.set('setEvent', op => op(newEvent));

        expect(mockedRedisSetEvent).toHaveBeenCalledTimes(1);

        expect(mockedCosmosdbSetEvent).toHaveBeenCalledTimes(1);

    });
});

Example 6 – updated and short version

import { Persister } from '../src';
import { EventRedisRepository, EventCosmosDbRepository, IEventRepository, IEvent } from './sample';
import { mocked } from 'jest-mock'

jest.mock('./sample');

const eventRedisRepository = new EventRedisRepository();

const eventCosmosdbRepository = new EventCosmosDbRepository();

const mockedEventRedisRepository = mocked(eventRedisRepository, true);

const mockedEventCosmosdbRepository = mocked(eventRedisRepository, true);

const module_name = `${Persister.name}`;

describe(`${module_name} Test`, () => {
    it('should call setEvent function in both persisters', () => {

        const eventPersister = new Persister<IEventRepository>([eventRedisRepository, eventCosmosdbRepository]);

        const eventId = '4e0c71bb-eedf-43dd-b9a1-911a7ed6d74f';
        const newEvent: IEvent = { id: eventId, eventType: 'Submission', data: {} };

        eventPersister.set('setEvent', op => op(newEvent));

        expect(mockedEventRedisRepository.setEvent).toHaveBeenCalledTimes(1);

        expect(mockedEventCosmosdbRepository.setEvent).toHaveBeenCalledTimes(1);

    });
});

How to install AWS and Azure CLI with Homebrew

Homebrew

Installing

Run the following code to install Homebrew if you don't have in your machine.

$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

Verifying

Run the following code to verify Homebrew is installed

$ brew --version

Homebrew 3.2.6
Homebrew/homebrew-core (git revision c4d5aac8ec; last commit 2021-08-07)
Homebrew/homebrew-cask (git revision 1169dcb641; last commit 2021-08-07)

AWS

AWS CLI

Installing

$ brew update && brew install awscli

Verifying

$ aws --version

aws-cli/2.2.27 Python/3.9.6 Darwin/20.6.0 source/x86_64 prompt/off

Upgrading

$ brew upgrade awscli

Uninstalling

$ brew uninstall awscli

Using AWS CLI

Create default profile

$ aws configure

AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/EXAMPLEKEY
Default region name [None]: eu-west-2
Default output format [None]: json

Create Named Profiles

$ aws configure --profile kenan-dev

AWS Access Key ID [None]: AKIAI44QH8DHBEXAMPLE
AWS Secret Access Key [None]: je7MtGbClwBF/2Zp9Utk/EXAMPLEKEY
Default region name [None]: eu-west-2
Default output format [None]: json

List AWS Profiles

$ aws configure list-profiles

List AWS Configuration Data

$ aws configure list
$ aws configure list --profile kenan-dev

Set any credential or configuration settings

$ aws configure set aws_access_key_id bar --profile default
$ aws configure set aws_secret_access_key foo --profile kenan-dev

Get any credential or configuration settings

$ aws configure get aws_access_key_id --profile default
$ aws configure get region --profile kenan-dev

AWS SAM CLI

Installing

$ brew tap aws/tap
$ brew install aws-sam-cli

Verifying

$ sam --version

SAM CLI, version 1.27.2

Upgrading

$ brew upgrade aws-sam-cli

Uninstalling

$ brew uninstall aws-sam-cli

Azure

Demo repository is in the following link.

https://github.com/nodejs-projects-kenanhancer/serverless-azure-functions-typescript-demo

Azure CLI

Installing

$ brew update && brew install azure-cli

Verifying

$ az --version

azure-cli                         2.26.1 *

core                              2.26.1 *
telemetry                          1.0.6

Python location '/usr/local/Cellar/azure-cli/2.26.1/libexec/bin/python'
Extensions directory '/Users/kenanhancer/.azure/cliextensions'

Python (Darwin) 3.8.11 (default, Jun 29 2021, 03:08:07) 
[Clang 12.0.5 (clang-1205.0.22.9)]

Legal docs and information: aka.ms/AzureCliLegal


You have 2 updates available. Consider updating your CLI installation with 'az upgrade'

Please let us know how we are doing: https://aka.ms/azureclihats
and let us know if you're interested in trying out our newest features: https://aka.ms/CLIUXstudy

Upgrading

$ brew update && brew upgrade azure-cli

# OR

$ az upgrade

Uninstalling

$ brew uninstall azure-cli

Sign in with Azure CLI

Sign in interactively

$ az login

Sign in with device code flow interactively

If no web browser is available or the web browser fails to open, you may force device code flow with below command.

$ az login --use-device-code

Azure Functions Core Tools

Installing

$ brew tap azure/functions
$ brew install azure-functions-core-tools@3

# if upgrading on a machine that has 2.x installed
$ brew link --overwrite azure-functions-core-tools@3

Verifying

$ func --version

3.0.3477

Upgrading

$ brew upgrade azure-functions-core-tools@3

Uninstalling

$ brew uninstall azure-functions-core-tools@3