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