In the base interface, interfaces cannot add to the types of members directly. Instead, you can utilize an intersection type:
export interface Module {
name: string;
data: any;
structure: {
icon: string;
label: string;
}
}
export type DataModule = Module & {
structure: {
visible: boolean;
}
}
export type UrlModule = Module & {
structure: {
url: string;
}
}
let urlModule: UrlModule = {
name: "",
data: {},
structure: {
icon: '',
label: '',
url: ''
}
}
These intersection types should function similarly to interfaces, as they can be implemented by classes and validated when assigning object literals to them.
An alternative approach using interfaces would involve a bit more verbosity and require a type query to retrieve the original type of the field, along with another intersection:
export interface DataModule extends Module {
structure: Module['structure'] & {
visible: boolean;
}
}
export interface UrlModule extends Module {
structure: Module['structure'] & {
url: string;
}
}
For a more verbose option (though easier to comprehend in some ways), you could define a separate interface for structure:
export interface IModuleStructure {
icon: string;
label: string;
}
export interface Module {
name: string;
data: any;
structure: IModuleStructure;
}
export interface IDataModuleStructure extends IModuleStructure {
visible: boolean;
}
export interface DataModule extends Module {
structure: IDataModuleStructure;
}
export interface IUrlModuleStructure extends IModuleStructure {
url: string;
}
export interface UrlModule extends Module {
structure: IUrlModuleStructure;
}
let urlModule: UrlModule = {
name: "",
data: {},
structure: {
icon: '',
label: '',
url: ''
}
}
Edit
Following @jcalz's suggestion, we might also consider making the module interface generic and passing the appropriate structure interface:
export interface IModuleStructure {
icon: string;
label: string;
}
export interface Module<T extends IModuleStructure = IModuleStructure> {
name: string;
data: any;
structure: T;
}
export interface IDataModuleStructure extends IModuleStructure {
visible: boolean;
}
export interface DataModule extends Module<IDataModuleStructure> {
}
export interface IUrlModuleStructure extends IModuleStructure {
url: string;
}
export interface UrlModule extends Module<IUrlModuleStructure> {
}
let urlModule: UrlModule = { // Alternatively, we could simply use Module<IUrlModuleStructure>
name: "",
data: {},
structure: {
icon: '',
label: '',
url: ''
}
}