Typescript, accurately represent the behavior of a class method
I have few interfaces and a class that accepts an optional options object:
interface asObject { a: number, b: number }
interface options { returnAs: string; }
interface IA {
go(): string;
go(o: options): string | asObject;
}
class A implements IA {
public go(o?: options): string | asObject {
if(o&& o.returnAs && typeof o.returnAs === 'string') {
switch(o.returnAs) {
case 'object':
return { a: 5, b: 7 };
default:
return 'string';
}
}
}
}
And the error I am getting is "the class does A
not implement the interface correctly IA
".
If I try to overload the method:
...
public go(): string;
// Notice the parameter is no longer optional, ? removed.
public go(o: options): string | asObject { /* implementation as above */ }
...
Now I get: "The overload signature is incompatible with the function implementation."
I know I can just remove the overloaded signature on the interface IA
and remove the overloaded method in the class A
:
// Interface IA, notice the parameter is now optional, ? added.
go(o?: options): string | asObject;
// Class A
public go(o?: options): string | asObject { /* implementation as above */ }
Let me explain:
The class A
has a method named go
, if go
no options object is provided it will return a string, but if the user provides an options object, the return value depends on returnAs
what the string or object means.
My question is:
I don't think the solution I provided accurately reflects the behavior of the method go
.
Is there a way to keep the exact behavior for using typescript and not get errors like I did in my first 2 attempts above?
When I say exact behavior, I mean:
I'm looking for a way where typescript can output AObject
like string
:
var AObject = new A().go();
And he will be able to conclude AObject
as either string
, or asObject
:
var AObject = new A().go({ returnAs: 'object|string' });
I'm not 100% sure about typescript, in which case I'd be happy for a suggestion.
source to share
The easiest way is to declare the result A.go
as any
:
public go(o?: options): any {
Or declare a functional interface:
interface asObject { a: number, b: number }
interface options { returnAs: string; }
interface IGoFunction {
(): string;
(o: options): string | asObject;
}
interface IA {
go: IGoFunction;
}
class A implements IA {
go = <IGoFunction>function (o?: options): string | asObject {
if (o && o.returnAs && typeof o.returnAs === 'string') {
switch (o.returnAs) {
case 'object':
return { a: 5, b: 7 };
default:
return 'string';
}
}
}
}
In fact, you don't even need to declare named interfaces:
class A {
go = <{
(): string;
(o: options): string | asObject;
}>function (o?: options): string | asObject {
...
The downside is that the function is added to every instance A
, but you can explicitly add it to the prototype:
class A {
go: {
(): string;
(o: options): string | asObject;
};
}
A.prototype.go = function (o?: options): any {
...
source to share