How to avoid optional chaining in TS?

Hey there, I can't remember the recommended pattern for this and I actually feel like Theo covered it once in a video but: How do we avoid an optional chaining scenario and implement clean optional/interchangeable interfaces. For example:
interface Tab {
name: string;
type: 'projects' | 'todos' | 'about';
data: {
id?: string;
name: string;
url?: string;
}
}
interface Tab {
name: string;
type: 'projects' | 'todos' | 'about';
data: {
id?: string;
name: string;
url?: string;
}
}
Here we have this type for a Tab on a web app, the tab could be of various possible types and each type has it's own possible 'data' inside it. For example, data.id makes sense for projects and todos but not about, maybe 'name' applies to all three but then url only applies to about. Instead of defining all possible options inside the data type (and using nullish options) AND ALSO avoiding this situation: interface ProjectsDataType and interface TodosDataType and doing data: ProjectsDataType | TodosDataType etc. Is there a better way to optionally refer to a bunch of possible data interfaces? Or is optional chaining the way to do this? I'm aware of doing things like data: T and essentially specifying the data type everytime you call on the interface with Tab<T> but I feel like there's a slightly better situation here? Help pls!
2 Replies
Zougui
Zougui3mo ago
What you're looking for is a discriminated union. Here's an example:
interface ProjectTab {
type: 'projects';
data: {
url: string;
name: string;
};
}

interface TodoTab {
type: 'todos';
data: {
id: string;
name: string;
};
}

type Tab = ProjectTab | TodoTab;
interface ProjectTab {
type: 'projects';
data: {
url: string;
name: string;
};
}

interface TodoTab {
type: 'todos';
data: {
id: string;
name: string;
};
}

type Tab = ProjectTab | TodoTab;
Then you just check for the value of type and data will have the expected type
boommachine
boommachine3mo ago
thanks insightful

Did you find this page helpful?