Categories
TypeScript

JavaScript Object Features in TypeScript — Modules

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 define and use modules.

Using Modules

Almost all apps can’t be contained in a single file.

Therefore, we need to put our code in modules so that we can organize the code.

This way, we can break our code into manageable chunks.

JavaScript modules can be used with TypeScript projects.

And they should be used since they’re now standard in JavaScript.

Node.js also has support for JavaScript modules since version 12, so we can use them without adding any transpilation in Node projects as well.

Creating a JavaScript Module

To create a module, we just have to create a JavaScript file.

Then if we want to make a member be importable, then we put the member at the top level and use the export keyword to expose it to the outside.

For instance, we can write:

export const name = "joe";

This will make our name variable be importable from another module.

This kind of export is called a named export since we’ve to specify the name of the member explicitly when we import it.

We can also do a default export.

To do that, we can use the default keyword:

export default {
  name: "james"
};

We can only have default export in any module.

We can name it anything when we import this in another module.

Using a JavaScript Module

To use an export member of a module, we’ve to import it.

For instance, if we want to use the following exported member:

export const name = "joe";

We can write:

import { name } from "./module";

console.log(name);

We used the import keyword with the member of the module in curly braces to import it.

Then we can reference it anywhere inside the module.

To import a default export, we skip the curly braces.

For instance, if we have:

export default {
  name: "james"
};

Then we can import the object by writing:

import obj from "./module";

console.log(obj.name);

We skipped the curly braces in the import statement and log the value after the import.

The ./ tells us that we’re searching for a path relative to the current module.

So we should include it for relative imports.

If we skip the ./ , then we indicate that we’re importing from a dependency rather than a module in the local project.

The most common location for module dependencies would be in the node_modules folder.

Defining Multiple Named Members in a Module

We can define multiple named members in a module.

For instance, we can write:

export const name = "joe";
export const age = 20;

We exported the members name and age from the module.

Then we import then by writing:

import { name, age } from "./module";
console.log(name, age);

We then import them by separating them with a comma.

With named members, we can selectively import the members that we want.

This way, we don’t have to import things that we don’t use.

Conclusion

In a TypeScript project, we’ve to use modules to make them organized into small, manageable chunks.

We can just import and export members as we wish.

And we don’t have to expose everything to the outside or import everything.

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.