Enforce Immutable Return in TypeScript

Hello, I am curious to know if there is a way to prevent overwriting a type so that it remains immutable at compile time.

For example, let's create an interface:

interface freeze{
  frozen: boolean;
}

Now, let's define a deep freeze function:

function deepFreeze<T>(obj: T) {
  var propNames = Object.getOwnPropertyNames(obj);
  for (let name of propNames) {
    let value = (obj as any)[name];
    if (value && typeof value === "object") {
      deepFreeze(value);
    }
  }
  return Object.freeze(obj);
}

If we attempt something like this:

function shouldntWork(): freeze 
{  
 let mutableFreeze: freeze = { frozen:false}
  let immutableFreeze = deepFreeze(mutableFreeze); 
  return immutableFreeze;
}

I understand that this behavior is not a bug, but I am wondering if there is an eslint rule or similar mechanism to prevent the overwrite of types. Because if I were to do something like this:

function other()  {
  let something = shouldntWork();
  something.frozen = true;
  console.log(something);
}

This would lead to a runtime crash, even though I want the error to be caught at compile time.

Without specifying :freeze, TypeScript would infer the correct readonly type and provide a compile-time error.

You can test this on the TypeScript playground: here

Answer №1

If you're in need of a DeepReadOnly option, check out this resource -> https://github.com/microsoft/TypeScript/issues/13923

For an example, take a look here -> https://www.typescriptlang.org/play?exactOptionalPropertyTypes=true#code/KYDwDg9gTgLgBDAnmYcxQJYFsMwwN1QF44BnGTAOwHM4AfOSgVywCNgp65WIIAbYAENKXJpQAmwAGYZKwcV2Z8+AWABQoSLATJUAEWDAwAJSHiIlPogA8AFQB8cIurhxbcUDGATSaTDjxCOAB+NzgALhcwz29xXwBBKChBG1kpDjgAVUdQgyNTQXNLRETkm2yIqLyTMwsrAHlWACtgAGMYO3t1TWh4WS8oKUFW-UMawrqSpJTOjxAvHzgCoqtSmerlyc7HAG8AX27wXp0UOA3a4saW9tmSHaioC6s4AG0ABThZOABrYEQIKRuAC64TOY02xTs7yBXTUBzU6kRan6HCGIzgUkewAAXsB7mpXJiILjKKCePwhJQANzqeHqATwUgQLDAABiWNxoMxhlxTjgOyJJK5gj4pGAdLUUjE7QwFjIAAsIEw+OJKDAAOrQb4ACgAlPyogyEMByBkSJIjOyecBtUyWVaccBdVS4A9gDAmFARF5TVAaXCkVLKDK5RAYPKOHrXPjXEa7e75bJaCRSIrlaqNVq9f7XPHw0mAHSC7x8ihMYAuuAAeirAGFhJQw3BBKRSBhqN6IHAAOTFyjd7htQRMMWfPq+QRwR6FAC0kz8EBQsEQBairQsTIEBb4EGotuZCaTztpgeleDlFrADtxnW1EGaoNsuvC5wmkIcBoJcCN6EXADlBBZXwSCuNoYALah3XqAB3Sg3igRcOCQACgLvZpjy-KRoDgbUjUoQDUABBcwBQk19RjVxv3dOB8BFcs+TQppmwnShEF1F58JZIEc0ojBAW1Wi+HogAyYSTmAIjBPoogZLgAAie9rhgOTyKiSi4Eva8bSkp0eNceF9LdD0vTgUD2iLDkbUU-UWzBfInhsBx-QlMMIygbMgA

Please avoid specifying the return type for 'shouldntWork' to allow TypeScript to deduce the read-only nature.

export type primitive = string | number | boolean | undefined | null
export type DeepReadonly<T> =
  T extends primitive ? T :
  T extends Array<infer U> ? DeepReadonlyArray<U> :
  DeepReadonlyObject<T>
export interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
export type DeepReadonlyObject<T> = {
  readonly [P in keyof T]: DeepReadonly<T[P]>
}



interface freeze{
  frozen: boolean;
}

let someFreeze: freeze = {frozen: false}

function shouldntWork() {
  let tester = deepFreeze(someFreeze); 
  return tester;
}

function other()  {
  let something = shouldntWork();
  something.frozen = true;  //Cannot assign to 'frozen' because it is a read-only property.
  console.log(something);
}


function deepFreeze<T>(obj: T):DeepReadonly<T> {
  let propNames = Object.getOwnPropertyNames(obj);
  for (let name of propNames) {
    let value = (obj as any)[name];
    if (value && typeof value === "object") {
      deepFreeze(value);
    }
  }
  return Object.freeze(obj) as DeepReadonly<T>;
}

other();

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What steps can be taken to troubleshoot a TypeScript-powered Node.js application running in WebStorm?

Seeking advice on debugging a node.js application utilizing TypeScript within WebStorm - any tips? ...

TypeScript was looking for 'never' but found an intersection instead

Can someone help me understand why a conflicting type intersection did not produce a type of never? What am I overlooking? type A = {value: string} type B = {value: number} type D = A & B type E<T> = T extends never ? 'never' : ' ...

Can you explain the significance of this particular method signature in the TypeScript code snippet shown above?

Referencing the ngrx example, we encounter the code snippet for the method store.select, which has a complex signature with two arrows. What is the significance of this method signature? The interface definition in the type file presents the following sig ...

Modify the [src] attribute of an image dynamically

I have a component that contains a list of records. export class HomeComponent implements OnInit { public wonders: WonderModel[] = []; constructor(private ms: ModelService){ ms.wonderService.getWonders(); this.wonders = ms.wonder ...

When using the delete method in Next.js, req.body is undefined

Strangely, I cannot figure out the reason why fetching data and inserting it into the body of my response results in an "undefined" message in the console. Interestingly, I have two nearly identical components - one employing a POST method with a populated ...

Encountering an ERROR of TypeError when attempting to access the property 'length'

I encountered the following error message: ERROR TypeError: Cannot read property 'length' of undefined at eval (webpack-internal:///./node_modules/@angular/common/esm5/http.js:163) at Array.forEach () at HttpHeaders.lazyInit ...

Utilizing MakeStyles from Material UI to add styling to a nested element

I was pondering the possibility of applying a style specifically to a child element using MakesStyles. For instance, in a typical HTML/CSS project: <div className="parent"> <h1>Title!</h1> </div> .parent h1 { color: # ...

Retrieve data from a table within an Angular component

Struggling with the ng2-smart-table library, I am facing challenges in passing values entered in the edit line to a custom component: Refer to the code snippet below for passing Maximum and Minimum Temperature values to the SmartTableEditorFunctionsCompon ...

Executing installed packages using npm: A step-by-step guide

Recently, I have encountered a confusing issue in my coding journey. In Python, I got used to installing packages and using them right away without any hiccups. For example, with SpotDL, everything worked seamlessly. However, things took a different turn w ...

Bringing in a JSON file into a ReactXP project

I'm encountering a strange issue, possibly a bug, with importing a JSON file as an object into my application. I have the following configurations: "compilerOptions": { "resolveJsonModule": true, "esModuleInterop": true, } While it appears t ...

The modification in Typescript's type order from version 1.7 to 1.8 resulted in a significant

A Visual Studio Cordova application with a unique TypeScript source structure: /src /app /appsub1 appsub1.ts -> 4 : 7 /appsub2 appsub2.ts -> 5 : 6 app.ts -> 3 : 5 /mod1 /mod1sub1 mod1sub1.ts -> 7 : 4 m ...

complete() method is not triggered by Observable

Note: I understand that there is a timer observable from rxjs, but for the purpose of this project, I am using it as a proof of concept to enhance my understanding of observables. In my Angular application, I have developed a timer with a start button, a ...

Error: The Class object cannot be found in Object(...)(...)

I've been encountering a TypeError while trying to implement this angular code. The error seems to be generated around Class({constructor: function() {}}), but I'm not exactly sure why. Any help on this would be greatly appreciated. import "hell ...

What strategies should be followed for managing constant types effectively in TypeScript?

enum ENUM_POSITION_TYPE { LEFT = 1, RIGHT = 2 } // type PositionType = 1 | 2 type PositionType = ??? export let a1: PositionType = ENUM_POSITION_TYPE.RIGHT //correct export let a2: PositionType = 1 as const //correct export let a3: PositionType = 3 / ...

Switch from Gulp-TSLint to Gulp-ESLint for enhanced code analysis!

I am currently in the process of updating a Gulp task that uses gulp-tslint to now use gulp-eslint. The code snippet below outlines the changes I need to make: const { src } = require('gulp'); const config = require('./config'); const ...

Mastering Typescript lookup types - effectively limit the properties included in a merge operation with the Partial type

Exploring lookup types, I'm interested in creating a safe-merge utility function that can update an entity of type T with a subset of keys from another object. The objective is to leverage the TypeScript compiler to catch any misspelled properties or ...

Create type definitions for React components in JavaScript that utilize the `prop-types` library

Exploring a component structure, we have: import PropTypes from 'prop-types'; import React from 'react'; export default class Tooltip extends React.Component { static propTypes = { /** * Some children components */ ...

Ways to implement the flow of change occurrences in the mat-select component

Seeking assistance with utilizing the optionSelectionChanges observable property within Angular Material's mat-select component. This property provides a combined stream of all child options' change events. I'm looking to retrieve the previ ...

Removing a key from an index signature in Typescript - a step-by-step guide

In my web application built with Angular, we encountered a need for a data type to store translations of strings in different languages. To address this requirement, a team member defined the following type: export class TranslatedString { [language: str ...

Is it possible for a voiceover artist to initiate API requests?

As I work on the registration feature of my application, I am faced with the requirement that email addresses must be unique in the database. Since I am responsible for the front-end development, I am considering creating a Value Object (VO) that can make ...