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.

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