DEV Community

Discussion on: It's 2021 and I still don't know the best way to write this in TypeScript.

Collapse
 
lionelrowe profile image
lionel-rowe • Edited

You can use field initializers outside the constructor to avoid repeating the field names:

class Person {
    constructor (public name: string) {}

    initial = this.name.charAt(0);
    length = this.name.length;
    initialRegex = new RegExp(this.initial, 'g');
    initialOccurance = (this.name.match(this.initialRegex) || []).length;
}
Enter fullscreen mode Exit fullscreen mode

If performance is critical, you could use a getter in combination with memoization of the results. This way, you'd delay evaluation until the first time the property was accessed, but wouldn't have to recalculate it on subsequent accesses.

const memoize = <T extends (arg: any) => any>(fn: T) => {
    type In = Parameters<T>[0]
    type Out = ReturnType<T>

    const results = new Map<In, Out>()

    return (arg: In) => {
        if (results.has(arg)) {
            return results.get(arg) as Out
        }

        const result = fn(arg)

        results.set(arg, result)

        return result as Out
    }
}

const expensiveUpper = (name: string) => {
    const start = Date.now()

    while (Date.now() < start + 1000) { /* hang */ }

    return name.toUpperCase()
}

class Person {
    constructor (public name: string) {}

    private _memoizedUpper = memoize(expensiveUpper)

    get upper() {
        return this._memoizedUpper(this.name)
    }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
reggi profile image
Thomas Reggi

Thanks for the reply! Yes memoization / caching of getters is cool but I often think of it as too much boiler plate, then there's the async issue 🙄. But I do appreciate your other example of using field initializers outside the constructor.

Collapse
 
lionelrowe profile image
lionel-rowe

Yeah memoization is only really suitable where you need the performance boost. If you do need it though, you only need to write the boilerplate once, or zero times if you use a library. It can be adapted for asynchronous usage, as long as the memoized function always produces the same output from the same inputs. That wouldn't be the case when calling a web API, but might be the case if the asynchrony is due to carrying out expensive computation on a worker thread or something.