I'm trying to create an interface that could have
export interface MenuItem {
title: string;
component?: any;
click?: any;
icon: string;
}
component
or click
to be setUse the Partial utility type to make all of the properties in a type optional, e.g. const emp: Partial<Employee> = {}; . The Partial utility type constructs a new type with all properties of the provided type set to optional. Copied!
To set default values for an interface in TypeScript, create an initializer function, which defines the default values for the type and use the spread syntax (...) to override the defaults with user-provided values.
A TypeScript Interface can include method declarations using arrow functions or normal functions, it can also include properties and return types. The methods can have parameters or remain parameterless.
Interfaces and Inheritance An interface can be extended by other interfaces. In other words, an interface can inherit from other interface. Typescript allows an interface to inherit from multiple interfaces. Use the extends keyword to implement inheritance among interfaces.
With the help of the Exclude
type which was added in TypeScript 2.8, a generalizable way to require at least one of a set of properties is provided is:
type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>
}[Keys]
And a partial but not absolute way to require that one and only one is provided is:
type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?:
Required<Pick<T, K>>
& Partial<Record<Exclude<Keys, K>, undefined>>
}[Keys]
Here is a TypeScript playground link showing both in action.
The caveat with RequireOnlyOne
is that TypeScript doesn't always know at compile time every property that will exist at runtime. So obviously RequireOnlyOne
can't do anything to prevent extra properties it doesn't know about. I provided an example of how RequireOnlyOne
can miss things at the end of the playground link.
A quick overview of how it works using the following example:
interface MenuItem {
title: string;
component?: number;
click?: number;
icon: string;
}
type ClickOrComponent = RequireAtLeastOne<MenuItem, 'click' | 'component'>
Pick<T, Exclude<keyof T, Keys>>
from RequireAtLeastOne
becomes { title: string, icon: string}
, which are the unchanged properties of the keys not included in 'click' | 'component'
{ [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys]
from RequireAtLeastOne
becomes
{
component: Required<{ component?: number }> & { click?: number },
click: Required<{ click?: number }> & { component?: number }
}[Keys]
Which becomes
{
component: { component: number, click?: number },
click: { click: number, component?: number }
}['component' | 'click']
Which finally becomes
{component: number, click?: number} | {click: number, component?: number}
The intersection of steps 1 and 2 above
{ title: string, icon: string}
&
({component: number, click?: number} | {click: number, component?: number})
simplifies to
{ title: string, icon: string, component: number, click?: number}
| { title: string, icon: string, click: number, component?: number}
Not with a single interface, since types have no conditional logic and can't depend on each other, but you can by splitting the interfaces:
export interface BaseMenuItem {
title: string;
icon: string;
}
export interface ComponentMenuItem extends BaseMenuItem {
component: any;
}
export interface ClickMenuItem extends BaseMenuItem {
click: any;
}
export type MenuItem = ComponentMenuItem | ClickMenuItem;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With