Home > Software engineering >  Method "property" is missing when using named parameters in a Typescript constructor
Method "property" is missing when using named parameters in a Typescript constructor

Time:08-24

When instantiating an object I much prefer the following format:

const MyTest = new Test({
  title: 'hello';
});

over

const MyTest = new Test('hello');

especially when there are a lot of properties to pass.

I tried to set this up using the following interface and class definitions:

interface ITest {
  title: string;

  readonly titlePlusCheese: string;
}

class Test implements ITest {
  public title: string;

  constructor(args: ITest) {
    this.title = args.title;
  }

  get titlePlusCheese(): string {
    return `${this.title} CHEESE`;
  }
}

However, when calling const MyTest = new Test({ title: 'hello' }); I get the following error:

Property 'titlePlusCheese' is missing in type '{ title: string; }' but required in type 'ITest'.ts(2345)

However, the following works:

interface ITest {
  title: string;

  readonly titlePlusCheese: string;
}

class Test implements ITest {
  public title: string;

  constructor(title: string) {
    this.title = title;
  }

  get titlePlusCheese(): string {
    return `${this.title} CHEESE`;
  }
}

const MyTest = new Test('hello');

which leads me to suspect I'm doing something silly.

Can anyone shed any light on it?

CodePudding user response:

ITest is the type of just the params you're passing in, not of the class itself. It does not need titlePlusCheese in it. Instead, put this directly in the class and that is all the typing you need. Your getter does this for you and defines titlePlusCheese as an implicitly readonly property of the class because there's no setter. Like this:

interface ITest {
  title: string;
}

class Test {
  public title: string;

  constructor(args: ITest) {
    this.title = args.title;
  }

  get titlePlusCheese(): string {
    return `${this.title} CHEESE`;
  }
}

This should show you why your second version works. It broadly does the same thing as your version implementing ITest, which is to incorporate titlePlusCheese in the class definition rather than the interface.

It is probably worth reading a bit more about the difference between interfaces and classes. I searched and found these useful snippets:

When use a interface or class in Typescript

https://blog.logrocket.com/when-how-use-interfaces-classes-typescript/


Here's a more extended version inspired by Frank's comment to show the difference between using an interface to shape the constructor arguments and another one to specify what the class must implement:

interface IArgs {
  title: string;
}

interface IClass {
  title: string;
  reasonly titlePlusCheese: string
}

class Test implements IClass {
  public title: string;

  constructor(args: IArgs) {
    this.title = args.title;
  }

  get titlePlusCheese(): string {
    return `${this.title} CHEESE`;
  }
}

CodePudding user response:

I'm not sure if I'm following exactly what you are trying to achieve there, but sounds a bit weird, from an OOP perspective, using the interface that defines the shape of a class as the arguments type for the class constructor sounds like an error.

Interface definition could (and usually does) contain methods, abstract properties, etc. that you obviously cannot provide on the construction stage.

That being said, if you really prefer that syntax of new Test({title: 'test'}) (personally I don't, find it confusing and prone to any sort of errors, also isn't going to scale for a bigger number of args), you can either inline the constructor type like constructor({title}: { title: string}) or use something like constructor({ title}: Pick<ITest, "title>), both, far from readable.

  • Related