Successive type label

Looking to create an object that can have either primitives or objects as properties? Avoid pitfalls like the following:

const obj: DesiredType = {
  correctProp1: 'string',
  correctProp2: 123,
  correctProp3: true,
  wrongProp4: [1, 2, 3],
  prop5: {
    wrongProp1: () => {},         
    wrongProp2: [1, 2, 3],      
    correctProp3: 'other string'
  }
}

Avoid using just Record<string, any>, as it allows functions and arrays. Likewise, steer clear of structures like:

Record<string, string | number | boolean | Record<string, any>>

This setup is problematic because anything can appear on the second level.

One attempted solution involved:

type PrimitiveOrObject = string | number | boolean | Record<string, PrimitiveOrObject>; // ts(2456) type 

DesiredType = Record<string, PrimitiveOrObject>;

However, this led to error ts(2456): Type alias "PrimitiveOrObject" circularly references itself.

Is there a method to define a type alias for objects containing primitives or objects like themselves?

Answer №1

Using a type alias generically within the same definition is not allowed, but it can be used in object type literals instead. By introducing some indirection, you can achieve the recursion you are aiming for.

type PrimitiveOrObject =
  string | number | boolean | { [key: string]: PrimitiveOrObject };

The

{ [key: string]: PrimitiveOrObject }
syntax is known as an index signature. When using a key type of string, this is essentially equivalent to Record (refer to this question for more information), and it allows you to bypass the recursion limitation that was causing issues in your original approach.

Answer №2

Adding onto Silvio's response, I'd like to emphasize that the issue is not related to indirection, object type, or index signature.

Introducing additional layers of indirection can actually exacerbate the problem:

type PrimitiveOrObject = string | number | boolean | F<PrimitiveOrObject>;
//   ~~~~~~~~~~~~~~~~~ PrimitiveOrObject circularly references itself

type F<T> = { [k: string]: T }

Similarly, combining types in certain ways can yield the same problematic behavior as with an indexed signature:

type PrimitiveOrObject = string | number | boolean | PrimitiveOrObject[];
//                                  works just fine  -------------------

In my opinion, it's helpful to think of a type alias declaration only being able to reference itself when used in conjunction with a built-in type constructor. Utility types may cause TypeScript to struggle because it lacks a universal method for determining when to cease recursive checking within the type definition. Built-in type constructors are exceptions with specific behaviors regarding recursion.

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

Webpack bundling, however, encountering issues with resolving TypeScript from the node_modules package

Hey everyone, I've been exploring various approaches to tackle this issue. We are working with two folders within a makeshift mono-repo structure (without using yarn workspace). One folder is named Mgt-Shared and the other is Server. We have set up a ...

Issue: The error message "TypeError: React.createContext is not a function" occurs when using Next.js 13 alongside Formik with TypeScript

I'm currently working on creating a form using Formik in NextJs 13 (Typescript). However, the form I designed isn't functioning properly. To troubleshoot, I added code snippets from Formik's examples as shown below. Unfortunately, both my fo ...

Unlocking the Secrets of AnimatedInterpolation Values

I have a question about how to access the value of an AnimatedInterpolation in react-native without resorting to calling private code. To achieve this, I first create an animated value and then wrap it in an interpolation like so: animated = new Anima ...

Vue3 and Typescript issue: The property '$el' is not recognized on type 'void'. What is the correct way to access every existing tag type in the DOM?

Currently, I am in the process of migrating a Vue2 project to Vue3 and Typescript. While encountering several unusual errors, one particular issue with $el has me puzzled. I have been attempting to target every <g> tag within the provided template, ...

Node.js has been giving me trouble as I try to install Inquirer

:***Hey Guys I'm Working on a TypeScript/JavaScript Calculator! ...

Adding an event listener to the DOM through a service

In the current method of adding a DOM event handler, it is common to utilize Renderer2.listen() for cases where it needs to be done outside of a template. This technique seamlessly integrates with Directives and Components. However, if this same process i ...

NodeJS and TypeScript are throwing an error with code TS2339, stating that the property 'timeOrigin' is not found on the type 'Performance'

I am currently working on a NodeJS/TypeScript application that utilizes Selenium WebDriver. Here is an excerpt from the code: import { WebDriver } from "selenium-webdriver"; export class MyClass { public async getTimeOrigin(driver: WebDriver ...

Switch up your component button in Angular across various pages

I've created a new feature within a component that includes a toolbar with multiple buttons. Is there a way to customize these buttons for different pages where the component is used? Component name <app-toolbar></app-toolbar> Component ...

Restricting a Blob to a particular data type

As seen in the GitHub link, the definition of Blob is specified: /** A file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functional ...

Tips for sorting an array of objects by multiple keys while maintaining the order of each key that comes before

I am looking to create a versatile function that can organize an array of objects based on specified keys, while maintaining the order of previous keys. Here is a sample scenario: const input = [ { a: 'aardvark', b: 'bear', c: 'c ...

find all the possible combinations of elements from multiple arrays

I have a set of N arrays that contain objects with the same keys. arr[ {values:val1,names:someName},   {values:val2,names:otherName}, ] arr2[   {values:valx,names:someNamex}, {values:valy,names:otherNamey}, ] My goal is to combine all possible c ...

I had high hopes that TypeScript's automatic type inference for constructor parameters would do the trick, but it seems to have let

You can experiment with the given code snippet at the online playground to confirm it. Consider this code: class Alpha { private beta; constructor(b: Beta) { this.beta = b; } doSomething() { ...

What is the best way to execute an Nx executor function using Node.js?

Can a customized Nx executor function be executed after the app image is built? This is the approach I am taking: "migrate-up": { "executor": "@nx-mongo-migrate/mongo-migrate:up", "options": { &q ...

Angular 8 throwing an ExpressionChangedAfterItHasBeenCheckedError when a method is called within the ngOnInit function

I encountered a common issue with an Angular template. I have a standard template for all my pages, containing a *ngIf directive with a spinner and another one with the router-outlet. The behavior and visibility of the spinner are controlled by an interce ...

The assignment of Type Program[] to a string[] is not valid

I am working with a class that contains information about different programs. My goal is to filter out the active and inactive programs, and then retrieve the names of those programs as an array of strings. Below is the structure of the Program class: ex ...

I am hoping to refresh my data every three seconds without relying on the react-apollo refetch function

I am currently working with React Apollo. I have a progress bar component and I need to update the user's percent value every 3 seconds without relying on Apollo's refetch method. import {useInterval} from 'beautiful-react-hooks'; cons ...

Error in Typescript: Function expects two different types as parameters, but one of the types does not have the specified property

There's a function in my code that accepts two types as parameters. handleDragging(e: CustomEvent<SelectionHandleDragEventType | GridHandleDragEventType>) { e.stopPropagation(); const newValue = this.computeValuesFromPosition(e.detail.x ...

How to refresh a specific component or page in Angular without causing the entire page to reload

Is there a way to make the selected file visible without having to reload the entire page? I want to find a cleaner method for displaying the uploaded document. public onFileSelected(event): void { console.log(this.fileId) const file = event.targe ...

Why use rxjs observables if they don't respond to updates?

I have an array of items that I turn into an observable using the of function. I create the observable before populating the array. However, when the array is finally populated, the callback provided to subscribe does not execute. As far as I know, th ...

Can you please provide a step-by-step guide for using socket.io with TypeScript and webpack, after installing it

Currently, I am experimenting with TypeScript in conjunction with node.js, socket.io, and webpack. To facilitate this setup, I have configured the webpack.config.js, tsconfig.json, and tsd.json files. Additionally, I acquired the typings file for socket.i ...