A One Piece-Themed Voyage Through TypeScript Basics
TypeScript brings order to JavaScript's unpredictable seas - just as a Log Pose guides ships through the Grand Line, TypeScript's type system guides developers through their code with confidence and safety.
Setting Sail: Navigating Safer Seas (Code)
What is TypeScript?
TypeScript is a superset of
JavaScript
TypeScript builds on JavaScript by adding static types, helping catch errors during development.
Created by Microsoft
TypeScript was first released in 2012 to help developers manage large-scale JavaScript projects.
Compatibility
TypeScript compiles down to plain JavaScript, allowing it to run anywhere JavaScript does.
Why use TypeScript?
Better Code Quality
TypeScript improves productivity and makes working with complex codebases much easier, leading to better code quality.
An improved developer experience offers powerful features like intelligent autocompletion, inline documentation, and smart refactoring tools that boost coding productivity.
Less Bugs
TypeScript won’t make your software bug free, but static code analysis can prevent a lot of type-related errors.
TypeScript helps catch errors at compile time by enforcing type checking, reducing runtime bugs and making your code more reliable.
Understanding TypeScript: Core Concepts
The Straw Hat Crew
Key Features
Each member of the crew plays their part:
Captain Luffy: Types
TypeScript’s most important feature is the Type system. This is like Luffy being the captain, he sets the direction and the tone of the adventure. Without a captain, a crew would be lost at sea, and without types, your code becomes less predictable and more prone to bugs.
TYPES - with type annotations, where the type of the variable is explicitly defined:
  • Primitives: number, string, boolean
let bounty: number = 30_000_000; let devilFruit: string = "Gomu Gomu no Mi"; let isCaptain: boolean = true;
  • Arrays
let crew: string[] = ["Luffy", "Zoro", "Nami", "Sanji", "Usopp"];
  • Tuples
A tuple is a fixed-length array where each element has a specific, predefined type based on its position. Tuples are useful because they ensure that every item in the array has a known and expected type. To define a tuple, you specify the type for each element in order.
let wantedPoster: [string, number] wantedPoster = ["Luffy", 30_000_000] console.log(`Wanted: ${wantedPoster[0]}`); // Wanted: Luffy console.log(`Bounty: ${wantedPoster[1]} Berries`); // Bounty: 30000000 Berries
  • Objects
const crewMember: { name: string; age: number; role: string; ateDevilFruit: boolean } = { name: "Monkey D Luffy", age: 19, role: "Captain", ateDevilFruit: true };
  • Any
TypeScript also has a special type, any, that you can use whenever you don’t want a particular value to cause type checking errors.
But, the use of any is generally discouraged since defeats the purpose of utilising TypeScript.
FUNCTIONS
TypeScript allows you to specify the types of both the input and output values of functions.
function setSail(destination: string): string { return `The Straw Hat Crew is heading for ${destination}!`; } console.log(setSail("Wano")); // The Straw Hat Crew is heading for Wano!
If you want to annotate the return type of a function which returns a promise, you should use the Promise type:
async function eatFood(): Promise<string> { return "Luffy ate all the food!"; }
First Mate Zoro: Static Typing and Inferred Typing
As a master swordsman, Zoro's strength lies in his discipline, he’s always ready for battle, but he’s also adaptable, capable of adjusting his sword techniques based on the situation. This balance is like Static Typing combined with Inferred Typing in TypeScript.
STATIC TYPING
Just as Zoro hones his skills through rigorous training, static typing enforces discipline in your code, checking types at compile-time.
  • Type Safety:
Zoro wouldn’t use the wrong sword for a fight, and TypeScript won’t let you use the wrong type.
let swordStyle: string = "Santoryu"; swordStyle = 3; // Error: Type 'number' is not assignable to type 'string'
  • Type Guards:
Sometimes Zoro needs to assess his opponent, a type guard is a TypeScript technique used to get information about the type of a variable, usually within a conditional block.
function identifyOpponent(opponent: string | number) { if (typeof opponent === "string") { return `Fighting ${opponent}`; } else { return `Bounty: ${opponent} berries`; } } identifyOpponent("Mr. 1"); // Fighting Mr. 1 identifyOpponent(75_000_000); // Bounty: 75000000 berries
Type guards help inspect specific properties of an object and make sure you perform operations that are valid for its type, ensuring type safety.
INFERRED TYPING
But, Zoro doesn’t always need to explicitly declare every move. Sometimes, he acts on instinct, trusting his training to guide him. Similarly, TypeScript can infer the type based on the context, saving you from redundant declarations.
let swordCount = 2; // TypeScript infers 'swordCount' as a number swordCount = 3; // Zoro adds another sword
The TypeScript Compiler (TSC) can often automatically infer types, and over-typing or explicitly declaring types when it's unnecessary can be considered inefficient.
Important note: Declaring type annotations on a function’s parameters is always recommended. Also, when dealing with complex data structures (like nested objects, tuples, or generics), explicit typing helps ensure clarity and prevents incorrect assumptions by the compiler.
Navigator Nami: Type Aliases and Interfaces
Nami, the crew’s navigator, thrives on clear maps and plans. Type Aliases and Interfaces serve as the navigational charts for your data structures. They help define clear blueprints for objects, ensuring consistency and preventing you from sailing into uncharted waters (errors and bugs).
  • Type Allias
  • Allow the definition of types with a custom name, applicable to primitives, unions, tuples, and more complex types.
type FavouriteThing = "Tangerines"; const nami: FavouriteThing = "Tangerines";
  • Interface
  • Primarily used for defining object types and specifying property names and their types. They can be extended or merged.
interface PirateProfile { crew: string; role: string; bounty?: number; // Optional property }
Example of an interface extension:
interface PirateProfile { crew: string; role: string; bounty?: number; // Optional property } interface NavigatorProfile extends PirateProfile = { mapsCollected: number; ownsLogPose?: boolean; // Optional property } const nami: NavigatorProfile = { crew: "Straw Hat Pirates", role: "Navigator", bounty: 66000000, mapsCollected: 50, ownsLogPose: true };
So you might be thinking, which one should I use and when?
In short:
  • Use interfaces when defining objects, especially when they might need to be extended or merged.
  • Use type aliases for primitive types, complex types, unions, intersections, or when you're defining something that isn’t strictly an object.
  • Type aliases can define objects as well, but they don’t support extension or declaration merging.
A ongoing debate:
Loading...
A snippet from TypeScript docs:
"Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an interface are available in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable."
Sniper Usopp: Union and Intersection Types
Usopp is... flexible. He’s a sniper, storyteller, and self-proclaimed "brave warrior of the sea". Union and Intersection Types give TypeScript similar flexibility, allowing variables to hold multiple types or combine multiple types.
  • Union Types
TypeScript lets you create a union type that is a composite of selected types separated by a vertical bar, |
function longRangeAttack(attack: string | number) { typeof attack === "string" ? console.log(`Usopp attacks with his ${attack}!`) : console.log(`Usopp fires a shot from ${attack} meters away!`); } longRangeAttack("Kouzuki's Slingshot"); // Usopp attacks with his Kouzuki's Slingshot! longRangeAttack(100); // Usopp fires a shot from 100 meters away!
Use case
Union types are great for representing values that could belong to different categories.
For example, you might have a variable that tracks the status of a task:
let taskStatus: "Pending" | "In-progress" | "Completed" = "Pending";
is saying:
  • taskStatus is a variable
  • The type of taskStatus is restricted to the exact values "Pending", "In-progress", or "Completed"
  • It is initially set to "Pending"
  • Intersection Types
An Intersection Type allows you to combine multiple types into one, meaning the resulting type will have all the properties and methods from the combined types.
In TypeScript, you can use the & operator to combine types, let's combine all of Usopp's abilities together:
type Sniper { attackRange: number; observationHaki: boolean; } type Storyteller { storiesTold: number; } type BraveWarrior { braveryLevel: string; } type Usopp = Sniper & Storyteller & BraveWarrior; const usoppProfile: Usopp = { attackRange: 100, observationHaki: true, storiesTold: 9001, braveryLevel: "low", };
Note: you can use the & operator to combine either types or interfaces.
Cook Sanji: Generics
Sanji can cook any dish for any crew member, tailoring to their needs. Similarly, Generics in TypeScript enable writing code that can work with a variety of data types while maintaining type safety. They allow the creation of reusable components, functions, and data structures without sacrificing type checking.
Generics are represented by type parameters, which act as placeholders for types. These parameters are specified within angle brackets (<>) and a placeholder letter (commonly T, but it can be anything like U, V, etc.), and can be used throughout the code to define types of variables, function parameters, return types, and more.
function myFunction<T>(param: T): T { return param; }
function cookDish<T>(dish: T): T { console.log(`Cooking ${dish}`); return dish; } const luffyMeal = cookDish<string>("Meat Skewers") console.log(luffyMeal); // "Meat Skewers" const namiMeal = cookDish<string>("Tropical Fruit Salad") console.log(namiMeal); // "Tropical Fruit Salad"
The Sky's the Limit!
Now that you have a grasp of the basics of TypeScript, try converting an existing JavaScript project to TypeScript:

typescriptlang

Documentation - Migrating from JavaScript

How to migrate from JavaScript to TypeScript

Alternatively, follow the steps below to start a new adventure!
Setting Up TypeScript
Preparing Your Ship to Set Sail

1

Install TypeScript
You will need a copy of Node.js as an environment to run the package. Then you use a dependency manager like npm to download TypeScript into your project:
npm i typescript --save-dev

2

Code
Create a simple file ending with .ts such as helloworld.ts and write your first few lines of TypeScript code.
let message: string = 'Hello World'; console.log(message);

3

Compile/Transpile
You can then run the TypeScript Compiler using the following command:
tsc helloworld.ts
You should now see the transpiled helloworld.js JavaScript file, which you can run if you have Node.js installed, by typing:
node helloworld.js
Next Steps to TypeScript Mastery:
Charting Your Course
Official TypeScript Docs:

typescriptlang

The starting point for learning TypeScript

Find TypeScript starter projects: from Angular to React or Node.js and CLIs.

Test Yourself! - W3Schools Quiz:

www.w3schools.com

W3Schools.com

Well organized and easy to understand Web building tutorials with lots of examples of how to use HTML, CSS, JavaScript, SQL, PHP, Python, Bootstrap, Java and XML.

A Better Tutorial - Codecademy:

Codecademy

Learn TypeScript | Codecademy

Learn TypeScript, a superset of JavaScript that adds types to make the language scale!

Made by Timothy Li using Gamma
Made with Gamma