Is it possible for an object's property specified in a TypeScript interface to also incorporate the interface within its own declaration?

While it may seem like an unusual request, in my specific scenario, it would be a perfect fit.

I am working with an object named layer that looks something like this:

const layer = {
    Title: 'parent title',
    Name: 'parent name',
    Layer: {
        Title: 'child title'
    }
}

This object has a mandatory property - Title, and optional properties - Name and Layer.

If the Layer property exists, it follows the same structure as described above (it could have additional nested layers).

Now, I need to create an interface for this object, and I'm considering using the following structure:

interface LayerInterface {
    Title: string;
    Name?: string;
    Layer?: LayerInterface;
}

My question is whether I can use LayerInterface as the type of the Layer property which is defined within the LayerInterface itself.

I'm simply wondering if this approach is valid or if there's a simpler solution available.

Answer №1

Absolutely, your recursive interface definition seems to be functioning as intended:

interface LayerInterface {
    Title: string;
    Name?: string;
    Layer?: LayerInterface;
}

The code compiles without any errors, and you can clearly see how it enforces specific types for nested properties within the object.

function processLayer(layer: LayerInterface) {}

processLayer(layer); // works fine

const problematicLayer = { Title: "", Name: "", Layer: { Title: 123, Name: false } }
processLayer(problematicLayer); // encounters an error
// --------> ~~~~~~~~
/* Argument of type
  '{ Title: string; Name: string; Layer: { Title: number; Name: boolean; }; }' 
  is not assignable to parameter of type 'LayerInterface'. 
*/

In this scenario, the incorrect data types in the nested Title and Name properties cause the problematicLayer object to fail the type check against LayerInterface.


This approach is not unusual at all; many interfaces and classes commonly utilize this recursive structure. For instance, tree-like structures like the DOM often have similar type definitions that reference themselves internally.

For example, in the DOM, each Element node has a children property which holds an array-like collection of Element nodes, giving you the ability to write recursive element-processing functions seamlessly:

function processElement(elem: Element) {
    console.log(elem.nodeName);
    for (let i = 0; i < elem.children.length; i++) {
        processElement(elem.children[i]);
    }
}

Regarding documentation:

Formal documentation regarding this aspect of TypeScript can be found in the (somewhat outdated) TypeScript Spec:

Classes and interfaces are capable of self-referencing in their internal structure, thus generating recursive types with infinite nesting. A simple illustration is shown by the following type:

interface A { next: A; }

which reflects an infinitely embedded sequence of 'next' elements.

A similar concept applies to type aliases, as illustrated in the relevant section of the TypeScript handbook:

The ability to refer back to itself via a property is also applicable to type aliases:

type Tree<T> = {
   value: T;
   left: Tree<T>;
   right: Tree<T>;
}

Hopefully, this information proves helpful; best of luck with your coding endeavors!

Link to code on 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

Attach [!hidden] to a dropdown menu choice using Angular 2

How can I implement a show/hide feature for a select box in Angular 2+? Here's what I have so far: <select> <option disabled selected>Flow progress</option> <option *ngFor='let flow of flows'>{{flow}}< ...

It seems like there is an issue with your network connection. Please try again

Recently, I made the switch to EndeavourOS, which is based on Archlinux. I completed all my installations without any issues and attempted to create a new NestJs project after installing NVM, Node's latest version, and the NestJs/cli. However, when I ...

Learn how to restrict input to only specific characters in an input box using Angular2 and validations

Is it possible to restrict user input in an input box to only specific characters such as '7' and '8'? I tried adding validations with attributes like type="number", min="7" and max="8", but even then other keys can be inserted before v ...

`As the input value for these methods`

I am encountering an issue when trying to pass in this.value as a method argument. The field values are all strings and the constructor arguments are also all strings, so I don't understand why it's not working. When I attempt to pass in this.cla ...

Enhanced hierarchical organization of trees

I came across this code snippet: class Category { constructor( readonly _title: string, ) { } get title() { return this._title } } const categories = { get pets() { const pets = new Category('Pets') return { ge ...

Typescript - Troubleshooting undefined error with static variables

My node API app is developed using express and typescript. The static variable of the Configuration Class is initialized with required configuration before starting the server. However, when I try to use this static variable in a separate TypeScript class ...

Exploring the Capabilities of TypeScript 1.8 in Visual Studio 2017

Recently, I've encountered an issue with my Visual Studio project that was created using TypeScript 1.8 in Visual Studio 2015. Upon upgrading to Visual Studio 2017 and attempting to open the project in the new IDE, I noticed that the TypeScript versio ...

Unable to set a breakpoint within Angular constructor or OnInit method

I am currently facing an issue with my Angular application where breakpoints set in F12 tools in Chrome or IE are not working. I have a simple test case below: export class LoginComponent implements OnInit { message: string; constructor(private r ...

Is it possible to conditionally trigger useLazyQuery in RTK Query?

Is it possible to obtain trigger from useLazyQuery conditionally? const [trigger] = props.useLazySearchQuery(); My objective is to retrieve trigger only when useLazySearchQuery is provided in the props. One way I can achieve this is by using const [ ...

The ngOnChanges lifecycle hook does not trigger when the same value is updated repeatedly

Within my appComponent.ts file, I have a property called: this._userMessage Afterwards, I pass it to the childComponent like so: <child-component [p_sUserMessage]='_userMessage'></child-component> In the childComponent.ts file: @ ...

Look for identical values within a nested array

My data consists of a nested array where each element has a property called name, which can only be either A or B. I need to compare all elements and determine if they are all either A or B. Here is an example of the input: [ { "arr": { "teach ...

Tips for customizing the legend color in Angular-chart.js

In the angular-chart.js documentation, there is a pie/polar chart example with a colored legend in the very last section. While this seems like the solution I need, I encountered an issue: My frontend code mirrors the code from the documentation: <can ...

What is the proper way to manage the (ion select) OK Button?

Hey there, I'm working with an Ionic select directive and I need to customize the functionality of the 'OK' button. When I click on it, I want it to call a specific function. I'm aware of the (ionChange) event, but that only triggers w ...

Eliminate a descendant of a juvenile by using the identification of that specific juvenile

Here is the current structure I'm working with: I want to figure out how to eliminate any field that has the id 3Q41X2tKUMUmiDjXL1BJon70l8n2 from all subjects. Is there a way to achieve this efficiently? admin.database().ref('UsersBySubjects&ap ...

Adding animation to rows in ngx-datatable: A Guide

I am looking to stack the rows of my data table (ngx) one after the other in a vertical fashion. I want to incorporate [@datatableAnimation], but I'm unsure where to place it. When adding it to <ngx-datatable [@datatableAnimation]>, it only af ...

Utilize the imported function from <Script> within NextJS

When working with vanilla JS, I am able to include a script like this: <head> <script src="https://api.site.com/js/v1/script.js"></script> </head> and then create an instance of it using: const fn = ScriptJS(); I can t ...

Tips for utilizing ng class within a loop

Having some trouble with my template that loops through a JSON file using json server. The issue I'm facing is related to correctly applying ng class when clicking on icons. Currently, when I click on an icon, it adds a SCSS class but applies it to al ...

Should the null-forgiving operator be avoided when using `useRef`?

Is the following code snippet considered poor practice? const Component: React.FC<{}> = () => { const ref = React.useRef<HTMLDivElement>(null!); return <div ref={ref} />; } I'm specifically questioning the utilization of ...

In a Typescript Next Js project, the useReducer Hook cannot be utilized

I'm completely new to Typescript and currently attempting to implement the useReducer hook with Typescript. Below is the code I've written: import { useReducer, useContext, createContext } from "react" import type { ReactNode } from &q ...

TS18047 jest error: "object may be null"

I'm currently working on a test (jtest) for an angular component, but it keeps failing due to this particular error. Any thoughts on how to resolve this? :) it("should require valid email", () => { spectator.component.email.setValue( ...