Search code examples
typescript

How can I convert a type to a class in typescript?


I have a class Client and I want to create a new class UpdateClient but omitting few properties of class Client.

class Client {
    constructor() {
        this.clients = '';
        this.client_secret = '';
    }
    clients: string;
    client_secret: string;
}

I want class UpdateClient to be like this

class UpdateClient {
    constructor() {
        this.clients = '';
    }
    clients: string;
}

Now, I'm sure there will be few approaches in vanilla JS by which I can get the task done, like iterating over all enumerable properties of class client, but I don't want to that.

I want a typescript specific solution. I found Omit type utility and it's working as expected. However, there's a small issue which I'm unable to fix.

This is the whole code snippet

class Client {
    constructor() {
        this.clients = '';
        this.client_secret = '';
    }
    clients: string;
    client_secret: string;
}

type T = Omit<Client, 'client_secret'>

I'm getting a type instead of a class. I want to somehow convert this type T to the class UpdateClient and export it. The exported property needs to be a class because the other module using this one expects a class.

I'm using typescript v3.7.5


Solution

  • If all you want is for UpdateClient to be a class constructor that makes instances of Omit<Client, 'client_secret'>, you can write it this way:

    const UpdateClient: new () => Omit<Client, 'client_secret'> = Client;
    

    The declared type new () => ... means "a constructor which takes no arguments and produces an instance of ...". The syntax is either called a constructor signature or "newable" and is part of the static side of a class.

    The fact that the above code, assigning Client to the variable UpdateClient, compiles without error shows that the compiler agrees that Client does act like a no-arg constructor of Omit<Client, 'client_secret'>. If, for example, Client's constructor required an argument, or if Omit<Client, 'client_secret'> weren't a supertype of Client, you'd get an error:

    class RequiresArg {
      constructor(public clients: string) { }
    }
    const Oops: new () => Omit<Client, 'client_secret'> = RequiresArg; // error
    // Type 'typeof RequiresArg' is not assignable to type 'new () => Pick<Client, "clients">'
    
    class NotCompatible {
      clients?: number;
    }
    const StillOops: new () => Omit<Client, 'client_secret'> = NotCompatible; // error
    // Type 'number | undefined' is not assignable to type 'string'.
    

    Anyway, then this will work:

    const c = new UpdateClient();
    c.clients; // okay
    c.client_secret; // error at compile time, although it does exist at runtime
    

    Do note that even though UpdateClient's instances are not known by the compiler to have a client_secret property, it's still just an instance of Client at runtime, so the property will definitely exist at runtime. If that's a problem you should probably do something completely different. But since you said Omit<...> works for you, I guess that's not an issue.


    Okay, hope that helps; good luck!

    Playground link to code