Reputation: 2635
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.
Upvotes: 3
Views: 6995
Reputation: 164129
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
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) {
...
}
Upvotes: 4