Categories
TypeScript

An Introduction to TypeScript Interfaces

The big advantage of TypeScript over plain JavaScript is that it extends the features of JavaScript by adding features that ensure type safety of our program’s objects.

It does this by checking the shape of the values that objects take on. Checking the shape is called duck typing or structural typing. Interfaces are one way to fill the role of naming data types in TypeScript.

They are very useful for defining a contract within our code in TypeScript programs.

In the last part, we’ll look at how to define a TypeScript interface and how to add properties to it. We also look at excess property checks for object literals and defining types for interfaces.

In this article, we’ll look at how to extend interfaces and write interfaces that extend classes.


Extending Interfaces

In TypeScript, interfaces can extend each other just like classes. This lets us copy the members of one interface to another and gives us more flexibility in how we use our interfaces.

We can reuse common implementations in different places and we can extend them in different ways without repeating code for interfaces.

We can extend interfaces with the extends keyword. We can use the keyword to extend one or more interfaces separated by commas. For example, we can use the extends keyword as in the code below:

interface AnimalInterface {
  name: string;  
}
interface DogInterface extends AnimalInterface {
  breed: string;
  age: number;
}
interface CatInterface extends AnimalInterface {
  breed: string;
}

Then, to implement the Dog and Cat interfaces, we have to implement the members listed in the Animal interface as well. For example, we would implement them as in the following code:

interface AnimalInterface {
  name: string;  
}
interface DogInterface extends AnimalInterface {
  breed: string;
  age: number;
}
interface CatInterface extends AnimalInterface {
  breed: string;
}
class Cat implements CatInterface {
  name: string = 'Mary';
  breed: string = 'Persian';
}
class Dog implements DogInterface {
  name: string = 'Jane';
  breed: string = 'Labrador';
  age: number = 10;
}

As we can see, we have added everything from the parent interface and the child interface in our class implementations. We can also extend multiple interfaces as in the following code:

interface MachineInterface {
  name: string;  
}
interface ProductInterface {
  price: number;
}
interface ClockInterface extends MachineInterface, ProductInterface {
  tick(): void;
}
class Clock implements ClockInterface {
  name: string = 'Quartz';
  price: number = 20;
  tick() {
    console.log('tick');
  }
}

As we can see from the code above, we have all the members of the Machineinterface, ProductInterface, and ClockInterface if we implement the ClockInterface as we did with the Clock class.

Note that if we have the same member name in multiple interfaces then they must have identical data types as well. For example, if we have the following code:

interface MachineInterface {
  name: string;  
}
interface ProductInterface {
  name: number;
}
interface ClockInterface extends MachineInterface, ProductInterface {
  tick(): void;
}
class Clock implements ClockInterface {
  name: string = 'Quartz';
  price: number = 20;
  tick() {
    console.log('tick');
  }
}

The Typescript compiler would reject it since we have name being a string in the MachineInterface and name being a number in the ProductInterface.

If we try to compile the code above with the TypeScript compiler, we would get the error:

Interface ‘ClockInterface’ cannot simultaneously extend types ‘MachineInterface’ and ‘ProductInterface’. Named property ‘name’ of types ‘MachineInterface’ and ‘ProductInterface’ are not identical.(2320)

Hybrid Types

We can override the type that’s inferred by an object with the type assertion operator, which is denoted by the as keyword in TypeScript.

This way, we can use code that has dynamic types while we keep using the interface. For example, we can write the following code:

interface Person {
  name: string;
  (name: string): string;
}
function getPerson(): Person {
  let person = (function (name: string) { }) as Person;
  person.name = 'Joe';
  return person;
}
let person = getPerson();
person('Joe');

In the code above, we have the person variable in the getPerson function which we set explicitly with the Person type so that we can assign properties listed in the Person interface to the person variable.


Interfaces Extending Classes

TypeScript interfaces can extend classes. This means that an interface can inherit the members of a class but not their implementation. The class, in this case, acts as an interface with all the members declared without providing the implementation.

This means that when we extend a class with private or protected members, the interface can only be implemented by that class or a sub-class of it. For example, we can write an interface that extends a class as we do in the following code:

class Animal {
  name: string = '';
  private age: number = 0;  
}
interface BirdInterface extends Animal {
  breed: string;
  color: string;  
}
class Bird extends Animal implements BirdInterface {
  name: string = 'Bird';    
  breed: string = 'pigeon';
  color: string = 'Gray';
}

In the code above, we first created the class Animal which has a public member name and a private member age. Then, we added a BirdInterface which extends the Animal class by adding the public members breed and color.

Then, in the Bird class, which extends the Animal class and implements the BirdInterface, we have all the members of the BirdInterface plus the public members of the Animal class.

Since private members can’t be accessed outside of a class, we can’t access the member age in the Bird class. We also can’t add another age member in the Bird class.

Otherwise, we would get the errors:

Class ‘Bird’ incorrectly extends base class ‘Animal’. Property ‘age’ is private in type ‘Animal’ but not in type ‘Bird’.(2415)” and “Class ‘Bird’ incorrectly implements interface ‘BirdInterface’. Property ‘age’ is private in type ‘BirdInterface’ but not in type ‘Bird’.(2420)

However, if we change the age member in the Animal class to a protected member, which can be accessed by all sub-classes that extends Animal, then we can reference it in the Bird class as in the following code:

class Animal {
  name: string = '';
  protected age: number = 0;  
}
interface BirdInterface extends Animal {
  breed: string;
  color: string;    
}
class Bird extends Animal implements BirdInterface {
  name: string = 'Bird';    
  breed: string = 'pigeon';
  color: string = 'Gray';
  age: number = 1;
}

This is the same with methods. Private methods can’t be accessed by anything outside the class that it’s defined in and can’t be overridden by any sub-class or interface. For example, if we have the following code:

class Animal {
  name: string = '';
  private age: number = 0;  
  private getAge() {
    return this.age;
  }
}
interface BirdInterface extends Animal {
  breed: string;
  color: string;   
  getAge(): number;
}
class Bird extends Animal implements BirdInterface {
  name: string = 'Bird';    
  breed: string = 'pigeon';
  color: string = 'Gray';  
  getAge() { return 0 };
}

Then we would get the errors:

Class ‘Bird’ incorrectly extends base class ‘Animal’. Property ‘age’ is private in type ‘Animal’ but not in type ‘Bird’.(2415)” and “Class ‘Bird’ incorrectly implements interface ‘BirdInterface’. Property ‘age’ is private in type ‘BirdInterface’ but not in type ‘Bird’.(2420)

However, we can override protected methods in sub-classes as in the following code:

class Animal {
  name: string = '';
  private age: number = 0;  
  protected getAge() {
    return this.age;
  }
}
interface BirdInterface extends Animal {
  breed: string;
  color: string;     
}
class Bird extends Animal implements BirdInterface {
  name: string = 'Bird';    
  breed: string = 'pigeon';
  color: string = 'Gray';  
  getAge() { return 0 };
}

In TypeScript, interfaces can extend each other just like classes. This lets us copy the members of one interface to another and gives us more flexibility in how we use our interfaces.

We can reuse common implementations in different places and we can extend them in different ways without repeating code for interfaces.

Also, TypeScript interfaces can extend classes. This means that an interface can inherit the members of a class but not their implementation. The class, in this case, acts as an interface with all the members declared without providing the implementation.

This means that when we extend a class with private or protected members, the interface can only be implemented by that class or a sub-class of it.

Categories
JavaScript Tips

JavaScript Tips — Rounding and JSON

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some tips we should follow when writing JavaScript code.

Rounding Number to N Decimal Places

We can round numbers with the toFixed method.

It takes the number of decimal places to round to as the argument.

For example, we can write:

let num = 2.575949004696;  
num = num.toFixed(3);

It returns a string with the rounded number.

So we get “2.576” as the new value of num .

Floating-Point Problems

We should be aware that adding floating numbers may not give the results we expect.

For example, if we have:

0.1 + 0.2 === 0.3

Then we get false .

This is because floating-point numbers are 64-bit binary numbers.

The conversion will give us discrepancies.

To fix this, we use the toFixed or toPrecision methods.

Check the Properties of an Object When using a for-in Loop

To check the properties of an object with the hasOwnProperty method.

For example, we can write:

for (let name in object) {  
  if (object.hasOwnProperty(name)) {  
    // ...  
  }  
}

We check that the property is in the object itself rather than its prototypes with hasOwnProperty .

Comma Operator

The comma operator always returns the last item in the list.

So if we have:

const a = (100, 99);

Then a is 99.

Cache Variables that Need Calculation or Querying

To speed up our app, we should cache variables whenever we can.

For example, we write:

const navRight = document.querySelector('#right');   
const navLeft = document.querySelector('#left');   
const navUp = document.querySelector('#up');   
const navDown = document.querySelector('#down');

Then we won’t use the variables anywhere instead of querying the DOM all the time.

Verify the Argument Before Passing it to isFinite()

isFinite lets us check if an expression returns a finite number.

For example, we can write:

isFinite("foo")

it returns false .

If we have:

isFinite(10)

it returns true .

Serialization and Deserialization with JSON

We can use the JSON object to serialize and deserialize JSON.

To convert objects to JSON, we use JSON.stringify .

To parse JSON strings into objects, we use JSON.parse .

We can use JSON.stringify by writing:

const str = JSON.stringify(obj)

It returns the stringified JSON object.

Also, we can pass in additional arguments to make the string more readable:

const str = JSON.stringify(obj, undefined, 2);

The 3rd argument has the number of spaces to indent.

To parse a JSON string to an object, we write:

const obj = JSON.parse(str)

where str is the JSON string.

Avoid the use of eval() or the Function Constructor

We should avoid eval and the Function constructor since they both run code from strings.

This is security risk since we can pass malicious strings into it.

Code in strings can’t be optimized and they’re hard to debug.

Avoid Using with()

with should never be used since the scope is confusion.

Variables can be inserted at the global scope, which is also not good.

If 2 variables have the same name in a with statement, it can overwrite the value.

Conclusion

There’re things in JavaScript we should avoid.

We can round numbers and manipulate JSON with the standard library.

Categories
JavaScript Tips

JavaScript Tips — Arrays and Numbers

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some tips we should follow when writing JavaScript code.

Verify that a Given Argument is a Number

We can check if a given argument is a number with the isNaN and isFinite functions.

For example, we can write:

function isNumber(n){
  return !isNaN(parseFloat(n)) && isFinite(n);
}

isNaN checks that a number isn’t NaN and isFinite checks that a number is finite.

If isNaN returns false and isFinite returns true , then we know that n is a number.

We use parseFloat to try to transform it to a floating-point number first.

Verify that a Given Argument is an Array

We can check if a variable is an array by using the Array.isArray method.

For example, we can write:

Array.isArray(obj)

This works everywhere including outside frames and other contexts.

If we don’t have to worry about frames, we can also use:

Object.prototype.toString.call(obj) === '[object Array]'

or:

arr instanceof Array

to do the check.

Get the Max or the Min in an Array of Numbers

We can get the max or min of a number with the Math.max or Math.min methods.

And we can use the spread operator to spread the arguments.

For example, we can write:

const numbers = [5, 4, 4, 7, 2, 6, 2];
const maxInNumbers = Math.max(...numbers);
const minInNumbers = Math.min(...numbers);

We spread the numbers array as arguments of the Math.max and Math.min methods to get the max and min number from the array respectively.

Empty an Array

We can empty an array by setting the length property to 0.

For example, we can write:

const myArray = [1, 2, 3];
myArray.length = 0;

Then myArray will be emptied.

Don’t Use delete to Remove an Item from an Array

delete isn’t supposed to be used to remove an item from an array.

So we shouldn’t write code like;

const myArray = [1, 2, 3];
delete myArray[1];

Instead, we use splice :

const myArray = [1, 2, 3];
myArray.splice(1, 1);

to remove the item with index 1.

The first argument is the index to delete.

The 2nd argument is how many items to delete starting from the index.

Truncate an Array Using Length

We can set the length property to a number bigger than 0 to truncate an array.

For example, we can write:

const myArray = [1, 2, 3, 4, 5, 6];
myArray.length = 2;

Now myArray would have length 2.

Use Logical and/or for Conditions

We can use && or || to create conditional expressions.

For example, we can write:

const foo = 10;
(foo === 10) && doSomething();
(foo === 5) || doSomething();

(foo === 10) && doSomething(); is the same as:

if (foo === 10){
  doSomething();
}

and (foo === 5) || doSomething(); is the same as:

if `(foo === 5){
  doSomething();
}`

Use the map() Method to Loop Through an Array’s Items

If we need to map each array entry to a new value, we can use the map method.

For example, we can write:

const squares = [1, 2, 3].map((val) => {
  return val ** 2;
});

We square each number in the array with map .

So we get:

[1, 4, 9]

as the value of squares .

Conclusion

We can do many things with arrays and numbers.

Categories
JavaScript Tips

JavaScript Tips — Randomness and Arrays

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some tips we should follow when writing JavaScript code.

Get a Random Number in a Specific Range

We can get a random number from a specify range with the Math.random() method.

For example, we can write:

const min = 2;
condt max = 10;
const x = Math.floor(Math.random() * (max - min + 1)) + min;

Since Math.random() only returns a number between 0 and 1, we’ve to multiply what it returns with max — min + 1 .

Then we add that to min to make it within range.

Generate an Array of Numbers with Numbers from 0 to Max

We can create an array of numbers by using a for loop.

For example, we can write:

let numbersArray = [] , max = 100;

for (let i = 1; numbersArray.push(i++) < max);

We use the for loop to increment i by 1 with each iteration.

And then we end with we reach max — 1 .

i++ increments and returns the number we return.

Generate a Random Set of Alphanumeric Characters

We can create a random set of alphanumeric characters by using the toString and substr methods.

For instance, we can write:

function generateRandomAlphaNum(len) {
  let randomString = "";
  while (randomString.length < len) {
    randomString += Math.random()
      .toString(36)
      .substr(2);
  }
  return randomString.substr(0, len);
}

We have the generateRandomAlphaNum function.

And we use the while loop to generate the string up to the given len value.

To generate the character we use the Math.random() with the toString and substr calls to create the character.

Shuffle an Array of Numbers

We can shuffle an array of number with the sort method.

For example, we can write:

const numbers = [5, 2, 4, 3, 6, 2];
numbers = numbers.sort(() => { return Math.random() - 0.5});

We return a random number that’s positive or negative so that the order is switched only when it’s positive.

That’s random so we shuffle them.

A String Trim Function

We can trim all whitespace with the replace method and some regex.

For example, we can write:

str.replace(/^s+|s+$/g, "")

We trim all whitespace with the regex.

JavaScript strings also have the trim method to do the trimming.

Append an Array to Another Array

To append an array to another array, we can use the spread operator with push .

For example, we can write:

array1.push(...array2);

We can also use the spread operator without push :

const arr = [...arr1, ...arr2];

Transform Arguments into an Array

We can get arguments as an array with the rest operator.

For example, if we have:

const foo = (...args) => {}

Then whatever we have in args will be an array.

We can pass in anything into foo and it’ll end up in args .

Conclusion

We can generate random entities with the Math.random method.

Also, we can append items to arrays and get arguments.

Categories
JavaScript Tips

JavaScript Tips— Rules and Random

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some tips we should follow when writing JavaScript code.

Linter Rules and Formatting Style

We can use linters to automatically make styles consistent.

This way, there won’t be any discrepancies between different styles.

And we won’t have to point out bad formatting in code reviews.

There’s the Standard JS and Airbnb styles for example.

They all have plugins for ESLint, which is the most popular linter for JavaScript code.

Nice Async Code

We shouldn’t have async code that has too much nesting.

To avoid that, we can use promises.

Instead of writing nested async callbacks like:

asyncFunc1((err, result1) => {
  asyncFunc2(result1, (err, result2) => {
    asyncFunc3(result2, (err, result3) => {
      console.lor(result3)
    })
  })
})

We write:

asyncFuncPromise1()
  .then(funcPromise2)
  .then(funcPromise3)
  .then((result) => console.log(result))
  .catch((err) => console.error(err))

We have no nesting in the 2nd example except for the callbacks.

It’s much easier to read.

We can convert existing code to promises:

const fs = require('fs')
const promisify = require('es6-promisify')

const readFile = promisify(fs.readFile)
readFile(filePath)
  .then((data) => JSON.parse(data))

We used the es6-promisify package to convert Node styles async callbacks to promises.

Write Performant Code

We can optimize the code when we need to.

Instead of focusing on micro-bencmarks, we should watch for its true impacts.

This way, we won’t waste too much time unnecessarily optimizing our code.

Use let and const

We should use let and const to declare our variables.

This way, we always have block scoped variables that can’t be used before it’s declared.

Falsy Variables

undefined, null, 0, false, NaN, '' (empty string) are all falsy.

We definitely have to be aware of these since when we convert them to boolean, they’ll all be false.

Create an Object Constructor

We can create an object constructor so that we can use them as templates to create new objects.

For example, we can write:

function Person(firstName, lastName){
  this.firstName =  firstName;
  this.lastName = lastName;
}

const james = new Person("james", "smith");

We have a constructor with the firstName and lastName parameters we can use with the new operator to create new objects.

Be Careful When Using typeof, instanceof, and constructor

We can use typeof to check for types of various kinds of values.

It works great with primitive values and functions.

However, typeof null returns 'object' .

Using typeof on non-function objects also returns 'object' .

constructor is a property of the prototype of an object that can be overridden by code.

instanceof lets us check for an instance of a constructor.

The check is done all the way up the prototype chain.

We can use them by writing:

const arr = [1, 2, 3];
typeof arr;
arr instanceof Array
arr.constructor();

typeof arr returns 'object'

arr instanceof Array returns true .

arr.constructor() returns [] .

Create a Self-calling Function

We can create a self calling function by wrapping it in parentheses and calling it.

For example, we can write:

(function(){
    // ...
})();

(function(a, b){
  var result = a + b;
  return result;
})(1, 2)

We can keep private variables in the function.

Get a Random Item from an Array

We can get a random value from an array by writing:

const items = [2, 'foo', 'bar', 1, false];
const randomItem = items[Math.floor(Math.random() * items.length)];

We generate the index by using Math.floor(Math.random() * items.length) .

And we pass that into items .

This way, we get the index with the array.

Conclusion

We can use linters to keep code formatting consistent.

Also, we can use various constructs to make code easier to understand.

We can also get random items from an array easily.