Reputation: 3674
Lets say I have an two classes that inherit from some interface: (stripped down for simplicity)
interface IVehicle {}
class Car implements IVehicle {
constructor(public color: string) {}
}
class Helicopter implements IVehicle {}
I now have a function which given a instance (which type extends IVehicle
) will return a new instance of the same type
function clone<T extends IVehicle>(element: T): T {
if (element instanceof Car) {
return new Car(element.color);
}
...
return element;
}
clone(new Helicopter())
However I get a error around return new Car(...)
saying:
'Car' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'IVehicle'
Which seems untrue as the instanceof
should asset that T
is of subtype Car
right?
After that I tried casting the return type to of type T
with return new Car(element.color) as T;
.
But this now produces this error:
Conversion of type 'Car' to type 'T' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
And includes the first error.
Has TypeScript caught a legitimate types issue or is this an error? Am I missing something from the type constraint? How can I fix this error?
Upvotes: 0
Views: 59
Reputation: 26064
If you intend to clone instances of IVehicle, I think adding this operation to the interface itself is a good solution.
interface IVehicle<ActualType> {
clone(): ActualType;
}
class Car implements IVehicle<Car> {
constructor(public color: string) {}
clone(): Car {
return new Car(this.color);
}
}
class Helicopter implements IVehicle<Helicopter> {
clone(): Helicopter {
return new Helicopter();
}
}
const h = new Helicopter();
const hClone = h.clone();
Update
If you cannot modify source of Vehicles, and subclasses form a closed set, you can use method overloading
interface IVehicle {}
class Car implements IVehicle {
constructor(public color: string) {}
}
class Helicopter implements IVehicle {}
function clone(element: Car): Car;
function clone(element: Helicopter): Helicopter;
function clone(element: IVehicle): IVehicle {
if (element instanceof Car) {
return new Car(element.color);
} else if (element instanceof Helicopter) {
return new Helicopter();
} else {
throw new Error("Unknown subclass of IVehicle passed to clone " + element)
}
}
const h = new Helicopter();
const h1 = clone(h);
Upvotes: 1