I have the code below as a generic function to get all parents in a Tree type of structure.
When I use it in a class though, I'm getting a Type Error.
type GetParentFunc<T> = (x:T)=>T;
function getAllParents<T>(obj:T, getParent:GetParentFunc<T>)
{
let result:T[] = [];
let parent = getParent(obj);
while ( parent != null )
{
result.push( parent );
parent = getParent(parent);
}
return result;
}
class Category
{
parent:Category;
// doesn't work
parentsBroken = () =>
{
return getAllParents(this, x => this.parent);
}
// works
parentsFixed = () =>
{
return getAllParents<Category>(this, x => this.parent);
}
}
T is getting filled in with this - and I get the following error about the x => x.parent argument.
Type 'Category' is not assignable to type 'this'.
This is easily fixable like:
getAllParents<Category>(this, x => x.parent)
getAllParents(this as Category, x => x.parent)
But I am a visual noise minimalist, and would rather spend some time so I can get rid of this noise for the foreseeable future.
Is there a way to exclude the this Type from a generic?
Like:
export function getAllParents<T exclude this>(obj:T, getParent:Func<T>)
One way of looking at this is that in
getAllParents(obj, getParent), it's possible forgetParentto return something wider than its input type, and that it's this wider type you want to see as an output. Thethistype in TypeScript is essentially "the current class type whatever it happens to be" (which is implemented as an "implicit" generic type parameter). But you wantedCategory, the type returned byx => x.parent.So we can rewrite
getAllParentsto explicitly account for this;objis of typeT, andgetParentaccepts aT, but it returns aUwhich is wider thanT. Which meansTshould be constrained asT extends U:That compiles cleanly, and now your usage looks like:
so
Tis inferred asthis, andUis inferred asCategory, and the output isCategory[]as desired.Playground link to code