Typescript: Create class via constructor passing in named parameters?

Viewed 31504

I have a class where I have the constructor defined with 3 parameters which are all optional. I was hoping to be able to pass in named parameters so I don't need to pass in undefined.

constructor(year?: number,
            month?: number,
            date?: number)

I was hoping to create an intance of the class like so

  const recurrenceRule = new MyNewClass(month: 6)

but it didn't work and i tried

  const recurrenceRule = new MyNewClass(month = 6)

and that didn't work.

The only way i got it to work was either

  const recurrenceRule = new MyNewClass(undefined, 4)

or

  const recurrenceRule = new MyNewClass(, 4)

But it seems so messy, I was hoping to pass in named arguments and becasue they are all optional I should be able to just pass in 1 - right ?

6 Answers

You could also use Partial to achieve the same result.

You would instantiate the same way as in the previous answers.

class Bar {
  year = new Date().getFullYear();
  month = new Date().getMonth();
  day = new Date().getDay();
  constructor(bar?: Partial<Bar>) {
    Object.assign(this, bar);
  }
  
  log () {
    console.log(`year: ${this.year}, month: ${this.month}, day: ${this.day}`);
  }
}

new Bar().log();
new Bar({ day: 2 }).log();
new Bar({ day: 2, month: 8 }).log();
new Bar({ year: 2015 }).log();

Note 1. The Partial<Bar> will accept anything as long as the properties of the same name have the same type. Yes, this somewhat violates type-safety. The reason this is still powerful is that if you start typing an object literal with {, the intellisense will tell you what properties you can initialize.

Note 2. The Object.assign will disregard readonly properties since it is a JavaScript-specific thing, not TypeScript-specific. This means that while you can't assign a readonly property a new value, you certainly can with Object.assign.

It seems this still isn't as easily possible as it could be, though it would be nice.

But the solution only took a few minutes.

Here's what I did.. I used this fantastic answer to get a list of properties minus methods.

export class Member {
    private constructor(public memberID: number = -1,
        public email: string = '',
        public loggedIn: boolean = false) {
    }

    static newByNamed(obj: NonFunctionProperties<Member>) {
        Object.assign(new Member(), obj);
    }
    
    public clear() {
        this.memberID = config.nonExistentID;
        this.email = '';
        this.loggedIn = false;
    }
}

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

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

And then I can call it with

let member = Member.newByNamed({email = 'me@home'...});

In some cases, it's just easier to have an interface and separate maker function. (For future readers.)


interface Foo {
  x: number
  y: number
}

function newFoo({x=5,y=10}) : Foo {return {x,y}}

const f = newFoo({x:100})
Related