Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 489
prograhammer Avatar asked Nov 06 '17 15:11

prograhammer


People also ask

What are the brackets in TypeScript?

They are using < > to pass the type parameters. So you decide to use the same. Don't let the < > intimidate you. It is just a way to pass types as arguments.

What is assertion in TypeScript?

In Typescript, Type assertion is a technique that informs the compiler about the type of a variable. Type assertion is similar to typecasting but it doesn't reconstruct code. You can use type assertion to specify a value's type and tell the compiler not to deduce it.

What is casting in TypeScript?

❮ Previous Next ❯ There are times when working with types where it's necessary to override the type of a variable, such as when incorrect types are provided by a library. Casting is the process of overriding a type.

What is angle bracket in JavaScript?

As you probably know, angle brackets in pairs are used: As a deprecated syntax for type-assertions. For manually specifying generic type parameters. For declaring elements in .


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.

like image 192
jcalz Avatar answered Sep 21 '22 13:09

jcalz


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.

like image 37
noppa Avatar answered Sep 17 '22 13:09

noppa