Aure77
Aure77

Reputation: 3209

How to use polymorphism with generics in Typescript?

If I have an object of type that take a generic T that extends another interface/type. How can I type an object/method param to allow any subtype generic.

An exemple will be easier to understand:

type Options = {
  id: string;
};

type Task<O extends Options = Options> = {
  start: (options: O) => void;
};

const t1: Task<Options> = {
  start: ({ id }) => {
    console.log(id);
  }
};
const t2: Task<{ id: "static"; foo: true } & Options> = {
  start: ({ id, foo }) => {
    console.log(id, foo);
  }
};

const TaskManager = {
  registerTask: (task: Task) => { // maybe I miss something here, I would like to avoid Task<any>
    // use task
  }
};
TaskManager.registerTask(t1); // Ok
TaskManager.registerTask(t2); // ERROR: Argument of type 'Task<{ id: "static"; foo: true; } & Options>' is not assignable to parameter of type 'Task<Options>'.

How can I modify registerTask to allow any Task that accept Task<T extends Options>

Can someone help me to fix this typing issue ?

EDIT: Also, If TaskManager store an array of Task, how to type this array with generics ?

class TaskManager {
  private static tasks: Task[] = [];
  public static registerTask = <O extends Options>(task: Task<O>) => {
    TaskManager.tasks.push(task); // ERROR: Argument of type 'Task<O>' is not assignable to parameter of type 'Task<Options>'
  };
}

I need to provide generic at class level ?

Temporary solution is to declare an array of Task<any>:

private static tasks: Task<any>[] = [];

to allow any subtypes.

Upvotes: 2

Views: 1061

Answers (1)

jcalz
jcalz

Reputation: 327994

You can make TaskManager.registerTask() a generic method like this:

const TaskManager = {
  registerTask: <O extends Options>(task: Task<O>) => {

  }
};

which will then cause the compiler to infer O when you call it:

TaskManager.registerTask(t1); // okay
// (property) registerTask: <Options>(task: Task<Options>) => void

TaskManager.registerTask(t2); // okay
// (property) registerTask: <{ id: "static"; foo: true; } & Options>(
//   task: Task<{ id: "static";  foo: true; } & Options>
// ) => void

Playground link to code

Upvotes: 2

Related Questions