Categories
TypeScript

JavaScript Features that we can use in TypeScript Code

TypeScript is a natural extension of JavaScript that’s used in many projects in place of JavaScript.

However, not everyone knows how it actually works.

In this article, we’ll look at JavaScript features that we can adopt in our JavaScript code.

Using Arrow Functions

Arrow functions are a way to concisely define functions.

They’re often used to define functions that are arguments of other functions.

In JavaScript, functions are regular objects, so they can be passed into another function as arguments.

For instance, we have an example below:

const addPrices = (...rest) => {
  return rest.reduce((total, b) => total + b, 0);
};

In the code above, the outer function is a function.

We also passed a function into the reduce method.

There are 3 parts to an arrow function.

We have the input parameters, the equal sign, the great than sign, and the result value.

return and the curly braces are only required only if the arrow functions need to run more than one statement.

Arrow functions can be used anywhere that a function is used.

Working with Arrays

JavaScript arrays are similar to the ones that are in other programming languages.

However, they can be dynamically resized and can contain any combination of values.

This means that they can be in any combination of types.

The size of an array isn’t specified when it’s created and it’ll be allocated automatically as items are added or removed.

JavaScript arrays are zero-based and are defined using square brackets.

It can have initial contents separated by commas.

For instance, we can write:

const names = ['joe', 'jane', 'alex'];

Then we create an array with all strings.

Also, we elements in an array can be read or set using the square brackets in using some array methods.

It has a concat method which takes one or more arrays as arguments to combine it with another array.

join takes a separator string that combines the entries into a string and returns it, with each value separated by the separator .

pop removes and returns the last item of an array.

shift removes and returns the first element in the array.

push takes an item as an argument and add it to the end of the array in-place.

unshiift takes an item and add it to the start of an array.

reverse returns a new array with the items of the original array reversed.

slice(start, end) returns a second of an array.

sort sorts an array optionally with a comparison function for custom sorting.

splice(index, count) removes an item from the index and up to the count .

every(test) returns true if the test function returns true when it’s called with all values of an array.

some(test) returns true if the test function returns true when it’s called with some entries of the array.

filter(test) returns an array with the items that returns true when they’re called with test .

find(test) returns the first instance of something that returns true when called with test .

findIndex(test) returns the index of the first instance of something that returns true when called with test .

forEach(callback) loops through the and calls callback on each item.

includes(value) returns true if value is inside the array.

map(callback) returns a new array that has the results of invoking callback for all items in the array.

reduce(callback) returns an accumulated value produced by running the callback for every item of the array.

Spread Operator

Spread operator can be used on arrays in various ways.

We can use them for spreading array entries as arguments into a function call.

For instance, we can write:

const addPrices = (...rest) => {
  return rest.reduce((a, b) => a + b, 0);
};

const prices = [1, 2];
const totalPrice = addPrices(...prices);

Then we spread the prices array as arguments into the addPrices function.

Conclusion

Arrow functions are useful in our TypeScript code.

Array methods are also very useful for finding things and manipulating items.

There are also methods to manipulate methods in place, like adding and remove items.

The spread operator to spread items that are in an array into arguments of a function.

Categories
TypeScript

TypeScript’s Relation to JavaScript

TypeScript is a natural extension of JavaScript that’s used in many projects in place of JavaScript.

However, not everyone knows how it actually works.

In this article, we’ll look at how TypeScript makes JavaScript projects better.

Type Coercion

JavaScript does data type coercion of its variables when it does certain operations.

It produces consistent results, we just have to know how it works.

If we use the == operator to compare objects, then type coercion will be done before comparison operations are done.

For instance, we may have:

let applePrice = 1;
let orangePrice = '1';
if (applePrice == orangePrice) {
  //...
}

Then both will be converted to numbers before comparison.

When we write:

let totalPrice = applePrice + orangePrice;

then both will be converted to strings before concatenation, which is probably not what we want.

Avoiding Unintentional Type Coercion

To make our lives easier, we should take steps to avoid unintentional data type coercion.

To do that, we can use the === operator for comparisons and convert types explicitly first before doing concatenation.

For instance, we can write:

let applePrice = 1;
let orangePrice = '1';
if (applePrice === orangePrice) {
  //...
}

for comparison, and:

let totalPrice = Number(applePrice) + Number(orangePrice);

=== does comparison without applying data type coercion.

And we prevent concatenation with the + sign by using the Number function to convert both operands to numbers before adding them.

The Benefits of Explicit Type Coercion

We can use type coercion to our benefit.

For instance, we can use the || to coerce null , undefined or other falsy values to false so that the || operator will return the 2nd operand.

For instance, if we have:

let firstName;
let secondName = firstName || "jane";

Since firstName is undefined , then || operator will coerce firstName to false and return the 2nd operand, which is 'jane' .

Therefore, we can use the || operator to return a default value inc case the first is falsy.

Working with Functions

Functions are a building block of JavaScript.

We can define functions to run code that’s invoked repeatedly.

For instance, we can write:

const addPrices = (first, second) => {
  return first + second;
};

Then we can call it by writing:

let applePrice = 1;
let orangePrice = 2;
const totalPrice = addPrices(applePrice, orangePrice);

The addPrices function received number values when we call it above.

However, JavaScript doesn’t do any validation when we pass in the variables to the function.

Therefore, we may get unexpected results.

This is where TypeScript can help, we can validate the data types before the function is called when the arguments are passed in.

Function Results

In JavaScript, function return types are determined by the value we return.

It can be anything, depending on what we return.

For instance, if we have:

let applePrice = 1;
let orangePrice = 2;
const totalPrice = addPrices(applePrice, orangePrice);

then we return a number.

On the other hand, if we have:

let applePrice = 1;
let orangePrice = '2';
const totalPrice = addPrices(applePrice, orangePrice);

Then addPrices returns a string.

This may be a problem since we probably don’t want to concatenate instead of add.

Only 2 numbers are added together. Otherwise, it’s concatenated.

Also, we don’t have to pass in all arguments.

For instance, if we add the 3rd argument to addPrices as follows:

const addPrices = (first, second, third) => {
  return first + second + third;
};

let applePrice = 1;
let orangePrice = 2;
const totalPrice = addPrices(applePrice, orangePrice);

Then we get NaN because third is undefined , and adding number and undefined results in NaN .

Avoiding Argument Mismatch Problems

In JavaScript, we can set a default value for a parameter that we know are optional.

For instance, we can write:

const addPrices = (first, second, third = 0) => {
  return first + second + third;
};

or, we can use the rest parameter notation to put some or all arguments into an array.

If third has no value passed in, then third is set to 0.

We can write:

const addPrices = (...rest) => {
  return rest.reduce((a, b) => a + b, 0);
};

Then rest is an array as the spread operator puts all the arguments into an array.

Conclusion

TypeScript just uses JavaScript’s type system.

It tames the dynamic nature of JavaScript’s type system by restricting the types of parameters and return types of functions.

Categories
TypeScript

TypeScript Project Setup— Modules and Options

TypeScript is a natural extension of JavaScript that’s used in many projects in place of JavaScript.

However, not everyone knows how it actually works.

In this article, we’ll look at how to use modules in a TypeScript project.

Also, we look at the options we can set for the TypeScript compiler

Module Formats

Before ES6, we have different kinds of JavaScript modules to solve the problems with organizing code into small pieces.

There were multiple types of modules before that.

TypeScript has built-in support for standard JavaScript modules.

So we can use the export and import syntax for exposing module members and using them respectively.

For instance, we can export a member by writing:

module.ts

export const printMsg = (msg: string): void => {  
  console.log(msg);  
};

Then we can use it in another module by importing it.

The export keyword lets us export a member in a module.

We can export anything at the top level.

Then we can use our module by using the import keyword.

For example, we can write:

import { printMsg } from "./module";  
printMsg("foo");

We import the printMsg function with the import statement.

Then we called it.

We should get 'foo' logged in the console log just like when we called it in the same module.

If we build the code, then they’ll be bundled together.

And the code will vary depending on the target that we choose.

We won’t get any references to modules in our build artifacts.

If we’re using a standalone TypeScript project, then we’ve to specify in the lib section of tsconfig.json .

We should specify the module system that we want to compile with the Typescript compiler with the module setting.

The following choices can be set for this setting.

none will disable modules.

commonjs selects the CommonJS module format which is supported by Node

amd selected Asynchronpus Module Definition, supported by the RequireJs module loader.

system selects the module format supported by the SystemJS module folder.

umd selected the Universal Module Definition module format.

es2015 or es6 selects the module format in the ES6 language specification.

esnext selects modules features that have been proposed for the next version of the JavaScript language.

To add the option, we can write:

{    
  "compilerOptions": {    
    "target": "es2018"  
    //...  
    "module": "commonjs"  
  }  
}

Once we have that, we can use the module system of our choice.

We can use commonjs for Node apps, but es6 is the standard so we should use that.

Modules are usually located in the node_modules folder.

TypeScript compiler uses the classic resolution with module is set to es2015 , system , or amd .

The resolution style can be set using the moduleResolution configuration property in tsconfig.json using the classic or node value.

Compiler Configuration Settings

There are many other compiler configuration options in addition to the module and target settings.

allowJs sets whether we wan to include JavaScript files in the compilation process.

allowSyntheticDefaultImports allows imports from modules that don’t declare a default export.

It’s used to increase code compatibility.

baseUrl specifies the root location to resolve module dependencies.

checkJs tells the compiler to check JavaScript code for common errors.

declaration is the option we set to produce type declaration files that provide type information for JavaScript code.

downlevelIteration enables support for iterators when targeting an older version of JavaScript.

emitDecoratorMetadata option lets us include decorator metadata in the JavaScrti emitted by the compiler and is used with the experimentalDecorations option.

experimentalDecorators enables support for decorators.

forceConsistentCasingInFileNames enures the names of import statements match the case used by the imported file.

importHelpers determines whether the helper code is added to JavaScript to reduce the amount of code that’s produced overall.

isolateModules treats each file as a separate module.

jsx specifies how HTML elements in JSX/TSX files are processed.

jsxFactory specifies the name of the factory function that’s used to replace HTML files in JSX/TSX files.

noImplicitAny prevents implicit use of the any type.

noImplicitReturns option requires all paths in a function to return a result.

resolveJsonModule allows us to import JSON files as if they’re modules.

skipLibCheck lets us skip the checks for declaration files.

strict enables stricter checking of TypeScript code.

strictNullChecks prevents null and undefined from being accepted as values for other types.

suppressExcessPropertyErrors stops the compiler from generating errors for objects that defined properties, not in the specified range.

typeRoots specifies the root location the compiler uses to look for declaration files.

types specifies a list of declaration files to include when compiling.

Conclusion

With the TypeScript compiler, we can specify lots of options to control how code is converted from TypeScript to JavaScript code we can use.

Modules should be used in our project, but we’ve to specify the kind of modules we want to build first.

Categories
TypeScript

JavaScript Object Features in TypeScript — Maps and Sets

TypeScript is a natural extension of JavaScript that’s used in many projects in place of JavaScript.

However, not everyone knows how it actually works.

In this article, we’ll look at how to use maps to store key-value pairs, and sets to store items without duplicates.

Storing Key-Value Pairs with Maps

We can store key-value pairs with maps.

The difference between maps and object literals is that we can have keys other than strings and symbols.

We can define a map with JavaScript’s built-in Map constructor.

For instance, we can write the following to do that:

const data = new Map();
const key = {
  foo: 'bar'
};
data.set("foo", "bar");
data.set(key, 2);

As we can see, we have a map with a string key and an object key in a map.

The set method stores a value with the given key.

To get a value by a key, we can use the get method with the key as an argument.

There’s also the keys method to get an iterator with the keys of a map.

The values method returns the values of a map.

The entries method returns an iterator with the key-value pairs of a map.

This is the default iterator for maps.

Use Symbols for Map Keys

Symbols can be used for Map and object keys.

For instance, we can write:

const data = new Map();
const symbol = Symbol();

data.set("foo", "bar");
data.set(symbol, 2);

Each symbol that’s created is different so we’ve to store it in a variable so that we can get the value with the symbol key later.

This is also the same for non-primitive keys.

Therefore, we can have multiple symbols with the same name but with different references.

For instance, we can write:

const symbol1 = Symbol('foo');
const symbol2 = Symbol('foo');

They have the same name but are different.

So:

symbol1 === symbol2

would return false .

We can get the value with the given symbol key by writing:

const value = data.get(symbol);

Storing Data by Index

We can store data without duplicates with a Set instance.

For instance, we can write:

const set = new Set([1, 2, 3]);

Then we get a set with those values returned.

Sets can’t have duplicates, so we if we write:

const set = new Set([1, 2, 3, 1]);

We still get a set with 1, 2, and 3 in it as only the first instance of something is kept.

Sets have the size property that returns the size of a set.

Set instances have some useful methods to get items from them, add/remove items, and iterate through them.

add takes the value that we want to add to a set as the argument.

entries returns an iterator for all entries in the order that they were added.

has returns true is a set has the specified value.

forEach takes a callback that’s run on every entry of a set.

Conclusion

We can use maps to store key-value pairs. It can store keys that aren’t strings or symbols, unlike objects.

Sets can be used to store data without duplicates.

They’re both iterable objects, and we can call a method to return iterators from them.

Categories
TypeScript

TypeScript Project Setup- Hot Reloading, Libraries and Build Targets

TypeScript is a natural extension of JavaScript that’s used in many projects in place of JavaScript.

However, not everyone knows how it actually works.

In this article, we’ll look at how to create a TypeScript project that does hot reloading.

We also look at how to choose type definitions to include in our project so that we can use the features we want.

Using Watch Mode

To make our lives easier, we should make sure that our project recompiles and reloads each time we save our code.

This way, we don’t have to recompile and restart our app every time we change code.

With watch mode, we watch the source code files that are changed, and then the compiler will rebuild the app automatically.

Our TypeScript project should already have the TypeScript compiler installed already.

Then we run:

tsc --watch

to watch for code changes and recompile the code.

Now if we change our code, we should see messages when building and show any compiler errors if they’re found.

Automatically Executing Code After Compilation

tsc --watch only recompiles the files and don’t run them.

To run them, we’ve to use another program.

The ts-watch package starts the compiler in watch mode, observes the 9output and runs commands based on compilation results.

For instance, we can write:

npx tsc-watch --onsuccess "node dist/index.js"

to run tsc-watch without installing it and runs dist/index.js with Node on success.

Starting the Compiler Using NPM

We can add a start script in package.json to run commands to start the project.

So we can write:

{
  //...
  "scripts": {
    "start": "tsc-watch --onsuccess "node dist/index.js""
  },
  //...
}

Version Targeting

With the TypeScript compiler, we can target different versions of JavaScript in our final build.

We can go as low as ES3 to the newest versions.

This way, we can support legacy browsers easily.

For instance, we can write:

{
  "compilerOptions": {
    "target": "es5",
    //...
  }
}

to support browsers like Internet Explorer, which don’t have the support of the latest JavaScript features built-in.

The following versions are supported.

es3–3rd edition of the language. It’s the default value when target isn’t specified. It’s defined in 1999

es5 — the 5th edition of JavaScript released in December 2009

es6–6th edition of JavaScript which added lots of features for creating complex apps like classes, modules, arrow functions, promises, etc.

es2015 — same as es6

es2016–7th edition of the JavaScript specification includes the includes method for arrays and the exponentiation operator

es2017–8th edition of the JavaScript specification. It adds features for inspecting objects and async/await

es2018–9th edition of the JavaScript specification. It adds the spread and rests operations or objects ad string handling and async operations.

esNext — target features that will be included with the next edition of the JavaScript specification.

Since ES6, JavaScript versions are indicated by the year.

ES6 is the same as ES2015. Then JavaScript moved to release new features incrementally each year.

Therefore, the version changed to the year.

When file changes are saved, then the code will be compiled and run.

The generated code is in the dist folder.

Setting the Library Files for Compilation

To enable features that aren’t available in the target version, we can change the target.

We can set the lib compiler options with the following options.

es5 , es2015 , es2016 , es2017 and es2018 change the type definition file that correspond to the JavaScript version.

esnext lets us include proposed additional to JavaScript into our code.

dom include DOM libraries.

dom.iterable provide type information for the additions to the DOM API and allow iteration over HTML elements.

webworker inclusion of web worker features.

es2015.core include type information for the main features introduced by ES2015.

es2015.collection settings include type information for Map and Set constructors.

es2015.generator and es2015.iterable includes type information for generators and iterable features.

es2015.promise — includes type information for promises.

es2015.reflect — includes type information for reflecting features to get access to properties and prototypes

es2015.symbol , es2015.symbol.wellknown— includes type information about symbols

We include lib settings under the compilerSections section.

For instance, we can write:

{
  "compilerOptions": {
    "target": "es5",
    "outDir": "./dist",
    "rootDir": "./src",
    "noEmitOnError": true,
    "lib": ["es2015", "dom", "es2015.collection"]
   }
}

Conclusion

To use the JavaScript features we want, we’ve to include the TypeScript type definitions to include in our protect.

We can also target the version of JavaScript we want for our TypeScript project.