Definition of generic with recursive immutability

I created a type definition to ensure immutability of types and their properties all the way down. However, when I attempt to use this with a generic type, the compiler claims that the types do not overlap, preventing me from casting an object as immutable. My goal is to utilize my existing Immutable definition with generics.

If I remove the generic or modify the immutable definition (by removing

 as T[K] extends (...args: Array<any>) => any ? never
), then the casting works with generics. This seems to be related to that line.

The part

 as T[K] extends (...args: Array<any>) => any ? never
is intended to exclude functions assigned to property K in T from being included in the immutability check, but there might be more to it since I didn't write the original code.

Below is the code snippet:


type Immutable<T> = T extends string | number | boolean | bigint | symbol | undefined | null
    ? Readonly<T>
    : T extends object
    ? { readonly [K in keyof T as T[K] extends (...args: Array<any>) => any ? never : K]: Immutable<T[K]> }
    : never;

interface IWithValue {
    value: number;
}

interface IWithSelected<TData> {
    selected: TData;
}

function f<T extends IWithValue>(
    g: (x: Immutable<IWithSelected<T>>) => void,
    x: IWithSelected<T>
) {
    // Casting error due to incompatible types
    g(x as Immutable<IWithSelected<T>>);
}

// Other function definitions...

Link to Playground for above code

Modifying the Immutable definition eliminates the error:

type Immutable<T> = T extends string | number | boolean | bigint | symbol | undefined | null
    ? Readonly<T>
    : T extends object
    ? { readonly [K in keyof T]: Immutable<T[K]> }
    : never;

interface IWithValue {
    value: number;
}

interface IWithSelected<TData> {
    selected: TData;
}

function f<T extends IWithValue>(
    g: (x: Immutable<IWithSelected<T>>) => void,
    x: IWithSelected<T>
) {
    // No errors here
    g(x as Immutable<IWithSelected<T>>);
}

Link to Playground for modified code

Answer №1

From my perspective, a : T differs greatly from readonly_A : Immutable<T>

You won't be able to simply cast a to readonly_A.

Your types may be correct, but the variables are not. What you actually need to do is to convert a to readonly_A, creating a new instance that is immutable. In order to pass it to g function, you require a readonly copy of your object.

This approach seems to be effective :

function readonlyCopy <T>(something:T) : Immutable<T>  {
    const readonlyCopy :  Immutable<T> =  JSON.parse(JSON.stringify(something))
    return readonlyCopy;
}
    

then

 g(readonlyCopy(x));

Answer №2

Alright, I believe I have grasped the concept.

Upon reviewing the Readonly definition:

type Readonly = {
readonly [P in keyof T]: T[P];
};

and your exclusion for value types being immutable:

string | number | boolean | bigint | symbol | undefined | null

I conducted tests on readonly:

type ROstring = Readonly<string>; // results in string 
type ROnumber = Readonly<number>; // results in number 
type ROboolean = Readonly<boolean>; // results in boolean 
type RObigint = Readonly<bigint>; // results in bigint 
type ROsymbol = Readonly<symbol>; // results in symbol 
type ROundefined = Readonly<undefined>; // results in undefined 
type ROnull = Readonly<null>; // results in null 

Therefore, this declaration:

readonly [P in keyof T]: T[P];

essentially maintains identity for those types. The concept of immutability is quite similar; we can eliminate exceptions:

type Immutable<T> = { readonly [K in keyof T  as T[K] extends (...args: Array<any>) => any ? never : K]: Immutable<T[K]> }

All casts are now functioning properly. (exception being f6, which should technically crash).

Even better, g(x) also operates well.

check out the playground with a special feature at the end

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

Checking the functionality of a feature with Jasmine framework in an Angular application

I am working on writing unit test cases and achieving code coverage for the code snippet below. Any advice on how to proceed? itemClick($event: any) { for (let obj of this.tocFiles) { let results = this.getchildren(obj, label); if (results) { conso ...

What steps should I take to choose esNext from the typescript menu within visual studio 2017?

When utilizing dynamic import with TypeScript in Visual Studio 2017, I encountered the following error: TS1323(TS) Dynamic imports are only supported when the '--module' flag is set to 'commonjs' or 'esNext'. I attempted to c ...

How to access saved values in WebdriverIo using Typescript

I am currently using WebdriverIO along with Typescript for automating an Android app. Scenario: Go to the Training Page Get the Session name (This value changes dynamically) I want to store the retrieved session name in a variable and then later assert ...

The Angular component seems to be failing to refresh the user interface despite changes in value

Recently, I created a simple component that utilizes a variable to manage its state. The goal is to have the UI display different content based on changes in this variable. To test this functionality, I implemented the component and used a wait function to ...

When the React Native Expo app is running, the TextInput form is covered by the keyboard

When I launch the app using expo and implement my DateFormInput component, the issue of Keyboard covering TextInput arises. Despite trying packages like "@pietile-native-kit/keyboard-aware-scrollview", "@types/react-native-keyboard-spacer", "react-native-k ...

Display a nested component post initialization in Angular

<ng-container *ngIf="isTrue; else notTrue"> <app-child [property1]="value" [property2]="value" [property3]="value" (function1)="func($event)" ></app-child> </ng-container> <ng-t ...

Inaccurate recommendations for type safety in function overloading

The TypeScript compiler is not providing accurate suggestions for the config parameter when calling the fooBar function with the 'view_product' type. Although it correctly identifies errors when an incorrect key is provided, it does not enforce t ...

It is possible that req.user may be undefined, especially when using express and passport.js

I am facing an issue with my Node.js TypeScript authentication system that utilizes passport. The problem arises when I attempt to access req.user in a specific route, resulting in the error message: Object is possibly 'undefined'. This behavio ...

Make sure to always keep all stars contained within a div element

How can I keep five stars inside a div even when the screen size is small? I have created a div with an image and I want to place five stars within that div. However, as I reduce the size of the screen, the stars come out of the box. Is there a way to en ...

Updating the Nuxt3 editing page with useFetch and $fetch for fetching data, along with a typed storage object that prevents loading issues

My journey with nuxt3 is still new. I am currently working on developing a new API-based app with nuxt3. Previous versions seemed to work "somehow," but they did not meet my expectations. Here are the things I am looking to cover, both in theory and in cod ...

Automatic completion of absolute paths in VS Code with the ability to click and view definitions through the configuration file js/tsconfig.json

In order to ensure that absolute paths function correctly, I have found that there are two key steps involved: the compilation process and configuring the code editor. I successfully managed the compilation aspect by utilizing babel-plugin-module-resolver ...

Even when I try to access the properties of the arguments object, it remains empty and has a zero

Why is the arguments object showing a length of zero when I pass parameters to the function, even though I can still access its properties? I am referring to the arguments object that is implemented by all JavaScript functions, allowing you to access the f ...

I incorporated a CSS file from another HTML document. What do you think about that?

In my work with Ionic 3 and angular 4, each HTML file is accompanied by its own CSS file. There are times when I reference a class in one HTML file that is defined in another HTML file. I have observed that this CSS class gets injected into the main.js He ...

Transform the `PascalCase` format into `strictCamelCase` in TypeScript type conversion

By utilizing the built-in Uncapitalize<S> function, you can easily convert a string like FooBar to fooBar: const fooBar: Uncapitalize<'FooBar'> = 'fooBar'; However, this method proves inadequate when dealing with class name ...

Using TypeScript with Vue to Retrieve Information from a Form

Currently, I am attempting to retrieve data from a form in Vue using TypeScript. However, when declaring the data that I intend to use with this form, it seems to be posted on the fields as shown in this screenshot: message getting posted. I am unsure ho ...

Encountering difficulties in creating a custom Response type in Express.js with TypeScript

I have encountered a TypeScript error while trying to create my own custom Response interface by extending some methods instead of using the default Response type of Express.js: The last overload resulted in the following error: Argument of type '(r ...

Ways to implement a filter pipe on a property within an array of objects with an unspecified value

Currently, I'm tackling a project in Angular 8 and my data consists of an array of objects with various values: let studentArray = [ { Name: 'Anu', Mark: 50, IsPassed: true }, { Name: 'Raj', Mark: 20, IsPassed: false }, { Na ...

Every time you try to import a config.json file in Typescript, it never fails

I have the most recent version of Visual Studio Code installed. Visual Studio Code is currently utilizing TypeScript v3.3.3. I've successfully installed the following packages via npm, both locally (save-dev) and globally: TestCafe v1.1.0 Core-JS v ...

Adding a unique font to the themeprovider within styled components: A step-by-step guide

In the process of developing a React application using material-ui styled-components along with TypeScript. The task at hand is to incorporate a custom font into my styled components, but I'm facing challenges in making it functional. My initial ste ...

Exploring Angular (5) http client capabilities with the options to observe and specify the response type as 'blob'

Situation: I'm facing a challenge in downloading a binary file from a backend system that requires certain data to be posted as JSON-body. The goal is to save this file using File-Saver with the filename specified by the backend in the content-disposi ...