Kartik Chauhan
Kartik Chauhan

Reputation: 3068

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

Upvotes: 2

Views: 6959

Answers (2)

Rikki
Rikki

Reputation: 1229

This feels like your object hierarchy is upside down. Even though this is the order that you've developed this in, doesn't mean you cannot insert the simpler class as the superclass of the existing one.

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

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

Now you have the two classes you want and they're related in the way you want. You might want to rename the two (maybe Client and ClientWithSecret), but do that as a refactor so existing references to Client update to point to the right one.

If you want to add anything shared, then add it to BaseClient. If the two need to diverge, then create a new subclass (extends) for that purpose.

Upvotes: 0

jcalz
jcalz

Reputation: 328618

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

Upvotes: 5

Related Questions