简体   繁体   中英

Typescript: Implementing a generic interface

Consider the following generic interface:

interface Extractor<T> {
  extractCandidate(): T;
  process(candidate: T): T;
}

Conceptually, each Extractor implementation is responsible for extracting a particular kind of object from some datasource. The consumer of the Extractor can extract a candidate object using extractCandidate() or, given any candidate, perform some further processing on it to get a more refined version of the object using process() .

Now, let's say I implement an Extractor for a class MyClass , like so:

class MyClass {
  a: string;
  b: string;
}

class MyExtractor implements Extractor<MyClass> {
  extractCandidate() {
    //Some dummy logic
    return new MyClass();
  }
  process(candidate) {
    //Some dummy logic
    let res = new MyClass();
    res.a = candidate.a;
    return res;
  }
}

Now let's say I instantiate MyExtractor and use it:

let myExtractor = new MyExtractor();
let processed = myExtractor.process('blah');

Question 1 : Why does this not generate a compile-time error? Based on the definition of the Extractor interface, I would expect that the compiler would not allow me to call myExtractor.process() with anything but an instance of MyClass , or at least with something that is structurally compatible.

Question 2 : How can I enforce the desired behavior? Do I just need to assert that the candidate parameter of MyExtractor.process() is of type MyClass ?

I suspect this has something to do with TypeScript's structural typing system but, after reading some related questions and the FAQ , I'm still not sure how it specifically applies here.

My Typescript version is 2.1.4.

The code you posted for MyExtractor has the following signature for the process method:

process(candidate: any): MyClass

The reason for that is that you haven't specified a type for candidate so by default it is any .
The compiler won't complain because it satisfies candidate: T (as any can be T ).

If you change your code to:

process(candidate: MyClass) {
    ...
}

Then for:

let processed = myExtractor.process('blah');

You'll get:

Argument of type '"blah"' is not assignable to parameter of type 'MyClass'

You can avoid that by using the --noImplicitAny flag which will cause the compiler to complain about:

process(candidate) {
    ...
}

Saying:

Parameter 'candidate' implicitly has an 'any' type


Edit

Candidate isn't allowed to be "anything else", it is allowed to be any (and that's the default), a good reason for that for example is for overloading:

process(candidate: string): MyClass;
process(candidate: MyClass): MyClass;
process(candidate: any) {
    ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM