3

I have a function in which a variable is declared with a reasonably complex structure:

export function foo() {
    const myVar = {
        // some properties with complex types here...
    }

    // Do something with `myVar`
}

And now I wish to export the type of myVar so that I can use it in other files. Is there any way to get type information of scoped variables from outside the function? Something like this:

export type MyVarType = GetVariableFromFunction<typeof foo, "myVar">;

Some more context to avoid the XY problem:

I have created a TypedMessenger class. The purpose of this class is to make it easier to communicate with iframes and workers etc. It works something like this:

import type {TheirHandlers} from "./worker.ts";

const myHandlers = {
    foo(x: number) {
        return x;
    ,
    bar() {
        return "hello";
    },
};
export type MyHandlers = typeof myHandlers;

const messenger = new TypedMessenger<TheirHandlers, MyHandlers>();
messenger.setResponseHandlers(myHandlers);

On the other end you create a similar TypedMessenger but with the two generic parameters flipped. And then you are able to call messenger.send("foo", 3) and it will autocomplete and type check the arguments you passed in.

Now the issue that I'm running into is that I have created a messenger inside a function, and many of these handlers use variables from the scope of that function.

5
  • I don't see anything better than what you're doing. The feature doesn't exist (looks like ms/TS#31160 is asking for it but wasn't particularly well received) and it's hard to imagine a solution that doesn't involve explicit scope-mucking like you're doing. Personally I'd just do the duplicated definition (making sure that you annotate const myVar: MyVarType inside the function to remind you to keep them in sync). Not sure how to write up an answer here, since most of the answer is contained in the question. Commented Jan 9, 2023 at 1:32
  • @jcalz Alright thanks! I was worried this would be the case. Not sure how I feel about the duplicated definition, the whole reason I wrote the the TypedMessenger is that I didn't have to bother with writing signatures. And since I'm using JSDoc to annotate types, writing these signatures are particularly tedious, with lots of inline imports everywhere. Then again the last solution looks really messy, it's a trade-off I guess. Commented Jan 9, 2023 at 11:33
  • As for an answer, I guess the answer is "It's not possible" ¯\_(ツ)_/¯ Commented Jan 9, 2023 at 11:34
  • 1
    Well, uh, do you want to write that up yourself? I don't mind writing an answer that's just "👎NOPE SORRY👎" with a link to ms/TS#31160, but you might want to do it and move the stuff in the question that looks like attempts to answer down into the answer, so that it's a nice clean Q/A pair. Q: How do I access the type of a variable outside the scope of a function? A: You can't; here's the link to a github issue requesting it, and here are the various workarounds with their pros and cons. Commented Jan 9, 2023 at 15:11
  • 2
    Sure, will do :) Commented Jan 9, 2023 at 21:54

1 Answer 1

2

Unfortunately getting types from outside the scope of a function is not possible right now. There's an open issue for it but it doesn't have any recent activity.

There's a couple of ways you can work around this though:

Declare the type outside of the function scope and export it.

type MyVarType = {
    x: boolean;
    fn: (x: number) => void;
};

export function foo() {
    const myVar = {
        x: true,
        fn: (x: number) => {}
    }
}

The downside of this method is that you have to write your structure twice. First you have to declare the type, and then you have to create the value of the variable. If your variable is complex enough you might want to use one of the other methods.

Declare myVar outside of the function.

const myVar = {
    x: true,
    fn: (x: number) => {}
}
export type MyVarType = typeof myVar;

export function foo() {
    // Do stuff with `myVar`...
}

This works unless you need access to other variables inside your function:

const myVar = {
    fn: (x: number) => {
        console.log(scopedVar); // scopedVar doesn't exist :(
    }
}
export type MyVarType = typeof myVar;

export function foo() {
    const scopedVar = "hello";
    
    myVar.fn(3);
}

Create an extra function and use ReturnType<...>.

function getMyVar(scopedVar: string) {
    return {
        fn: (x: number) => {
            console.log(scopedVar);
        }
    }
}
export type MyVarType = ReturnType<typeof getMyVar>;

export function foo() {
    const scopedVar = "";

    getMyVar(scopedVar).fn(3);
}

As a last resort you can create a function that returns the variable, any scoped variables can be passed in the arguments of getMyVar. And you can get the type of the variable using ReturnType<typeof getMyVar>.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.