Peeling off the layers of an array declared as const to reveal its mutable version without being restricted to tuples

I'm facing a challenge with an array declared as as const:

// example of a simple mock class
class Child { _ = "" }

const child = new Child();

const schema = [[child], child] as const; // readonly [readonly [Child], Child];

This array represents a union of types, where it can be understood as an array of Child or another array of Child (with one more level of nesting). Essentially, I aim to convert the type of schema into (Child | Child[])[].

type T = UnwrapAsConstArray<readonly [readonly [Child], Child]> // => (child | Child[])[]

I'm finding it challenging to devise the logic for this transformation. My attempt can be viewed here, but it's not functioning as desired, as shown at the bottom.

For those willing to give it a try, here's a playground where you can experiment with potential solutions, along with some test cases and the expected outcomes.

It's important to note that I require the solution to be recursive and applicable for any level of nesting.

For a related question, check out Create a type tuple from an array without "as const". I'm seeking the "opposite" of that scenario, which I believe is achievable.

Answer №1

Here is my proposed approach:

type UnwrapAsConstArray<T> = 
  T extends readonly any[] ? UnwrapAsConstArray<T[number]>[] :
  { -readonly [K in keyof T]: UnwrapAsConstArray<T[K]> }

This is quite similar to a basic DeepMutable<T> type defined as:

type DeepMutable<T> = { -readonly [K in keyof T]: DeepMutable<T[K]> }

The main difference is the check for readonly any[], where we extract the union of its element types T[number], apply UnwrapAsConstArray on that, and generate a regular array.

These pass all the tests provided in the example:

type UnwrappedSchema = UnwrapAsConstArray<typeof schema>;
//   ^? type UnwrappedSchema = ({ _: string; } | { _: string; }[])[]
// structurally equivalent but not written as `(Child | Child[])[]`

type Test01 = Assert<Equals<
  UnwrappedSchema, (Child | Child[])[]>>; // passes

type Test02 = Assert<Equals<UnwrapAsConstArray<
  readonly [readonly [1], 2]>, (2 | 1[])[] // passes
>>;

type Test03 = Assert<Equals<UnwrapAsConstArray<
  readonly [1, readonly [2, 3], readonly [readonly [4, 5, 6]]]>, 
  (1 | (2 | 3)[] | (4 | 5 | 6)[][])[] // passes
>>;

type Test04 = Assert<Equals<UnwrapAsConstArray<
  readonly []>, never[] // passes
>>;

type Test05 = Assert<Equals<UnwrapAsConstArray<
  readonly [1]>, 1[] // passes
>>;

It's important to note that this function will remove read-only properties from any object types like Child included in the input, without an end condition. Any type that doesn't survive the mapping process will be transformed (functions will become {}). Extra logic needs to be added to handle such cases if necessary.

Click here to view the code in a TypeScript playground

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 are the recommended guidelines for using TypeScript effectively?

When facing difficulties, I have an array with functions, such as: this._array = [handler, func, type] How should I declare this private property? 1. Array<any> 2. any[] 3. T[] 4. Array<T> What is the difference in these declarations? ...

Why is it that Eslint Plugins are present locally, but I am unable to compile them on the server?

The ESlint command is encountering an error on the server during the build process: ESLint couldn't find the plugin "@typescript-eslint/eslint-plugin". However, everything is running smoothly on the local environment. Since there is no global eslint ...

Caution in version 3.5.1 of Vue Router: The tag prop of `<router-link>` is now obsolete and has been eliminated in Vue Router 4

After updating the node packages of our Vue.js app, a warning is now appearing in the browser console: [vue-router] The 'tag' prop has been deprecated and removed in Vue Router 4. To remove this warning, use the v-slot API. Check out the migrat ...

What might be causing the component in Angular and TypeScript to have trouble reading the data returned by the service?

I've been working on this for hours without success. I created a web API get method that returns a simple array. public Hero[] getHeroes() { return new Hero[] { new Hero { Id = 1, Name = "Hunain Hafeez-1" }, ...

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 ...

Tips for creating Material UI elements using React and Typescript

I am looking to extend a component from the Material UI library by creating a custom component that encapsulates specific styles, such as for a modal wrapper. However, I feel that my current solution involving the props interface may not be ideal. Is the ...

Oops! The system encountered a problem: the property 'modalStack' is not recognized on the type 'NgxSmartModalService'. Maybe you meant to use '_modalStack' instead?

Currently, I'm facing an issue while attempting to run ng build --prod in my Angular 6 project. I have also incorporated the NgxSmartModal package for handling modals. Unfortunately, the build process is failing and I can't seem to figure out why ...

Angular2: Once user is authenticated, navigate to specific routes

I've developed an admin panel with a router for page navigation. The layout of the admin panel includes a sidebar (Component), header (Component), and content (Component). Within the content, I have inserted <router-outlet></router-outlet> ...

Utilizing regular expressions to search through a .md file in JavaScript/TS and returning null

I am currently using fs in JavaScript to read through a changelog.MD file. Here is the code snippet: const readFile = async (fileName: string) => { return promisify(fs.readFile)(filePath, 'utf8'); } Now I am reading my .md file with this fu ...

What is the best way to find a partial string match within an array of objects when using Jest?

I am currently utilizing the following versions: Node.js: 9.8.0 Jest: 22.4.2 A function called myFunction is returning an array structured like this: [ ... { id: 00000000, path: "www.someUrl.com/some/path/to" } ... ] I ...

Display an API generated popup list using Vue's rendering capabilities

I'm attempting to generate a pop-up within a displayed list using custom content retrieved from an API request. Currently, my code looks like this: <template> <div class="biblio__all"> <a v-for="i in items" ...

Encountering an error when attempting to include React TypeScript onChange with a Material UI switch component

I'm working on implementing a show/hide functionality using a switch. I want the component to be displayed when the switch is turned on and hidden when it's turned off. Here's the code I've written: const VirtualEventSection = ({ con ...

Need an email verification request through firebase

Need help with sending email verification upon user sign up. Here is the code in provider/user.ts: onCreate(form: NgForm) { var user = new User(); user.name = form.value.name; user.email = form.value.email; user.contact = form.value.contact; if(form.valu ...

To subscribe to the display of [Object Object], you cannot use an *ngIf statement without being inside an *ngFor loop

In my angular Quiz project, I have a functionality where every user can create quizzes. I want to display all the quizzes that a logged-in user has completed. Here is how I attempted to achieve this: // Retrieving user ID and category ID inside Re ...

Exploring Angular routing with parameters and extracting parameter values

In an email, a user will click on a link that looks like this: do-something/doSomething?thing=XXXXXXXXXXX I'm trying to figure out how to define the route in the router and subscribe to get params. Here's what I currently have set up in the rout ...

The element is implicitly assigned an 'any' type due to the fact that an expression of type 'any' cannot be used to index types in nodejs and solidity

I am in need of setting networks in my contract using NodeJS and TypeScript. Below is the code I have written: let networkId: any = await global.web3.eth.net.getId(); let tetherData = await Tether.networks[networkId]; Unfortunately, I encountered ...

What is the best way to organize the data retrieved from the api into a map?

In my search page component, I display the search results based on the user's query input. Here is the code snippet: "use client"; import { useSearchParams } from "next/navigation"; import useFetch from "../hooks/useFetch&qu ...

Leveraging CSS attribute selectors within JSX using TypeScript

With pure HTML/CSS, I can achieve the following: <div color="red"> red </div> <div color="yellow"> yellow </div> div[color="red"] { color: red; } div[color="yellow"] { color: yellow; ...

VScode has identified potential problems with modules that utilize Angular Ivy, however, this should not cause any major issues

Encountering a problem with using Angular in VSCode, where there seems to be no ngModules due to AngularIvy. The error message indicates an issue with 'CommonModule' not being recognized as an NgModule class: [{ "resource": "[...]src/app/com ...

What is the best way to include a non-data custom attribute in a TSX template without any value?

Currently, I am working on a React component with Typescript. The initial code looks like this.... const NameFormatter = React.createClass({ render() { return ( <div> <div className="dataset-name"> ...