Search code examples
javascripttypescriptvisual-studio-codetype-conversion

how forcing a type instance instead of typeof in typescript


how can I export a type by forcing it to be an instance.

I have tried many ways, I only found one solution, creating a static getter but I would like to remove my static getter.

Here context: I would like to export a type of a instance of A from there $A.A, for ref only.


export const $A = (() => {
    class A {
        static get default() {
            return A.create();
        }
        static create() {
            return new A();
        }
        constructor() {}
    }

    return { A };
})();

i try many way, here 7 of them ! no one work instead the way 1 ! but it because i add a static getter in the js class.

export type _1 = typeof $A.A.default;
export type _2 = typeof new $A.A;
export type _3 = typeof $A.A.create();
export type _4 = typeof  $A.A();
export type _5 = typeof $A['A'];
export type _6 =  $A.A;
export type _7 = typeof new ()=>$A.A;

// example somewhere in the project, i want tell A should be a instance and not a typeof!
function foo(A:_6)

So what the syntax to emulate a instance in a ts type for export somewhere for typage usage only. My project is in js, but using ts only for help the tsserver to understand my refs when he dont.

  • So it for Intelisence in my ide only and no for generate ts=>js.

enter image description here


Solution

  • Preliminary note: the class A code here lacks any instance structure (no properties or methods). All non-nullish values will be assignable to that instance type; see this FAQ entry for more info. Just to avoid this weirdness, I've added a property to the example class:

    const $A = (() => {
        class A {
            static get default() {
                return A.create();
            }
            static create() {
                return new A();
            }
            constructor() { }
            someStructure = 123; // add structure here
        }
    
        return { A };
    })();
    

    Now the compiler can tell that {someRandomThing: 123} is not compatible with the A type you're having trouble naming.


    You might want to use the InstanceType<T> utility type to pull out the return type of a construct signature:

    type A = InstanceType<typeof $A.A>
    

    You could write this yourself using conditional type inference:

    type AlsoA = typeof $A.A extends new (...args: any) => infer I ? I : never;
    

    Or, you could use the method we had to use before conditional types existed: TypeScript pretends that the prototype property of a class is the same as its instance type. This isn't really true since the prototype generally only contains the methods and not other properties. But you can use it anyway:

    type AlsoAlsoA = typeof $A.A.prototype;
    

    Any of those should produce the same type.


    Let's make sure it works:

    function foo(a: A) { }
    
    foo($A.A.create()) // okay
    foo({ someRandomThing: 123 }) // error 
    // Argument of type '{ someRandomThing: number; }' is 
    // not assignable to parameter of type 'A'.
    

    Looks good!

    Playground link to code