I have a minimal reproduction of a recursive type problem I've encountered. Here's some typescript.
type Cloneable = string | number | Cloneable[];
function clone<T extends Cloneable>(arg: T): T {
if (Array.isArray(arg)) return arg.map(e => clone(e));
else return arg;
}
Type 'Cloneable[]' is not assignable to type 'T'. 'Cloneable[]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Cloneable'.
My intent is that return type of clone should be identical to its argument. In practice, it's going to involve some proxy stuff, but that's not relevant here. Is there a way I can type this correctly without resorting to any?
To explain the error message, let's merely declare the type of your
clonefunction without bothering to define it:Here's an example of something that conforms to
Cloneable, because it is an array containing strings and numbers, but it also happens to have an extra function attached to it:Sure enough, we can pass it to
clone:And TS now believes the type of
cloneTestwill be the complete type, so in your editor you'll see this pop-up:Does your implementation ensure that the returned clone reproduces any extra bells and whistles that any random subtype of
Tmight have? No. And what would it mean, in general terms, to clone such things? How do you ensure a deep clone of functions and properties that you have no understanding of?In other words, TS is pointing out the truth: your function cannot do what it claims to do.