Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

optional parameters in typescript?

Tags:

typescript

Just new to typescript in an angular context. Can you mark properties as optional in the example below? How would you recommend specifying a nav item has no children? With regular JS I would usually rely on a falsey value to check if the object has children property. But in Typescript is it best practice to initialise an empty array? What about other primitive properties that may/may not have values?

import { Injectable } from '@angular/core';

export class NavItem {
  key: string;
  text: string;
  target: string;
  children: NavItem[];
}

const data: NavItem[] = [
  {
    key: "dashboard",
    target: "dashboard",
    text: "Dashboard",
    children: []
  },
  {
    key: "sales",
    target: "sales",
    text: "Sales"
  }
];

@Injectable()
export class NavService {
  getNav() {
    return data;
  }

}
like image 756
Richard G Avatar asked May 02 '17 05:05

Richard G


Video Answer


1 Answers

Yeah, it's very easy to mark a property as optional, you add ? after it:

class NavItem {
  key: string;
  text: string;
  target: string;
  children?: NavItem[];
}

Alternatively you can also use a union with null or undefined:

class NavItem {
  key: string;
  text: string;
  target: string;
  children: NavItem[] | undefined;
}

With that being said, you misunderstood something very important, this is wrong:

const data: NavItem[] = [
  {
    key: "dashboard",
    target: "dashboard",
    text: "Dashboard",
    children: []
  },
  {
    key: "sales",
    target: "sales",
    text: "Sales"
  }
];

data is not an array of NavItem items, in order to get that you'll need to create instances of NavItem using the new keyword, for example:

const data: NavItem[] = [
  Object.assign(new NavItem(), {
    key: "dashboard",
    target: "dashboard",
    text: "Dashboard",
    children: []
  }),
  Object.assign(new NavItem(), {
    key: "sales",
    target: "sales",
    text: "Sales"
  })
];

The compiler doesn't complain about doing that because typescript is based on structural subtyping and the two types share the same structure.
But it's easy to see why it's wrong by adding methods to NavItem:

class NavItem {
  key: string;
  text: string;
  target: string;
  children?: NavItem[];

  getKey() {
    return this.key;
  }
}

Now try this with your code:

console.log(data[0].getKey());

And you'll end up with:

Uncaught TypeError: data[0].getKey is not a function

You should even get a compilation error saying that getKey is missing in your array items.

If you only intend to use NavItem as data objects then it doesn't really matter, but you should just use interfaces or type aliases instead as they don't get compiled to js and you avoid redundant memory usage:

interface NavItem {
  key: string;
  text: string;
  target: string;
  children?: NavItem[];
}

Edit

After a comment from @Zze I've decided to add a simple constructor that uses the "power" of Object.assign:

class NavItem {
  key: string;
  text: string;
  target: string;
  children?: NavItem[];

  constructor(data?: NavItem) {
      if (data) {
          Object.assign(this, data);
      }
  }
}

then:

new NavItem({
    key: "sales",
    target: "sales",
    text: "Sales"
})
like image 115
Nitzan Tomer Avatar answered Sep 22 '22 21:09

Nitzan Tomer