What does casting/assertion in parenthesis before brackets mean in TypeScript code?

Viewed 604

Here's a bit of code I was looking over (I'm new to TypeScript):

    // Set up decorator
    const proxy = Object.create(null) as ts.LanguageService;
    const oldLS = info.languageService;

    for (const k in oldLS) {
        (<any>proxy)[k] = function () {
            return (<any>oldLS)[k].apply(oldLS, arguments);
        }
    }

Look at this part of the code:

        (<any>proxy)[k] = function () {
            return (<any>oldLS)[k].apply(oldLS, arguments);
        }

Why is casting needed on (<any>proxy)[k] since aren't all objects accessible via the bracket notation? And furthermore, why is the above assignment not simply written as: (<any>proxy)[k] = (<any>oldLS)[k]; (what is this code trying to accomplish)?

2 Answers

On the face of it, it sounds like you're asking us to explain why some unknown person might have performed a type assertion in some unspecified TypeScript code for some unspecified TypeScript version. Not sure how someone could really help you there.

But luckily the magic of search engines has divined that you're referring to this tutorial for writing your own language service plugin in TypeScript, written by @RyanCavanaugh.


The reason seems to be that the key k is being inferred as string, and not as keyof ts.LanguageService, so the compiler rejects indexing into proxy and oldLS with k, unless you do some sort of assertion.

I'm not exactly sure why this is happening; I think that since TypeScript v2.1.4, the variable a in for a in b should have the type keyof typeof b.

For example:

function hmm<T>(t: T): void {  
  for (const k in t) { // k is inferred as keyof T
    const keyofT: keyof T = k; // no error
  }
}

But in the code you included it infers only as string, for some reason ( maybe someone in the know can explain).

Once TypeScript fails to infer something you yourself know to be true, you can usually force the compiler to bend to your will by an annotation (in the case of bivariant parameters), or a type assertion.

In my case, I'd probably have done this instead:

    type K = keyof ts.LanguageService
    for (const k in oldLS) {
      proxy[k as K] = function() {
        return oldLS[k as K].apply(oldLS, arguments);
      }
    }

but that's a judgment call.

Hope that helps; good luck.

I guess RyanCavanaugh would be the best person to answer this if the snippet is taken from his sample-ts-plugin repo, but I'll take a stab at it anyway.

What does the casting on (<any>proxy)[k] accomplish since it is on the left side of the assignment?

It casts the variable proxy to any before the property k is accessed.
This is to ignore compiler errors (I assume).

aren't all objects accessible via the bracket notation?

Not if noImplicitAny compiler option is on.

Try to toggle the noImplicitAny option in this example.

(what is this code trying to accomplish)?

It's taking an existing function from the original info.languageService and exposing it as a method of proxy. As noted in the comments, (<any>proxy)[k] = (<any>oldLS)[k]; might not work correctly because the original function would be called with a wrong this context.

Bergi's shorter (<any>proxy)[k] = oldLS[k].bind(oldLS); version would work in any project where programmer sanity is maintained, but it could "fail" to call the correct function if oldLs[k] method was initialized lazily or changed later.

Related