Step 2.1: Introduction to TypeScript
In this step, we'll cover enough TypeScript concepts to be productive with the React & Redux frameworks.
Topics in this step will include:
To try out TypeScript concepts and see the corresponding JavaScript, you can use the TypeScript playground. We won't be using it in this training, but it's very handy in general!
Modules
Historically, JS was only executed in-browser. The code all had to be loaded using <script> tags. With the introduction of node.js, the JS community needed a way to scale beyond just single script files. Other languages support the notion of modules, so various groups started developing modularity standards for JS.
The most important ones to know about are:
- commonjs - Node.js's standard to support modules
- synchronous loading using
require()function require()can be dynamically called in the course of a program
- synchronous loading using
- ESM (ECMAScript module) - language-level support
- statically analyzable and synchronous
- dynamic and asynchronous support via
import()that returns a promise
For more information about the many modularity patterns and standards developed over time, see this article. You may still encounter some of the older patterns in legacy code.
TypeScript Types
Refer to demo/src for examples of some of the types available in TS that benefit a React developer.
Spread Operator
The spread operator ... provides a quick way to clone and concatenate objects and arrays. This syntax is seen a lot inside React props and Redux reducers.
With objects:
// Shallow copy an object
const cloned1 = { ...obj };
// Shallow copy and add/overwrite a key
const overridden1 = { ...obj, key: value };
// Shallow copy multiple objects and add a key
const cloned2 = { ...obj1, ...obj2, key: value };
// Use an expression to calculate a key dynamically
const overridden = { ...object, [key + '-suffix']: value };
With arrays:
const copy1 = [...arr];
const copy2 = [...arr1, ...arr2];
const copyWithExtras = [123, ...arr, 'hello'];
Spreading an array into positional arguments to a function:
function myFunc(a: number, b: number, c: number) {
// ...
}
const arr = [1, 2, 3];
myFunc(...arr);
Spreading an object into props for a React component:
const obj = { a: 1, b: 2, c: 3 };
// equivalent to:
// <MyComponent a={obj.a} b={obj.b} c={obj.c} />
const rendered = <MyComponent {...obj} />;
Destructuring
Destructuring is a concise way to take properties out of an object or array:
const obj = { foo: 1, bar: 2 };
const { foo, bar } = obj;
// foo = 1, bar = 2
const arr = [1, 2];
const [foo, bar] = arr;
// foo = 1, bar = 2
You can separate an item from the rest of the object with destructuring:
const obj = { a: 1, b: 2, c: 3, d: 4 };
const { a, ...rest } = obj;
// a = 1, rest = {b: 2, c: 3, d: 4}
const arr = [1, 2, 3];
const [foo, ...bar] = arr;
// foo = 1, bar = [2, 3]
Promise
A promise is an object representing work that will be completed later, asynchronously. Promises are chainable, which helps with writing maintainable async code. (Typically, legacy async code uses callbacks to let the caller have control over what to do after the task has been completed, which becomes very hard to read.)
const aPromise = new Promise((resolve, reject) => {
// do something async and call resolve() to let promise know it is done
setTimeout(() => {
// setTimeout will call this method after 1s, simulating async operation like network calls
resolve();
}, 1000);
});
Each promise instance exposes a then() function that is chainable. It also provides catch(), which catches all exceptions or reject() calls:
// Promise.resolve() creates an already-resolved promise instance
const aPromise = Promise.resolve('hello world');
aPromise
.then(result => {
return makeAnotherPromise();
})
.then(result => {
return makeYetAnotherPromise();
})
.catch(err => {
console.error(err);
});
For more information, see this overview of promises or this deep dive.
Async / Await
This syntax is inspired heavily by C#'s async / await syntax. An async function is written like this:
async function someFunctionAsync() {
// Inside here, we can await on other async functions
const result = await someOtherFunctionAsync();
return result + ' hello';
}
All functions that are marked async return a Promise automatically. The previous example returned a Promise<string>, and can be used like this:
someFunctionAsync().then(result => {
console.log(result);
});
For more information, see this article.
Exercise
If you don't already have the app running, start it by running npm start from the root of the frontend-bootcamp folder.
Exercises will be completed under this step's exercise/src folder unless otherwise noted. You'll also want to open the Step2-01 exercise page to see the results as you work.
Modules
-
Open the file
exercise/src/fibonacci.tsin VS Code -
Inside this file, write a function called
fib(n)that takes in a number and returns then-th Fibonacci number (be sure the specify the type ofn).
HINT:
function fib(n: number) { return n <= 1 ? n : fib(n - 1) + fib(n - 2); }
-
Export
fib(n)as a named export -
Export a const variable
FibConstas a default export -
Inside
index.tsin the same folder, import bothfibandFibConst, and use the built-inconsole.log()function to log the result offib(FibConst).
Types and Interfaces
Inside exercise/src/index.ts:
-
Add a type alias for string union type describing the states of Red-Green-Yellow traffic light:
type TrafficLight = ??? -
Describe a type of car with an interface:
interface Car { ... }complete withwheels,color,make,model -
Create a valid car instance and log it using
console.log():const myCar: Car = { ??? };
Generics
Inside exercise/src/stack.ts, create a generic class for a Stack<T> complete with a typed pop() and push() methods.
Hint: the JavaScript array already has
push()andpop()implemented for you. That can be your backing store.
In exercise/src/index.ts, create a Stack<number> and use console.log() to demonstrate its functionality.
Spread and Destructuring
- Note the following code in index.ts:
const obj1 = {
first: 'who',
second: 'what',
third: 'dunno',
left: 'why'
};
const obj2 = {
center: 'because',
pitcher: 'tomorrow',
catcher: 'today'
};
-
Now create a one-liner using the spread syntax
{...x, ...y}to create a new variablemegaObjthat combines these two objects. -
Use the destructuring syntax to retrieve the values for
{first, second, catcher}frommegaObj.
Async / Await
Note the following code in index.ts:
function makePromise() {
return Promise.resolve(5);
}
-
Call
makePromise()with theawaitsyntax and log the results. -
Create a new function that uses the
asynckeyword. Inside the function, make anawaitcall tomakePromise()and return the results.