Skip to content

Instantly share code, notes, and snippets.

@kamauwashington
Last active September 4, 2022 23:32
Show Gist options
  • Save kamauwashington/66862db0089a25d6890785cc08ac29cf to your computer and use it in GitHub Desktop.
Save kamauwashington/66862db0089a25d6890785cc08ac29cf to your computer and use it in GitHub Desktop.

TypeScript Bits

The awesome Readonly<T> Utility Type

TypeScript Bits are small, easily digestible pieces of information to encourage TypeScript/JavaScript best practices. From beginner, to intermediate, to ninja, these bits are for you 😄


Estimated reading time : 2 minutes

Lets all be honest, side effects are terrible. They are especially terrible when working with a runtime that doesn't implicitly discern between mutable and immutable pre transpilation or compilation (like JavaScript). I have often found functions during code review where an Array or Object is manipulated in the subroutine returning the input Array or Object. Ideally, functions should remain pure.

A pure function is a function where the return value is only determined by its input values, without observable side effects...

So how do we enforce immutability pre-transpilation? Is that even possible in TypeScript?

Before

let myNumArray : Array<number> = [0,1,2,3,4,5];

function removeFirstItem (inArray : Array<number>) : Array<number> {
    inArray.shift();
    return inArray;
}
removeFirstItem(myNumArray);
console.log(myNumArray); // prints [1, 2, 3, 4, 5]

After (will not compile enforcing pure function practice)

let myNumArray : Array<number> = [0,1,2,3,4,5];

function removeFirstItem (inArray : Readonly<Array<number>>) : Readonly<Array<number>> {
    inArray.shift(); // Property 'shift' does not exist on type 'readonly number[]'.(2339)
    return inArray;
}
removeFirstItem(myNumArray);
console.log(myNumArray);

After pt. 2

let myNumArray : Array<number> = [0,1,2,3,4,5];

function removeFirstItem (inArray : Readonly<Array<number>>) : Readonly<Array<number>> {
    let numArrayOut : Array<number> = inArray.concat(); // returns new mutable array;
    numArrayOut.shift();
    return numArrayOut; //implicit cast to Readonly<T>
}
removeFirstItem(myNumArray);
console.log(myNumArray); prints [0, 1, 2, 3, 4, 5] untouched

Notes

  • IMPORTANT The above code WILL RUN (in the "After" block), but if using TypeScript it will not compile
  • The use of Readonly as a return value of a pure function is a design choice not a mandate. It is however good practice as it requires any chained operations or next use to explicitly create a new mutable instance to work with, further enforcing pure functions principles.
  • This is very useful in function type definitions as it explicitly requires that the implementation be pure.

Visit the TypeScript Deep Dive for Readonly for more in-depth information!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment