Alex
Alex

Reputation: 67698

Model class constructor in Typescript

I am new to typescript and I am trying to create a "model" class.

The constructor should accept a list of properties (that come from the database), and any of them should be optional.

Here is the code so far:

export type UserRole = "admin" | "moderator" | "user" | "visitor";


export default class User{

    public id: number | null = null;
    public login: string = '';
    public email: string = '';
    public role: UserRole = 'visitor';
    ...

    constructor({id, login, email, role, ... }){
        this.id = id;
        this.login = login;
        this.email = email;
        this.role = role;
        ....
    }

As you can see, it doesn't look right. A lot of code is duplicated. And if I want to make the properties optional it will duplicate even more code:(

Can anyone point me in the right direction? thanks

Upvotes: 0

Views: 3045

Answers (2)

Aadit M Shah
Aadit M Shah

Reputation: 74234

Here's how I would implement the User class.

type UserRole = 'admin' | 'moderator' | 'user' | 'visitor';

class User {
    constructor(
        public id: number | null = null,
        public login: string = '',
        public email: string = '',
        public role: UserRole = 'visitor'
    ) {}
}

However, if you want to create the instance of the User class from an object of user properties then you will require some duplication.

type UserRole = 'admin' | 'moderator' | 'user' | 'visitor';

interface UserProps {
    id: number | null;
    login: string;
    email: string;
    role: UserRole;
}

class User implements UserProps {
    constructor(
        public id: number | null = null,
        public login: string = '',
        public email: string = '',
        public role: UserRole = 'visitor'
    ) {}

    static fromProps({ id, login, email, role }: Partial<UserProps> = {}) {
        return new User(id, login, email, role);
    }
}

There's no elegant way around this duplication. Tobias S. and jcalz show hacks to avoid this duplication, but I would discourage you from using such hacks.

Upvotes: 2

Tobias S.
Tobias S.

Reputation: 23865

I would suggest to use following utility type from here:

type NonFunctionPropertyNames<T> = {
    [K in keyof T]: T[K] extends Function ? never : K
}[keyof T];

type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;

This will create a type out of all properties of a class without the methods.

You can use it like this:


export type UserRole = "admin" | "moderator" | "user" | "visitor";


export default class User{

    public id: number | null = null;
    public login: string = '';
    public email: string = '';
    public role: UserRole = 'visitor';
    

    constructor({id, login, email, role }: NonFunctionProperties<User>){
        this.id = id;
        this.login = login;
        this.email = email;
        this.role = role
    }
}

To make them all optional, just add Partial:

constructor({id, login, email, role }: Partial<NonFunctionProperties<User>>)

Playground

Upvotes: 2

Related Questions