Categories
Quasar

Developing Vue Apps with the Quasar Library — Table

Quasar is a popular Vue UI library for developing good looking Vue apps.

In this article, we’ll take a look at how to create Vue apps with the Quasar UI library.

Table

Quasar comes with the q-table component to let us add a table into our Vue app.

To use it, we write:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-table
            title="Treats"
            :data="data"
            :columns="columns"
            row-key="name"
          >
          </q-table>
        </div>
      </q-layout>
    </div>
    <script>
      const columns = [
        {
          name: "name",
          required: true,
          label: "Dessert",
          align: "left",
          field: (row) => row.name,
          format: (val) => `${val}`,
          sortable: true
        },
        {
          name: "calories",
          align: "center",
          label: "Calories",
          field: "calories",
          sortable: true
        },
        { name: "fat", label: "Fat (g)", field: "fat", sortable: true },
        {
          name: "calcium",
          label: "Calcium (%)",
          field: "calcium",
          sortable: true,
          sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
        }
      ];
      const data = [
        {
          name: "Frozen Yogurt",
          calories: 159,
          fat: 6.0,
          calcium: "14%"
        },
        {
          name: "Ice cream sandwich",
          calories: 237,
          fat: 9.0,
          calcium: "8%"
        },
        {
          name: "Eclair",
          calories: 262,
          fat: 16.0,
          calcium: "6%"
        },
        {
          name: "Honeycomb",
          calories: 408,
          fat: 3.2,
          calcium: "0%"
        },
        {
          name: "Donut",
          calories: 452,
          fat: 25.0,
          calcium: "2%"
        },
        {
          name: "KitKat",
          calories: 518,
          fat: 26.0,
          calcium: "12%"
        }
      ];

      new Vue({
        el: "#q-app",
        data: {
          columns,
          data
        }
      });
    </script>
  </body>
</html>

The columns prop has the column definition.

data has the table data.

title has the table title.

row-key sets the property name with the unique ID value.

In the columns array, the field is a function that returns the value that we want to display from the table data.

format returns the formatted value

align sets the cell content alignment.

required sets whether the column is required.

label has the column label.

sortable lets us enable or disable sorting of the column.

sort is a method that lets us change how to sort the column data.

name is the property name of the data entry to display.

Pagination is built into the table by default.

We can add the dark prop to set the table to show with a dark background:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-table
            title="Treats"
            :data="data"
            :columns="columns"
            row-key="name"
            dark
          >
          </q-table>
        </div>
      </q-layout>
    </div>
    <script>
      const columns = [
        {
          name: "name",
          required: true,
          label: "Dessert",
          align: "left",
          field: (row) => row.name,
          format: (val) => `${val}`,
          sortable: true
        },
        {
          name: "calories",
          align: "center",
          label: "Calories",
          field: "calories",
          sortable: true
        },
        { name: "fat", label: "Fat (g)", field: "fat", sortable: true },
        {
          name: "calcium",
          label: "Calcium (%)",
          field: "calcium",
          sortable: true,
          sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
        }
      ];
      const data = [
        {
          name: "Frozen Yogurt",
          calories: 159,
          fat: 6.0,
          calcium: "14%"
        },
        {
          name: "Ice cream sandwich",
          calories: 237,
          fat: 9.0,
          calcium: "8%"
        },
        {
          name: "Eclair",
          calories: 262,
          fat: 16.0,
          calcium: "6%"
        },
        {
          name: "Honeycomb",
          calories: 408,
          fat: 3.2,
          calcium: "0%"
        },
        {
          name: "Donut",
          calories: 452,
          fat: 25.0,
          calcium: "2%"
        },
        {
          name: "KitKat",
          calories: 518,
          fat: 26.0,
          calcium: "12%"
        }
      ];

      new Vue({
        el: "#q-app",
        data: {
          columns,
          data
        }
      });
    </script>
  </body>
</html>

Conclusion

We can add a table into our Vue app with Quasar’s q-table component.

Categories
Quasar

Developing Vue Apps with the Quasar Library — Stepper Header Options

Quasar is a popular Vue UI library for developing good looking Vue apps.

In this article, we’ll take a look at how to create Vue apps with the Quasar UI library.

Stepper Header Options

We can change the stepper header to different styles.

For instance, we can write:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-stepper v-model="step" ref="stepper" color="primary" animated>
            <q-step
              :name="1"
              :error="step < 3"
              title="Step 1"
              icon="settings"
              :done="step > 1"
            >
              step 1
            </q-step>

            <q-step
              :name="2"
              title="Step 2"
              caption="Optional"
              icon="create_new_folder"
              :done="step > 2"
            >
              step 2
            </q-step>

            <q-step :name="3" title="Step 3" icon="assignment" disable>
              This step won't show up because it is disabled.
            </q-step>

            <q-step :name="4" title="Step 4" icon="add_comment">
              step 4
            </q-step>

            <template v-slot:navigation>
              <q-stepper-navigation>
                <q-btn
                  @click="$refs.stepper.next()"
                  color="primary"
                  :label="step === 4 ? 'Finish' : 'Continue'"
                >
                </q-btn>
                <q-btn
                  v-if="step > 1"
                  flat
                  color="primary"
                  @click="$refs.stepper.previous()"
                  label="Back"
                  class="q-ml-sm"
                >
                </q-btn>
              </q-stepper-navigation>
            </template>
          </q-stepper>
        </div>
      </q-layout>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {
          step: 1
        }
      });
    </script>
  </body>
</html>

to add the error prop into the q-step component.

The error prop lets us set the condition for when to display an error icon and red text.

We can also change the label style to display with the icon stacked above the label text with the alternative-labels prop:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-stepper
            alternative-labels
            v-model="step"
            ref="stepper"
            color="primary"
            animated
          >
            <q-step :name="1" title="Step 1" icon="settings" :done="step > 1">
              step 1
            </q-step>

<q-step
              :name="2"
              title="Step 2"
              icon="create_new_folder"
              :done="step > 2"
            >
              step 2
            </q-step>

            <q-step :name="3" title="Step 3" icon="assignment" disable>
              This step won't show up because it is disabled.
            </q-step>

            <q-step :name="4" title="Step 4" icon="add_comment">
              step 4
            </q-step>

            <template v-slot:navigation>
              <q-stepper-navigation>
                <q-btn
                  @click="$refs.stepper.next()"
                  color="primary"
                  :label="step === 4 ? 'Finish' : 'Continue'"
                >
                </q-btn>
                <q-btn
                  v-if="step > 1"
                  flat
                  color="primary"
                  @click="$refs.stepper.previous()"
                  label="Back"
                  class="q-ml-sm"
                >
                </q-btn>
              </q-stepper-navigation>
            </template>
          </q-stepper>
        </div>
      </q-layout>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {
          step: 1
        }
      });
    </script>
  </body>
</html>

We can also change the color of the icon with the inactive-color , active-color and done-color props to change the label colors at those states:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-stepper v-model="step" ref="stepper" color="primary" animated>
            <q-step
              done-color="deep-orange"
              active-color="purple"
              inactive-color="secondary"
              :name="1"
              title="Step 1"
              icon="settings"
              :done="step > 1"
            >
              step 1
            </q-step>

            <q-step
              :name="2"
              title="Step 2"
              icon="create_new_folder"
              :done="step > 2"
            >
              step 2
            </q-step>

            <q-step :name="3" title="Step 3" icon="assignment" disable>
              This step won't show up because it is disabled.
            </q-step>

            <q-step :name="4" title="Step 4" icon="add_comment">
              step 4
            </q-step>

            <template v-slot:navigation>
              <q-stepper-navigation>
                <q-btn
                  @click="$refs.stepper.next()"
                  color="primary"
                  :label="step === 4 ? 'Finish' : 'Continue'"
                >
                </q-btn>
                <q-btn
                  v-if="step > 1"
                  flat
                  color="primary"
                  @click="$refs.stepper.previous()"
                  label="Back"
                  class="q-ml-sm"
                >
                </q-btn>
              </q-stepper-navigation>
            </template>
          </q-stepper>
        </div>
      </q-layout>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {
          step: 1
        }
      });
    </script>
  </body>
</html>

Conclusion

We can add the stepper with various options with Quasar’s q-stepper component.

Categories
Quasar

Developing Vue Apps with the Quasar Library — Stepper

Quasar is a popular Vue UI library for developing good looking Vue apps.

In this article, we’ll take a look at how to create Vue apps with the Quasar UI library.

Stepper

Quasar comes with the q-stepper component to let us create multi-step content.

For instance, we can use it by writing:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-stepper v-model="step" ref="stepper" color="primary" animated>
            <q-step :name="1" title="Step 1" icon="settings" :done="step > 1">
              step 1
            </q-step>

            <q-step
              :name="2"
              title="Step 2"
              caption="Optional"
              icon="create_new_folder"
              :done="step > 2"
            >
              step 2
            </q-step>

            <q-step :name="3" title="Step 3" icon="assignment" disable>
              This step won't show up because it is disabled.
            </q-step>

            <q-step :name="4" title="Step 4" icon="add_comment">
              step 4
            </q-step>

            <template v-slot:navigation>
              <q-stepper-navigation>
                <q-btn
                  @click="$refs.stepper.next()"
                  color="primary"
                  :label="step === 4 ? 'Finish' : 'Continue'"
                >
                </q-btn>
                <q-btn
                  v-if="step > 1"
                  flat
                  color="primary"
                  @click="$refs.stepper.previous()"
                  label="Back"
                  class="q-ml-sm"
                >
                </q-btn>
              </q-stepper-navigation>
            </template>
          </q-stepper>
        </div>
      </q-layout>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {
          step: 1
        }
      });
    </script>
  </body>
</html>

The v-model binds to the step number.

We put the q-step components inside the q-stepper to show the step content.

The name prop has the step number which is compared with the step reactive property to determine which step to display.

The title prop is shown in the title bar.

caption is the subtitle of the step.

icon has the icon name.

And done is the condition when the step icon and text highlighted.

The disable prop disables the stepper.

The navigation slot has the content which shows at the bottom of the stepper that lets us navigate the steps.

$ref.stepper.next() moves to the next step and $ref.stepper.previous() moves to the previous step.

We can make the stepper display vertically with the vertical prop:

<!DOCTYPE html>
<html>
  <head>
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.min.css"
      rel="stylesheet"
      type="text/css"
    />
  </head>
  <body class="body--dark">
    <script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@1.12.13/dist/quasar.umd.min.js"></script>
    <div id="q-app">
      <q-layout
        view="lHh Lpr lFf"
        container
        style="height: 100vh;"
        class="shadow-2 rounded-borders"
      >
        <div class="q-pa-md">
          <q-stepper
            vertical
            v-model="step"
            ref="stepper"
            color="primary"
            animated
          >
            <q-step :name="1" title="Step 1" icon="settings" :done="step > 1">
              step 1
            </q-step>

            <q-step
              :name="2"
              title="Step 2"
              caption="Optional"
              icon="create_new_folder"
              :done="step > 2"
            >
              step 2
            </q-step>

            <q-step :name="3" title="Step 3" icon="assignment" disable>
              This step won't show up because it is disabled.
            </q-step>

            <q-step :name="4" title="Step 4" icon="add_comment">
              step 4
            </q-step>

            <template v-slot:navigation>
              <q-stepper-navigation>
                <q-btn
                  @click="$refs.stepper.next()"
                  color="primary"
                  :label="step === 4 ? 'Finish' : 'Continue'"
                >
                </q-btn>
                <q-btn
                  v-if="step > 1"
                  flat
                  color="primary"
                  @click="$refs.stepper.previous()"
                  label="Back"
                  class="q-ml-sm"
                >
                </q-btn>
              </q-stepper-navigation>
            </template>
          </q-stepper>
        </div>
      </q-layout>
    </div>
    <script>
      new Vue({
        el: "#q-app",
        data: {
          step: 1
        }
      });
    </script>
  </body>
</html>

Conclusion

We can add a stepper component into our Vue app with Quasar’s q-stepper component.

Categories
JavaScript

Introducing the JavaScript Window Object — Location Property

The window object is a global object that has the properties pertaining to the current DOM document, which is the things that are in the tab of a browser. The document property of the window object has the DOM document and associated nodes and methods that we can use to manipulate the DOM nodes and listen to events for each node. Since the window object is global, it’s available in every part of the application. When a variable is declared without the var , let or const keywords, they’re automatically attached to the window object, making them available to every part of your web app. This is only applicable when strict mode is disabled. If it’s enabled, then declaring variables without var , let , or const will be stopped an error since it’s not a good idea to let us declare global variables accidentally. The window object has many properties. They include constructors, value properties and methods. There’re methods to manipulate the current browser tab like opening and closing new popup windows, etc.

In a tabbed browser, each tab has its own window object, so the window object always represent the state of the currently opened tab in which the code is running. However, some properties still apply to all tabs of the browser like the resizeTo method and the innerHeight and innerWidth properties.

Note that we don’t need to reference the window object directly for invoking methods and object properties. For example, if we want to use the window.Image constructor, we can just write new Image() . In this article, we continue to look at what’s in the window object. In the previous sections, we looked at the constructors and some of the properties of the window object, including using the customElements to build a Web Component, and the crypto object to do cryptography on the client using symmetric and asymmetric encryption algorithms. In this part, we will look at the location.assign , location.reload, location.replace, and location.toString methods. Also, we will look at the document.readyState, document.referrer , and the document.title properties.

Methods of the window.document.location Object

location.assign

The location.assign method causes the window to load and display the document with the given URL. If location.assign fails to run because of a security violation, then the DOMException of the type SECURITY_ERROR will be thrown. This happens if the script that calls this method has a different origin than the currently loaded page. The most probably cause of this is that the script is hosted on a different domain. This is needed to prevent unauthorized scripts from taking users to URLs that they didn’t ask for, exposing them to malicious sites. It takes one argument, which is the string with the URL that we want to load. For example, if we want to go to https://medium.com, then we can write:

document.location.assign('https://medium.com');

location.reload

To reload the current page, we can use the location.reload method. It works exactly like when you press the Refresh button on your browser. In some browsers, this method takes an option boolean argument, which is false by default. If it’s true , then the page is always reloaded from server, bypassing the browser’s HTTP cache. The call for this method may be blocked and a SECURITY_ERROR type of the DOMException will be thrown if the origin that calls the reload method is different from the origin of the URL of the current loaded page. This is needed to prevent unauthorized scripts from taking users to URLs that they didn’t ask for, exposing them to malicious sites. We can call the reload method like in the following code:

location.reload(true);

location.replace

The location.replace method loads the new URL that’s passed into this function. If we use the replace method, then the page that we were in before the replace method is called won’t be saved in the session History unlike the assign method. This means that the user won’t be able to click the back button to go back to the page that was loaded this method was called. The call for this method may be blocked and a SECURITY_ERROR type of the DOMException will be thrown if the origin that calls the reload method is different from the origin of the URL of the current loaded page. This is needed to prevent unauthorized scripts from taking users to URLs that they didn’t ask for, exposing them to malicious sites. It takes one argument, which is a string for the URL to load. If the URL isn’t valid, then a SYNTAX_ERROR type of the DOMException will be thrown. For example, if we want to go to https://medium.com, then we can write:

document.location.replace('https://medium.com');

location.toString

The toString method returns a string of the whole URL. It’s a read only version of the href property. For example, we can call it like in the following code:

console.log(document.location.toString());

Then we get something the whole URL which is something like https://fiddle.jshell.net/_display/ .

window.document.readyState

To get the loading state of the document object we can use the readyState property. It is a read only string property that get can be set to one of the 3 values:

  • 'loading' — the document is loading
  • 'interactive' — the document is loaded and the document is parsed but sub-resources such as images, stylesheets and frames are still loading.
  • 'complete' — this document and sub-resources such as images, stylesheets and frames are all loaded. This state indicates that the load event is about to be triggered.

We can watch the state of the readyState property with a handler for the readystatechange event. One way to write it is to assign an event handler function to the onreadystatechange property. For example, we can write:

document.onreadystatechange = function() {
  console.log(document.readyState);
}

We can also use the addEventListener method to attach the readystatechange event listener, like in the following code:

document.addEventListener('readystatechange', event => {
  console.log(event.target.readyState);
})

window.document.referrer

The referrer property is a read only string property that returns the URI of the page that’s linked to the currently loaded page. An empty string is returned if the user has navigated to the page directly. That is, the user didn’t navigate to the page through a link, but rather by doing something like typing in the URL directly or clicking on a bookmark. Inside an iframe , the document.referrer will initially be set to the same value as the href of the parent window’s window.location . For example, if we log the document.referrer like in the following code:

console.log(document.referrer)

We may get a URL like https://jsfiddle.net/.

window.document.title

We can use the title property of the document object to get the title of a web page. It’s a property that can be get or set. When we use it as a getter, we get a string with the title of the web page. The value set by assigning a string to the document.title takes presence over the title in the title tag of the HTML markup, so the title that’s set programmatically will be returned if it’s set, otherwise the one in the HTML markup will be returned. For example, if we set the title with JavaScript by writing the following:

document.title = 'New Title';

Then we get ‘New Title’ on the top of the browser tab. Then when we log the title with console.log(document.title); we also get ‘New Title’. If we didn’t set the title with document.title , then we title in the HTML markup. In Gecko based browsers, this property applies to HTML, SVG, XUL and other documents. For XUL, the title attribute of the <xul:window> or other top level XUL element will have the value of document.title set. In XUL, accessing the document.title before the document if fully loaded has undefined behavior. It may return an empty string and setting document.title may have no effect.

In this article, we looked at various method properties of the location object.The location.assign let us go to another page with a different URL while keeping the currently loaded page in the history. The location.reload method will reload the page, with some browsers accepting a boolean argument that will bypass the cache when reloading the true is passed in. Like the location.assign, the location.replace method let us go to another page with a different URL but the currently loaded page won’t be kept in the history. The location.toString method gets the full URL in string form. We also looked at some other properties of the document object. The document.readyState let us know the state of the state of the page that’s being loaded. The document.referrer property is handy for getting the URL of the page that triggered the loading of the currently loaded page, and the document.title property let us get and set the title to the currently loaded web page.

Categories
TypeScript

Introduction to TypeScript Generics — Functions

One way to create reusable code is to create code that lets us use it with different data types as we see fit. TypeScript provides the generics construct to create reusable components where we can work with variety of types with one piece of code. This allows users to use these components and by putting in their own types. In this article, we’ll look at the many ways to define a generic function where we can set the types of parameters and the return value to avoid redefining functions that have the same logic but different parameter types and return types.

Defining Generic Functions

One basic use case for generics is for creating functions that has the same logic, but have different types for parameters and their return types. For example if we want to create an identity function, where we just return the same thing that’s passed in, we may write something like:

function echo(arg: number): number {
  return arg;
}

If we want this function to take accept more than one type as a parameter and return the same type that’s passed in, then we can turn it into a generic function by putting in a generic type marker instead of putting in the number type for the arg parameter and the return type. We denote a type variable with the T keyword. The T keyword allows us to capture the group for the generic function later. We can switch out number for T like in the following code:

function echo<T>(arg: T): T {
  return arg;
}

Then when we call the function, we can write something like:

console.log(echo<number>(1));
console.log(echo<string>('string'));
console.log(echo<boolean>(true));

Then we get the following out:

1
string
true

With the code above, we get type checking in each function call for the argument passed in and the return type. The code inside the <> denotes the type that we want the parameter and the return type that the function will accept for the parameter and the return type. We don’t have to put in the type explicitly like we did above. TypeScript is smart enough to identify the type as long as we define the generic function. For example, if we write the following code to call the echo function:

console.log(echo(1));
console.log(echo('string'));
console.log(echo(true));

The code will still compile and run and output the same things as it did above. This keeps the code shorter, but explicitly setting the type is clearer for developers and compiler may sometimes fail to identify the type, especially when the code is more complex. In this case, the type must be provided.

If we want to access specific properties of objects, then we have to be more specific with our generic types. For example, if we assume that the type for the parameter and the return type are always some kind of array, then we can specify that with the following code:

function echo<T>(arg: T[]): T[] {
  console.log(arg.length);
  return arg;
}

Equivalently, we can write that with the Array<T> generic type instead like in the following code:

function echo<T>(arg: Array<T>): Array<T> {
  console.log(arg.length);
  return arg;
}

In both pieces of code, we can log the length property of the array that we pass into the function as our argument since it’s guaranteed that what we pass in and return are always arrays. For example, we can call the new echo function like in the following code:

echo([1, 2, 3]);
echo(['a', 'b', 'c']);

Type inference would still work in the case above.

In the above examples, we have one parameter and one return value. But what if we want to pass in multiple parameters with different types and return one or more of them? We can write something like the following:

function echo<T, U>(arg: T, arg2: U): [T, U] {
  return [arg, arg2];
}

In the code above, we have 2 generic types, T and U , which represents same or different types. For example, we can use it like in the following code:

console.log(echo(1, 'a'));

In the code above, we have the first argument being a number and the second being a string, but we can also have both being the same type like we have in the following code:

console.log(echo(1, 2));

Variation of Type Markers

Another issue that we’re going to run into eventually is that we want parameters that have data types that are different from the return types. One way to solve this is to mix generic types and regular types with interfaces like we do in the following code:

interface EchoInterface {
  [prop: string]: any
}

function echo<T, U, V, W>(a: T, b: U, c: V, d: W): EchoInterface {
  return {a,b,c,d};
}

console.log(echo(1, 'a', false, {}));

Then we get the following from the console.log :

{a: 1, b: "a", c: false, d: {}}

We can also leave out the return type like we do in the following code:

function echo<T, U, V, W, X>(a: T, b: U, c: V, d: W) {
  return { a, b, c, d };
}

console.log(echo(1, 'a', false, {}));

We get the same output as we do above. Note that in the 2 examples above, we can specify as many letters as we want as generic type markers. Generic type markers doesn’t have to be one letter. It’s just convention for it to be a single letter. We can write something like the following:

function echo<T, U, V, W, AA>(a: T, b: U, c: V, d: AA) {
  return { a, b, c, d };
}

Another way to define a generic function is to put the signature in the interface along with the generic type markers there. Then when declare the function by assigning it to a variable, we can set the type of the variable with the interface and then assign the generic function to it with the usual generic type markers added to the function. For example, we can write something like the following code:

interface EchoFn {
  <T>(a: T): T;
};

function echo<T>(a: T): T {
  return a;
}

const e: EchoFn = echo;

We may also add the generic type marker inside the <> to the interface declaration like in the following code:

interface EchoFn<T> {
  <T>(a: T): T;
};

Then when we declare the variable that we assign the function to, we have to specify the type of the interface explicitly, like in the following code:

interface EchoFn<T> {
  <T>(a: T): T;
};

function echo<T>(a: T): T {
  return a;
}

const e: EchoFn<number> = echo;

In the code above, we have to add the number type declaration explicitly in the last line instead of letting TypeScript infer it automatically.

To avoid redefining functions that have the same logic but different parameter types and return types, we can define generic functions. To do this, we add generic type markers to our functions, and in interfaces that are used for typing the functions. We define functions by inserting generic type markers inside the <> after the function name, with each marker separated by commas, and also after the colon in parameters, and after the colon and before the open curly bracket. The generic type marker for the return type is optional. We can leave it out if we want to return something that has a different type than what we pass into the parameters. Likewise, in interfaces, we put the signature of the function in the interface and optionally in the interface definition to add enforce generic type markers in the functions we define.