Typescript - How to extends model with typescript interface

Viewed 702

I have a tasks that have a common interface and some extensions:

export interface Task {
  name: string;
  id: number;
  type: taskType
}

export enum taskType {
  EVENT = 'EVENT',
  NOTES = 'NOTES',
}

These are the extensions of Task:

export interface EventTask extends Task {
  startDate: Date,
  endDate: Date
}

export interface NotesTask extends Task {
  description: string,
}

When I get the backend result I can get these array, data properties change based on type:

[{id: 1, name: 'TaskNote1', type: 'NOTES', description: 'Hello world'}, {id: 2, name: 
'TaskEvent1', type: 'EVENT', startDate: '2021-04-04', endDate: '2021-04-19'}];

How can Obtain these variable model into the Task interface? I can't find a solution

2 Answers

Your array is of type (NotesTask | EventTask)[]. You can use Type Guards to define the difference between them. A type guard is a function which rules out the possibility of a value being the wrong type.

A type guard uses the is operator to tell TypeScript which type is which.

For example:

const isNotesTask = (task: NotesTask | EventTask): task is NotesTask => {
  return task.type === taskType.NOTES
}

const isEventTask = (task: NotesTask | EventTask): task is EventTask => {
  return task.type === taskType.EVENT
}

You can use this in an if statement like this:

// Outside the if statement task is of type NotesTask | EventTask.

if (isNotesTask(task)) {
  // In here task is of type NotesTask
}

if (isEventTask(task)) {
  // In here task is of type EventTask
}

The other answer good, but I find it to have more boilerplate and be less robust than the following solution:

interface CommonTaskProperties {
  name: string;
  id: number;
}

export type Task =
    | {type: 'EVENT', startDate: Date, endDate: Date} & CommonTaskProperties
    | {type: 'NOTES', description: string} & CommonTaskProperties

Then you can do this:

const task: Task = {type: "NOTES", description: "This is a notes task."};

switch (task.type) {
    case 'EVENT':
        // Handle event task here
        break;
    case 'NOTES':
        // Handle notes task here
        break;
}

Inside each of the cases, the type will appropriately be inferred without having to use explicit type guards or assertions.

Related