Home > database >  How TO: Typescript nested typing A<B<GENERIC>>
How TO: Typescript nested typing A<B<GENERIC>>

Time:06-11

Is it possible to have this kind of typing in typescript ?

interface Worker<T> {
    renderChunk: (projectUrl: string, from: number, to: number) => Promise<T>;
}

interface WorkerPool<W<U>> {
    addWorker(worker: W, address: number): void;

    getIdleWorker(): W | null;
}

But I also want some type of restriction that checks that W should be an implementation of Worker, let's say you have for example an implementation of Worker as follow:

class RenderWorker implements Worker<string[]> {
    renderChunk: (projectUrl: string, from: number, to: number) => Promise<string[]> {
         return Promise.resolve(['Foo'])
    }
}

The below implementation should be acceptable by typescript:

class RenderWorkerPool implements WorkerPool<RenderWorker> {} 

however this one should not be acceptable as string is not an implementation of Worker

class RenderWorkerPool implements WorkerPool<string> {} 

CodePudding user response:

Yes, using constrained generic types, you can ensure that W extends Worker.

interface WorkerPool<W extends AaWorker<any>> {
    addWorker(worker: W, address: number): void;

    getIdleWorker(): W | null;
}

Playground Link

Because of the nested generic, if you want to reason about the type of the worker, you'll need to specify that as well:

interface WorkerPool<U, W extends AaWorker<U>> {
    addWorker(worker: W, address: number): void;

    getIdleWorker(): W | null;
}

Playground Link

But, as Bernardo Duarte points out in the comments, you might not need generics for Worker at all. Instead, just take any conforming Worker implementation with the right generic type:

interface WorkerPool<U> {
    addWorker(worker: AaWorker<U>, address: number): void;

    getIdleWorker(): AaWorker<U> | null;
}

Playground Link

(I've changed Worker to AaWorker above, just so there's no naming conflict between your Worker and the Web Worker built-in objects.)

CodePudding user response:

I think you want this:

interface WorkerPool<W extends Worker<unknown>> {
  addWorker(worker: W, address: number): void;
  getIdleWorker(): W | null;
}

W is constrained by Worker<unknown>. So W is inferred to be a Worker, and the generic T of that worker is also inferred as part of that.

class RenderWorkerPoolA implements WorkerPool<RenderWorker> {} // fine
class RenderWorkerPoolB implements WorkerPool<string> {} // Type 'string' does not satisfy the constraint 'Worker<unknown>'

Playground

  • Related