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

Utilizing Google Closure Library with Angular 6

I am looking to integrate the google closure library into my angular 6 application. To achieve this, I have utilized the following commands: npm install google-closure-compiler and npm install google-closure-library. My application can be successfully co ...

Cannot locate module using absolute paths in React Native with Typescript

I recently initiated a new project and am currently in the process of setting up an absolute path by referencing this informative article: https://medium.com/geekculture/making-life-easier-with-... Despite closely following the steps outlined, I'm en ...

Is there a way to trigger validation with a disabled property?

My form element is set up like this: <input type="text" id="country" formControlName="Country" /> The form group looks like this: this.myForm = this.formbuilder.group({ 'Country': [{ value: this.user.Country, disabled: this.SomeProperty= ...

Importing custom pipes in Angular 2 becomes a bit tricky when working with data grouping

As a newcomer to Angular JS, I have recently started using Angular 2 for a project of mine. Here is an example of my JSON data: "locations": [ { "id": "ASS", "name": "test center", "city": "Staten Island", "zip": ...

Troubleshooting the order of Javascript execution in bundled TypeScript applications

I encountered an issue with the execution order of generated JavaScript code during bundling, resulting in an error message when everything is bundled together. An error occurred: [$injector:nomod] Module 'app.demo' is not accessible! This cou ...

Enhancing the NextPage Typescript Type: A Step-by-Step Guide

I'm working on a NextJS dashboard with role-based access control and I need to pass an auth object to each page from the default export. Here's an image showing an example of code for the Student Dashboard Home Page: Code Example of Student Dashb ...

Mismatched URLSearchParam type {property: value} does not match with undefined and Argument Type {property: value} cannot be assigned to {property: value}

Every time I try to run the code below, I encounter this error. The code is supposed to take a refresh token as input and make a POST request using URLSearchParams to a specific URL. However, I keep getting an error related to URLSearchParams. https://i.s ...

An issue arises in Typescript when attempting to pass an extra prop through the server action function in the useForm

I am struggling with integrating Next.js server actions with useFormState (to display input errors on the client side) and Typescript. As per their official documentation here, they recommend adding a new prop to the server action function like this: expo ...

The outcome of using Jest with seedrandom becomes uncertain if the source code undergoes changes, leading to test failures

Here is a small reproducible test case that I've put together: https://github.com/opyate/jest-seedrandom-testcase After experimenting with seedrandom, I noticed that it provides consistent randomness, which was validated by the test (running it multi ...

Generating instances of classes using variables in Typescript

Are there methods to modify the below piece of code in order for it to be compatible with Typescript? public shops: string[] = [ "AShop", "BShop", "CShop", ]; this.shops.forEach((shop, index) => { let instance = new window[shop](index ...

What is the best way to see if a variable is present in TypeScript?

I am facing an issue with my code that involves a looping mechanism. Specifically, I need to initialize a variable called 'one' within the loop. In order to achieve this, I first check if the variable exists and only then proceed to initialize it ...

Updating the Angular2 function in the main app component causes the current component to be reset

I developed an application that has the following structure: app.component.ts import { Component } from 'angular2/core'; import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router'; import { NgClass } from &apos ...

Transforming res.json() into an Array of Objects

I am dealing with a Java webservice that outputs a list of Json objects with specific properties: public class Oferta { private int id; private String categoria; private String descricao_oferta; private String anunciante; private double valor; private boo ...

Converting typescript path aliases into local file paths

Just dipping my toes into typescript and grappling with module resolution. The problem seems straightforward (or so I think), but there's something off about my tsconfig.json. If my folder structure looks like this: + release + definitions + ...

Whenever signing in with Next Auth, the response consistently exhibits the values of "ok" being false and "status" being 302, even

I am currently using Next Auth with credentials to handle sign-ins. Below is the React sign-in function, which can be found at this link. signIn('credentials', { redirect: false, email: email, password: password, ...

Beware of potential issues with FontAwesomeIcon when incorporating it into a Typescript file

I'm currently working with NextJS and Typescript. I encountered an issue when trying to import FontAwesomeIcon like this <FontAwesomeIcon icon={faCheck as any} /> as it triggered a warning in the console stating "Warning: FontAwesomeIcon: Suppor ...

Implementing Generic Redux Actions in Typescript with Iterable Types

Resolved: I made a mistake by trying to deconstruct an object in Object.assign instead of just passing the object. Thanks to the assistance from @Eldar and @Akxe, I was able to see my error in the comments. Issue: I'm facing a problem with creating a ...

Remove focus from input field after submitting in a project using Typescript and React with react-hook-form

I'm currently working on a TS-React project and encountering an issue with barcode scanning in my inputs. I am using react-hook-form along with the useForm Hook. The form consists of one input-text field and a submit button, both within a global form. ...

Tips for making the onChange event of the AntDesign Cascader component functional

Having an issue with utilizing the Cascader component from AntDesign and managing its values upon change. The error arises when attempting to assign an onChange function to the onChange property of the component. Referencing the code snippet extracted dire ...

Asynchronous and nested onSnapshot function in Firestore with await and async functionality

I'm facing an issue with the onSnapshot method. It seems to not await for the second onsnapshot call, resulting in an incorrect returned value. The users fetched in the second onsnapshot call are displayed later in the console log after the value has ...