DEV Community

loading...

Discussion on: Why TypeScript is a better option than JavaScript when it comes to functional programming?

Collapse
rdeneau profile image
Romain Deneau

👍 Nice article with good examples based on a real and interesting domain.

💡 Examples can be more readable and TypeScript idiomatics this way (IMHO):

// -- Common ----

const IRELAND = Object.freeze({
    name: 'Ireland',
    ageOfMajority: 18,
});

// -- FP ----

// Helpers
type predicate<T> = (a: T) => boolean;

const either: <T>(f: predicate<T>, g: predicate<T>) => predicate<T> =
    (f, g) => a => f(a) || g(a);

const both: <T>(f: predicate<T>, g: predicate<T>) => predicate<T> =
    (f, g) => a => f(a) && g(a);

// Domain
interface Person {
    birthCountry: string;
    naturalizationDate: Date | null;
    age: number;
}

const wasBornInCountry = (person: Person) => person.birthCountry === IRELAND.name;
const wasNaturalized   = (person: Person) => Boolean(person.naturalizationDate);
const isOver18         = (person: Person) => person.age >= IRELAND.ageOfMajority;
const isCitizen        = either(wasBornInCountry, wasNaturalized);

export const isEligibleToVote = both(isOver18, isCitizen);

// -- OOP ----

class Person {
    constructor(
        // Prefer inline properties
        // Prefer no "_" prefix for private fields except to ease JavaScript initeractions
        private readonly _age: number,
        private readonly _birthCountry: string,
        private readonly _naturalizationDate: Date | null = null,
    ) { }

    // Prefer "step-down rule": outside-in declarations
    isEligibleToVote() {
        return this._isOver18() && this._isCitizen();
    }

    private _isOver18() {
        return this._age >= IRELAND.ageOfMajority;
    }

    private _isCitizen() {
        return this._wasBornInCountry() || this._wasNaturalized();
    }

    private _wasBornInCountry() {
        return this._birthCountry === IRELAND.name;
    }

    private _wasNaturalized() {
        return !!this._naturalizationDate;
    }
}