Categories
TypeScript Best Practices

TypeScript Best Practices — Type Assertions and Type Annotations

TypeScript is an easy to learn extension of JavaScript. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust TypeScript code.

In this article, we’ll look at the best practices to following when writing code with TypeScript, including type assertions and having explicit type annotations.

Enforce Consistent Usage of Type Assertions

Type assertion styles should be consistent across our project.

Type assertions are also referred to as data type casting in TypeScript.

However, they’re technically different.

We can either use the as keyword or <> to add type assertions to our values.

For instance, we can either stick with:

let x = "foo" as string;

or:

let x = <string>"foo";

const is also allowed with the rule. It’s available since TypeScript 3.4 and it allows us to make something read-only.

For instance, we can write:

let x = "foo" as const;

or:

let x = <const>"foo";

Consistent Type Definitions with interface or type

In TypeScript, we can define interfaces for annotating types.

Also, we can create type alias with the type keyword to do the same thing.

It’s a good idea to be consistent with what we use when we define types.

For instance, we can either stick with interfaces:

interface Foo {
  a: string;
  b: number;
}

or type aliases:

type Foo = {
  a: string;
  b: number;
};

Require Explicit Return Types on Functions and Class Methods

It’s a good idea to add return type annotations for functions and class methods.

This way, we know what each function or method returns.

For instance, instead of writing:

function foo() {
  return 'bar';
}

We can write:

function foo: string() {
  return 'bar';
}

Likewise, with class methods, instead of writing:

class Foo{
  method() {
    return 'bar';
  }
}

We write:

class Foo{
  method(): string {
    return 'bar';
  }
}

If our method returns nothing, we use the void return type:

function foo(): void {
  return;
}

or:

class Foo{
  method(): void {
    return;
  }
}

Require Explicit Accessibility Modifiers on Class Properties and Methods

If we leave out the accessibility modifiers in classes, then it may be hard for people to understand whether a class property or method is accessible or not.

By default, if we leave them out, the class member is public.

So we may also want to restrict access to some members in most cases.

Therefore, we should include them.

So instead of writing:

class Foo {
  foo() {
    console.log("foo");
  }

  bar() {
    //...
  }
}

We may want to restrict access to some members by writing:

class Foo {
  foo() {
    console.log("foo");
  }

  private bar() {
    //...
  }
}

This way, the TypeScript compiler will give us an error if we try to compile it.

Access modifiers include public , private , readonly and protected .

A public member is available to everything.

A private member is only available within the class.

A readonly member is public but it’s read-only.

A protected member is only available within the class or any child class.

Require Explicit Return and Argument Types on Exported Functions and Classes’ Public Class Methods

To make working with functions and public methods of classes easier, we should add explicit argument and return types so that we can get autocomplete and errors when we work with them.

For instance, instead of writing:

export function test() {
  return;
}

We write:

export function test(): void {
  return;
}

And instead of writing:

class Foo {
  method() {
    return;
  }
}

We write:

export class Foo {
  method(): void {
    return;
  }
}

Likewise, with argument types, we write:

export class Foo {
  method(foo: string): string {
    return foo;
  }
}

and:

export function test(foo: string): string{
  return foo;
}

Now we don’t have to guess or look up the types of exported classes and functions.

Conclusion

We should annotate return and argument types so don’t have to look up the types of functions and class methods that are exported.

Consistent usage of type assertions notation may also be a good idea.

Also, it may be a good idea to have consistent usage of interface or type to create new types.

Categories
TypeScript Best Practices

TypeScript Best Practices — Classes, Types, and Overloads

TypeScript is an easy to learn extension of JavaScript. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust TypeScript code.

In this article, we’ll look at the best practices to following when writing code with TypeScript, including member overloads, class instance variables, and restricting types.

Member Overloads Should be Consecutive

If we have member overloads, then they should be consecutive so that we can spot them easily.

For instance, instead of writing:

declare namespace Foo {

export function foo(s: string): void;

export function foo(n: number): void;

export function bar(): void;

export function foo(sn: string | number): void;

}

We write:

declare namespace Foo {
  export function foo(s: string): void;
  export function foo(n: number): void;
  export function foo(sn: string | number): void;
  export function bar(): void;
}

This also applies to interfaces, classes, type aliases, and exports.

We should write:

interface Foo {
  foo(s: string): void;
  foo(n: number): void;
  foo(sn: string | number): void;
  bar(): void;
}

or:

class Foo {
  foo(s: string): void;
  foo(n: number): void;
  foo(sn: string | number): void {}
  bar(): void {}
}

or:

export function foo(s: string): void;
export function foo(n: number): void;
export function foo(sn: string | number): void;
export function bar(): void;

Use [] or Array<T> for Arrays

To restrict the types of arrays, we can use T[] or Array<T> to restrict the types that an array can hold.

For instance, instead of writing:

const arr = [1, 2, 3];

We write:

const arr: Array<number> = [1, 2, 3];

or:

const arr: number[] = [1, 2, 3];

So that arr can only hold numbers.

If we want our array to be read-only, we can write:

const arr: ReadonlyArray<number> = [1, 2, 3];

No Awaiting Something that’s Not Then-able

If something doesn’t have a then method, then we shouldn’t put await before it.

Usually, await should be used for promises rather than any object that has a then method.

For instance, instead of writing:

const foo = async () => await "value";

We should write:

const foo = async () => {
  await Promise.resolve(1);
};

No Comments With Prefix @ts Should be Used

@ts is used to suppress TypeScript compiler errors.

Therefore, we probably shouldn’t use them since they may lead to errors later.

Instead of writing:

// @ts-nocheck

We don’t write any comment that starts with @ts .

Prevent Specific Types from Being Used

We may want to prevent some types to be used.

For instance, we may want to prevent String from being used instead of string .

We can do that in our tsconfig.json , by writing:

{
  "@typescript-eslint/ban-types": ["error", {
    "types": {
      "String": {
        "message": "Use string instead",
        "fixWith": "string"
      },"{}": {
        "message": "Use object instead",
        "fixWith": "object"
      }
    }
  }]
}

The config above prevents String from being used and if it’s used, the compiler will show the message ‘Use string instead’ and won’t compile.

We did the same thing with the {} type.

Class Literals Should be Exposed in a Consistent Style

We should have a consistent style when exposing class members to the outside.

There are a few styles that we can adopt.

We can use the ‘fields’ style as follows:

class Foo {
  public readonly foo = 1;
  public readonly bar = [1, 2, 3];
  private readonly ['baz'] = 'hello';

  public get qux() {
    return `qux ${foo + 1}`;
  }
}

If we have the ‘fields’ style, then we don’t have getters for our instance variables if they have read-only values.

Alternatively, we can use the ‘getters’ style by writing:

class Foo {
  public readonly foo = 1;
  public readonly bar = [1, 2, 3];
  public static get baz() {
    return 1;
  }

  private get qux() {
    return 'qux';
  }
}

We have getters for any code that isn’t defined as read-only.

We can take our pick, but it’s a good idea to be consistent.

Conclusion

We can restrict types of array entries by specifying the types that we want the array to be.

Also, we can restrict types for the whole project by changing the configuration of our project.

We can also restrict that overloads of the same function be placed together.

Finally, we can stick to one style of declaring class instance variables.

Categories
Vuetify

Vuetify — Toolbars

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Toolbars

We can add toolbars with the v-toolbar component.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-card color="grey lighten-4" flat height="200px" tile>
          <v-toolbar prominent extended>
            <v-app-bar-nav-icon></v-app-bar-nav-icon>

            <v-toolbar-title>Title</v-toolbar-title>

            <v-spacer></v-spacer>

            <v-btn icon>
              <v-icon>mdi-magnify</v-icon>
            </v-btn>

            <v-btn icon>
              <v-icon>mdi-heart</v-icon>
            </v-btn>

            <v-btn icon>
              <v-icon>mdi-dots-vertical</v-icon>
            </v-btn>
          </v-toolbar>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

to add a toolbar with the v-toolbar component.

v-app-bar-nav-icon to add the nav icon.

v-toolbar-title lets us add the title.

v-space lets us add spacing to the toolbar items,

v-btn lets us add the buttons.

Dense Toolbars

We can add the dense prop to the v-toolbar to make it denser.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-card color="grey lighten-4" flat height="200px" tile>
          <v-toolbar dense>
            <v-app-bar-nav-icon></v-app-bar-nav-icon>

            <v-toolbar-title>Title</v-toolbar-title>

            <v-spacer></v-spacer>

            <v-btn icon>
              <v-icon>mdi-magnify</v-icon>
            </v-btn>

            <v-btn icon>
              <v-icon>mdi-heart</v-icon>
            </v-btn>

            <v-btn icon>
              <v-icon>mdi-dots-vertical</v-icon>
            </v-btn>
          </v-toolbar>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

to make the toolbar shorter.

Dark Variant

To make the toolbar show with a black background, we can add the dark prop;

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-card color="grey lighten-4" flat height="200px" tile>
          <v-toolbar dark>
            <v-spacer></v-spacer>

            <v-btn icon>
              <v-icon>mdi-reply</v-icon>
            </v-btn>

            <v-btn icon>
              <v-icon>mdi-dots-vertical</v-icon>
            </v-btn>
          </v-toolbar>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

Background Color

We can also change the background color of the toolbar.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-card color="grey lighten-4" flat height="200px" tile>
          <v-toolbar color="primary" dark>
            <v-spacer></v-spacer>

            <v-btn icon>
              <v-icon>mdi-reply</v-icon>
            </v-btn>

            <v-btn icon>
              <v-icon>mdi-dots-vertical</v-icon>
            </v-btn>
          </v-toolbar>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We have the color prop set to primary to make it blue.

And we add the dark prop to make the icons white.

Prominent Toolbar with Background

We can add a toolbar with a background image.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-toolbar dark prominent src="https://cdn.vuetifyjs.com/images/backgrounds/vbanner.jpg">
          <v-app-bar-nav-icon></v-app-bar-nav-icon>

          <v-toolbar-title>App</v-toolbar-title>

          <v-spacer></v-spacer>

          <v-btn icon>
            <v-icon>mdi-export</v-icon>
          </v-btn>
        </v-toolbar>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We have the v-toolbar component with the dark and prominent props to change the background color.

src sets the URL of the image.

Conclusion

We can add toolbars with various colors, sizes, and a background image.

Categories
Vuetify

Vuetify — Embedding Toolbar

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Extended Toolbar

A toolbar can be extended with the extension slot:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-card color="grey lighten-4" flat height="200px" tile>
          <v-toolbar extended>
            <v-app-bar-nav-icon></v-app-bar-nav-icon>

            <v-toolbar-title>Title</v-toolbar-title>

            <v-spacer></v-spacer>

            <v-btn icon>
              <v-icon>mdi-magnify</v-icon>
            </v-btn>

            <v-btn icon>
              <v-icon>mdi-heart</v-icon>
            </v-btn>

            <v-btn icon>
              <v-icon>mdi-dots-vertical</v-icon>
            </v-btn>
          </v-toolbar>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

Extension Height

The extension height can be changed with the extension-height prop:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-card color="grey lighten-4" flat height="200px" tile>
          <v-toolbar extended extension-height="150">
            <v-app-bar-nav-icon></v-app-bar-nav-icon>

            <v-toolbar-title>Title</v-toolbar-title>

            <v-spacer></v-spacer>

            <v-btn icon>
              <v-icon>mdi-magnify</v-icon>
            </v-btn>

            <v-btn icon>
              <v-icon>mdi-heart</v-icon>
            </v-btn>

            <v-btn icon>
              <v-icon>mdi-dots-vertical</v-icon>
            </v-btn>
          </v-toolbar>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

Collapse

A toolbar can be collapsed to save space.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-card color="grey lighten-4" flat height="200px" tile>
          <v-toolbar collapse>
            <v-btn icon>
              <v-icon>mdi-magnify</v-icon>
            </v-btn>

            <v-btn icon>
              <v-icon>mdi-dots-vertical</v-icon>
            </v-btn>
          </v-toolbar>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

to add a collapsed toolbar with the collapse prop.

Flexible Toolbar and Card Toolbar

We can make a flexible toolbar that’s added to cards.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-card flat>
          <v-toolbar color="primary" dark extended flat>
            <v-app-bar-nav-icon></v-app-bar-nav-icon>
          </v-toolbar>

          <v-card class="mx-auto" max-width="700" style="margin-top: -64px;">
            <v-toolbar flat>
              <v-toolbar-title class="grey--text">Title</v-toolbar-title>

              <v-spacer></v-spacer>

              <v-btn icon>
                <v-icon>mdi-magnify</v-icon>
              </v-btn>

              <v-btn icon>
                <v-icon>mdi-apps</v-icon>
              </v-btn>

              <v-btn icon>
                <v-icon>mdi-dots-vertical</v-icon>
              </v-btn>
            </v-toolbar>

            <v-divider></v-divider>

            <v-card-text style="height: 200px;"></v-card-text>
          </v-card>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We put the v-toolbar in a v-card to embed the toolbar on the card.

And we have another v-card inside it with another v-toolbar to display the toolbar content.

Conclusion

We can add toolbars to various containers with Vuetify.

Categories
Vuetify

Vuetify — Dynamic Toolbar Content and System Bar

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Floating Toolbar with Search

We can add a floating toolbar easily with Vuetify.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-card
          class="pa-4"
          flat
          height="300px"
          img="https://cdn.vuetifyjs.com/images/toolbar/map.jpg"
        >
          <v-toolbar dense floating>
            <v-text-field hide-details single-line></v-text-field>

            <v-btn icon>
              <v-icon>mdi-dots-vertical</v-icon>
            </v-btn>
          </v-toolbar>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We add the v-toolbar in a v-card .

The img prop has the URL of the background image as its value.

The floating prop will make it float on top of the card.

Contextual Action Bars

We can add the contextual action bar that changes when some action is taken.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-card max-width="500" class="mx-auto">
          <v-toolbar :color="selection.length ? 'grey darken-4' : 'deep-purple accent-4'" dark>
            <v-app-bar-nav-icon v-if="!selection.length"></v-app-bar-nav-icon>
            <v-btn v-else icon @click="selection = []">
              <v-icon>mdi-close</v-icon>
            </v-btn>

            <v-toolbar-title>{{ selection.length ? `${selection.length} selected` : 'App' }}</v-toolbar-title>

            <v-spacer></v-spacer>

            <v-scale-transition>
              <v-btn v-if="selection.length" key="export" icon>
                <v-icon>mdi-export-variant</v-icon>
              </v-btn>
            </v-scale-transition>
            <v-scale-transition>
              <v-btn v-if="selection.length" key="delete" icon>
                <v-icon>mdi-delete</v-icon>
              </v-btn>
            </v-scale-transition>

            <v-btn icon>
              <v-icon>mdi-dots-vertical</v-icon>
            </v-btn>
          </v-toolbar>

          <v-card-text>
            <v-select v-model="selection" :items="items" multiple label="Select an option"></v-select>
          </v-card-text>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    items: ["apple", "orange", "grape"],
    selection: []
  }),
};
</script>

We have the v-scale-transiton component that changes with the selection state.

The selection state is controlled by the v-select component.

We also set the v-toolbar-title text with the selection change.

Also, we change the color prop when the selection changes.

So when we select items from the dropdown, the toolbar will change.

System Bars

A system bar is another kind of toolbar.

It looks like the system bar on an Android phone.

To add one, we use the v-system-bar component:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-system-bar dark color="primary">
          <v-spacer></v-spacer>
          <v-icon>mdi-wifi-strength-5</v-icon>
          <v-icon>mdi-signal-cellular-outline</v-icon>
          <v-icon>mdi-battery</v-icon>
          <span>12:30</span>
        </v-system-bar>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We add a v-system-bar component with some icons inside.

Conclusion

We can add a system bar with some icons and text inside.

Also, we can make the toolbar change when its state changes.