Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Is it possible to exclude "this" from Type Inference in TypeScript?

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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>)

>Solution :

One way of looking at this is that in getAllParents(obj, getParent), it’s possible for getParent to return something wider than its input type, and that it’s this wider type you want to see as an output. The this type in TypeScript is essentially "the current class type whatever it happens to be" (which is implemented as an "implicit" generic type parameter). But you wanted Category, the type returned by x => x.parent.

So we can rewrite getAllParents to explicitly account for this; obj is of type T, and getParent accepts a T, but it returns a U which is wider than T. Which means T should be constrained as T extends U:

export function getAllParents<T extends U, U>(
    obj: T, getParent: (x: T) => U) {
    let result: U[] = [];

    let parent = getParent(obj);

    while (parent != null) {
        result.push(parent);

        parent = getParent(obj);
    }

    return result;
}

That compiles cleanly, and now your usage looks like:

parents = () => {
    return getAllParents(this, x => x.parent);
    //function getAllParents<this, Category>(obj: this, getParent: (x: this) => Category): Category[]        
}

so T is inferred as this, and U is inferred as Category, and the output is Category[] as desired.

Playground link to code

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading