0

Say I have this generic interface:

interface IProcessor<T>{
  process(param:T):T;
}

And it's implemented like this:

interface User{
  name:string;
}

class WebProcessorImplementation implements IProcessor<User>{
  process(param: User): User {
    console.log(`process user`);
    return {
      name:"User"
    }
  }
}

If I want to use an array of that generic interface, I get the complaint:

class Coordinator {
  processors:IProcessor[] //Generic type 'IProcessor<T>' requires 1 type argument(s).ts(2314)
}

Is there a way to tell Typescript everything will be ok here and that we will be passing it full implementations of this interface and the type parameter is not needed? I'm open to other approaches to solve my use case.

colinwurtz
  • 703
  • 1
  • 7
  • 23
  • 2
    TypeScript doesn't have direct support for [existential types](https://stackoverflow.com/questions/292274/what-is-an-existential-type) (see [microsoft/TypeScript#14466](https://github.com/microsoft/TypeScript/issues/14466)), which is what you'd need to express what you're talking about. But the example doesn't seem to have a motivating use case; what could you possibly do with "an `IProcessor` for some `T` I don't know"? For example, say you have a value `c` of type `Coordinator`... What can you pass to `c.processors[0].process()`? – jcalz May 11 '20 at 19:25
  • 1
    Perhaps your `IProcessor` interface needs to be fleshed out so that someone could do something with an instance of it without needing to know what `T` is. For example, `{ process(param:T):T; initialValue(): T}` would at least mean that someone could call `someProcessor.process(someProcessor.initialValue())` without knowing `T`. – jcalz May 11 '20 at 19:27
  • 1
    @jcalz thanks for the response and input. You were right, the design here was flawed which should have been a red flag. I changed the interface so that `process()` doesn't return a generic, but a standard type defined in my app. Each interface implementation is now responsible for processing its own data and returning it in a common format. – colinwurtz May 13 '20 at 13:11

2 Answers2

1

Right now you set it up such that IProcessor requires a type argument, to make it optional give it a default type ie.

interface IProcessor<T = unknown>{ 
  process(param:T):T;
}

unknown would allow anything however, If the generic types need to match a certain pattern, you can enforce that pattern at least with an extends

interface User{
  name:string;
}

interface DetailedUser{
    name: string;
    id: number;
    admin: boolean;
}

interface IProcessor<T extends User = User>{
  process(param:T):T;
}

class Coordinator {
    processors: IProcessor[] = [] //fine
    processors1: IProcessor<{}>[] = [] //error
    processors2: IProcessor<{ name?: string}>[] = [] //error
    processors3: IProcessor<User>[] = [] // fine
    processors4: IProcessor<DetailedUser>[] = [] // also fine
}

tsplaygroud

Alernativly You can always just add the type directly without changing Iprocessor

class Coordinator {
  processors: IProcessor<User>[] = []; //assuming you want User..
}

tsPlayground

lonewarrior556
  • 3,917
  • 2
  • 26
  • 55
0

To get it to compile, we could use any

class Coordinator {
  processors:IProcessor<any>[]
}

But that doesn't help if we're trying to access <T> inside Coordinator like @jcalz mentioned.

Ended up changing the interface definition to this:

type Result = {data:string}
interface IProcessor{
  process():Result;
}

Implementation

class WebProcessorImplementation implements IProcessor{
  process(): Result {
    console.log(`process something`);
    return {
      data:"User"
    }
  }
}

class Coordinator {
  processors:IProcessor[]
}
colinwurtz
  • 703
  • 1
  • 7
  • 23
  • 1
    `any` is not type safe, so someone could write `new Coordinator().processors.push({ process: (x: string) => 123 })` with no error. I'm not sure what your use case but I'd be very careful with using `any` here. As I said above, I don't know what the use case is since you can't really do anything with an `IProcessor` without knowing `T`, so I don't know what the right answer here is. – jcalz May 11 '20 at 22:33