TypeScript from JavaScript: A Comprehensive Guide - Part 2 of 7
Hello again, future TypeScript pros! 🌟
In the first part of this series, we introduced you to the basics of TypeScript, its advantages over JavaScript, and how to set up a TypeScript project. In this second part, we will dive deeper into TypeScript's type system and learn about more advanced features like interfaces, classes, and generics. Let's get started!
Table of Contents
- Recap
- Interfaces in TypeScript
- Classes in TypeScript
- Generics in TypeScript
- Conclusion
1. Recap
In the first part of this series, we learned about some basic types in TypeScript like number
, string
, boolean
, array
, tuple
, enum
, any
, and void
. We also learned how to set up a TypeScript project and compile TypeScript code into JavaScript.
2. Interfaces in TypeScript
Interfaces are a powerful way to define contracts within your code and contracts with code outside of your project. They are used to define the shape of an object, i.e., the properties and methods that an object should have.
a. Defining Interfaces
You can define an interface using the interface
keyword followed by the interface name. Here is an example of a simple interface:
interface Person {
firstName: string;
lastName: string;
}
In this example, the Person
interface has two properties: firstName
and lastName
, both of which are of type string
.
b. Implementing Interfaces
Once you have defined an interface, you can use it as a type for your variables, parameters, or return values. Here is an example:
function greet(person: Person) {
return `Hello, ${person.firstName} ${person.lastName}`;
}
const john: Person = {
firstName: 'John',
lastName: 'Doe',
};
console.log(greet(john)); // Hello, John Doe
In this example, the greet
function expects a Person
object as its parameter. The john
object is of type Person
, so it can be passed to the greet
function.
c. Optional Properties
Interfaces can also have optional properties. You can mark a property as optional by adding a ?
after its name. Here is an example:
interface Person {
firstName: string;
lastName: string;
middleName?: string;
}
const john: Person = {
firstName: 'John',
lastName: 'Doe',
};
const jane: Person = {
firstName: 'Jane',
lastName: 'Doe',
middleName: 'Mary',
};
In this example, the middleName
property is optional. The john
object does not have a middleName
property, and that's fine. The jane
object has a middleName
property, and that's fine too.
d. Read-Only Properties
Interfaces can also have read-only properties. You can mark a property as read-only by adding the readonly
keyword before its name. Here is an example:
interface Person {
readonly id: number;
firstName: string;
lastName: string;
}
const john: Person = {
id: 1,
firstName: 'John',
lastName: 'Doe',
};
john.id = 2; // Error: Cannot assign to 'id' because it is a read-only property
In this example, the id
property is read-only. Trying to modify it after its initial assignment will result in a compile-time error.
3. Classes in TypeScript
Classes in TypeScript are similar to classes in other object-oriented programming languages. They are used to define the blueprint for creating objects.
a. Defining Classes
You can define a class using the class
keyword followed by the class name. Here is an example of a simple class:
class Car {
brand: string;
model: string;
year: number;
constructor(brand: string, model: string, year: number) {
this.brand = brand;
this.model = model;
this.year = year;
}
start() {
console.log('Car started');
}
drive() {
console.log('Car is driving');
}
}
In this example, the Car
class has three properties: brand
, model
, and year
, and three methods: constructor
, start
, and drive
.
b. Creating Objects
Once you have defined a class, you can create objects of that class using the new
keyword. Here is an example:
const myCar = new Car('Toyota', 'Corolla', 2020);
myCar.start();
myCar.drive();
In this example, a new Car
object is created with the brand
, model
, and year
properties set to 'Toyota'
, 'Corolla'
, and 2020
, respectively. The start
and drive
methods are then called on the myCar
object.
c. Access Modifiers
TypeScript supports three access modifiers: public
, private
, and
protected
.
-
public
: This is the default modifier and can be accessed anywhere. -
private
: This can only be accessed within the class where it is defined. -
protected
: This can be accessed within the class where it is defined and in any subclass of that class.
Here is an example:
class Car {
public brand: string;
private model: string;
protected year: number;
constructor(brand: string, model: string, year: number) {
this.brand = brand;
this.model = model;
this.year = year;
}
}
const myCar = new Car('Toyota', 'Corolla', 2020);
console.log(myCar.brand); // Toyota
console.log(myCar.model); // Error: Property 'model' is private and only accessible within class 'Car'
console.log(myCar.year); // Error: Property 'year' is protected and only accessible within class 'Car' and its subclasses
In this example, the brand
property can be accessed outside of the Car
class because it is public
. The model
and year
properties cannot be accessed outside of the Car
class because they are private
and protected
, respectively.
d. Inheritance
Classes in TypeScript can inherit properties and methods from other classes. This is done using the extends
keyword. Here is an example:
class ElectricCar extends Car {
range: number;
constructor(brand: string, model: string, year: number, range: number) {
super(brand, model, year);
this.range = range;
}
charge() {
console.log('Car is charging');
}
}
const myElectricCar = new ElectricCar('Tesla', 'Model 3', 2020, 500);
myElectricCar.start();
myElectricCar.drive();
myElectricCar.charge();
In this example, the ElectricCar
class extends the Car
class and inherits its properties and methods. It also adds a new property, range
, and a new method, charge
.
4. Generics in TypeScript
Generics are a way to create reusable components that work with any type. They are similar to templates in other programming languages.
a. Generic Functions
You can create generic functions by using a type variable, which is a special kind of variable that works on types rather than values. Here is an example:
function identity<T>(arg: T): T {
return arg;
}
const myNumber = identity<number>(5);
const myString = identity<string>('Hello');
console.log(myNumber); // 5
console.log(myString); // Hello
In this example, the identity
function is a generic function that takes an argument arg
of type T
and returns a value of type T
. The myNumber
and myString
variables are created by calling the identity
function with <number>
and <string>
type arguments, respectively.
b. Generic Classes
You can also create generic classes by using a type variable. Here is an example:
class ArrayOfNumbers {
private items: number[];
constructor() {
this.items = [];
}
add(item: number) {
this.items.push(item);
}
getItems() {
return this.items;
}
}
const numbers = new ArrayOfNumbers();
numbers.add(1);
numbers.add(2);
numbers.add(3);
console.log(numbers.getItems()); // [1, 2, 3]
class ArrayOfStrings {
private items: string[];
constructor() {
this.items = [];
}
add(item: string) {
this.items.push(item);
}
getItems() {
return this.items;
}
}
const strings = new ArrayOfStrings();
strings.add('a');
strings.add('b');
strings.add('c');
console.log(strings.getItems()); // ['a', 'b', 'c']
In this example, the ArrayOfNumbers
and ArrayOfStrings
classes are not generic, and therefore, you need to create two separate classes for handling arrays of number
and string
items. This is not very efficient.
Here is how you can use generics to create a single Array
class that can handle arrays of any type:
class Array<T> {
private items: T[];
constructor() {
this.items = [];
}
add(item: T) {
this.items.push(item);
}
getItems() {
return this.items;
}
}
const numbers = new Array<number>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
console.log(numbers.getItems()); // [1, 2, 3]
const strings = new Array<string>();
strings.add('a');
strings.add('b');
strings.add('c');
console.log(strings.getItems()); // ['a', 'b', 'c']
In this example, the Array
class is a generic class that takes a type argument T
. The numbers
and strings
variables are created by calling the Array
class with <number>
and <string>
type arguments, respectively.
5. Conclusion
Congrats! 🎉 You have successfully learned about interfaces, classes, and generics in TypeScript. These are some of the most powerful features of TypeScript and will help you write more robust
and reusable code.
In the next part of this series, we will learn about modules, namespaces, and decorators in TypeScript. Stay tuned!
That's all for now, happy coding! 💻✨
If you are just joining in, here are the links to the other parts of this series:
That’s a wrap. Thanks for reading.
If you're looking for more premium content and resources to help you start and grow your business, consider subscribing to my Newsletter.
Want to see what I am working on? Check out my Twitter
Top comments (0)