Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to declare an optional generic type in an interface?

I have this interface:

export interface AlertInteraction<T> {
  id: AlertId;
  data: T;
  buttonId: AlertButtonId;
}

But there are times where data is not needed. I know I can declare it as data: T but I would like to know if I could something like this:

export interface AlertInteraction<T> {
  id: AlertId;
  data: T;
  buttonId: AlertButtonId;
}

export interface AlertInteraction {
  id: AlertId;
  buttonId: AlertButtonId;
}

So, if I give T then I assume I want to access the data, if no then assume it does not exist. Is it possible?

like image 298
distante Avatar asked Nov 17 '19 16:11

distante


People also ask

What is a generic interface in Java?

Like classes, interfaces also can be generic. A generic interface has generic type parameter list in an angle brackets <> following the name of the interface: interface interfaceName<T> { // ... } This make the type parameter T visible to all members of the interface.

How to create a generic type parameter list for an interface?

A generic interface has generic type parameter list in an angle brackets <> following the name of the interface: interface interfaceName<T> { // ... } Code language: TypeScript (typescript) This make the type parameter T visible to all members of the interface. The type parameter list can have one or multiple types. For example:

What is a generic interface in typescript?

Summary: in this tutorial, you will learn how to develop TypeScript generic interfaces. Like classes, interfaces also can be generic. A generic interface has generic type parameter list in an angle brackets <> following the name of the interface:

Can a generic interface inherit a non-generic interface?

The rules of inheritance that apply to classes also apply to interfaces: Generic interfaces can inherit from non-generic interfaces if the generic interface is covariant, which means it only uses its type parameter as a return value.


1 Answers

In order to achieve that we need to use conditional types.

type AlertId = string; // just example type alias
type AlertButtonId = string; // just example type alias
type AlertInteraction<T = undefined> = {
  id: AlertId;
  buttonId: AlertButtonId;
} & (T extends undefined ? {} : {
  data: T;
})


// example usage
// no need for data (T is undefined)
const a: AlertInteraction = {
  id: '1',
  buttonId: '1'
}
// data prop is required 
const b: AlertInteraction<number> = {
  id: '1',
  data: 1,
  buttonId: '1'
}

Most important is this part:

& (T extends undefined ? {} : {
  data: T;
})

We join conditionally or type {} if T is assignable to undefined, or we join {data: T} for other type. In result if we don't set the generic, it is by default undefined and our type constructor computes into:

// For T == undefined
{
  id: AlertId;
  buttonId: AlertButtonId;
} & {}
// what is equal to just:
{
  id: AlertId;
  buttonId: AlertButtonId;
}

For a case where argument(generic) is provided as something different then undefined, then the final type has a form:

// For T != undefined
{
  id: AlertId;
  buttonId: AlertButtonId;
} & { data: T}
// which is equal to
{
  id: AlertId;
  buttonId: AlertButtonId;
  data: T;
}

Where T is type given by argument of type constructor. To also clear the information - Type constructor = any type definition with an generic argument.

like image 87
Maciej Sikora Avatar answered Oct 18 '22 15:10

Maciej Sikora