It is a superset of JavaScript. TS builds on top of Javascript.
The main goals of TypeScript are:
Introduce optional types to JavaScript.
Implement planned features of future JavaScript, a.k.a. ECMAScript Next or ES Next to the current JavaScript.
TypeScript improves your productivity while helping avoid bugs.
Terminal to compile the app.ts file
tsc app.ts
Notes from : www.typescripttutorial.net
If installed the tsx module
tsx app.ts
JavaScript is dynamically typed, providing flexibility but also leading to many problems.
TypeScript adds an optional type system to JavaScript to solve these problems.
Typescript shows error during the compile time if the syntax does not matches.
a type is a label that describes the different properties and methods that a value has
every value has a type.
In TypeScript, every value is associated with a type.
TypeScript compiler uses types to analyze your code for hunting bugs and errors.
Once an identifier is annotated with a type, it can be used as that type only. If the identifier is used as a different type, the TypeScript compiler will issue an error.
Type is assigned to values not to variables.
TypeScript inherits the built-in types from JavaScript. TypeScript types are categorized into:
Primitive types.
Object types.
Primitive types
The following illustrates the primitive types in TypeScript:
Have one value: undefined. It is the default value of an uninitialized variable.
symbol
Represent a unique constant value.
Object types
Object types arefunctions, arrays, classes, etc. Later, you’ll learn how to create custom object types.
let variableName: type;
let variableName: type = value;
const constantName: type = value;
let name: string = 'John';
let age: number = 25;
let active: boolean = true;
Type annotation examples Arrays
let names: string[] = ['John', 'Jane', 'Peter', 'David', 'Mary'];
Objects
let person: {
name: string;
age: number;
};
person = {
name: 'John',
age: 25,
}; // valid
Function arguments & return types
let greeting : (name: string) => string;
greeting = function (name: string) {
return`Hi ${name}`;
};
Type inference
let counter = 0; // Code language: JavaScript
It is equivalent to the following statement:
let counter: number = 0; / Code language: TypeScript
Infer type by self
If you add a string to the items array, TypeScript will infer the type for the items as an array of numbers and strings: (number | string)[]
Best common type algorithm
let items = [1, 2, 3, 'Cheese'];
Contextual typing
infers event as click
document.addEventListener('click', function (event) {
console.log(event.button);
});
// if changed to scroll then it will show error
So, when do you use type inference and type annotations?
In practice, you should always use the type inference as much as possible. You use the type annotation in the following cases:
When you declare a variable and assign it a value later.
When you want a variable that can’t be inferred.
When a function returns the any type, you need to clarify the value.
TypeScript numbers
All numbers in TypeScript are either floating-point values that get the number type or big integers that get the biginttype.
JavaScript has the Number type (with the letter N in uppercase) that refers to the non-primitive boxed object. You should not use this Number type as much as possible in TypeScript. Avoid using the Number type as much as possible.
let price: number;
price = 9;
String type
TypeScript uses double quotes (") or single quotes (') to surround string literals.
To create a multi-line string using the backtick (`) .
let description = `This TypeScript string can
span multiple
lines
`;
let firstName: string = `John`;
let title: string = `Web Developer`;
let profile: string = `I'm ${firstName}.
I'm a ${title}`;
console.log(profile);
Boolean type
The TypeScript boolean type has two values: true and false. The boolean type is one of the primitive types in TypeScript.
let pending: boolean;
pending = true;
// after a while
// ..
pending = false;
// NOT operator
const pending: boolean = true;
const notPending = !pending; // false
console.log(result); // false
const hasError: boolean = false;
const completed: boolean = true;
// Logical AND operator
let result = completed && hasError;
console.log(result); // false
// Logical OR operator
result = completed || hasError;
console.log(result); // true
JavaScript has the Boolean type that refers to the non-primitive boxed object. The Boolean type has the letter B in uppercase, which is different from the boolean type.
It’s a good practice to avoid using the Boolean type.
Object Type
The TypeScript object type represents all values that are not in primitive types.
The following are primitive types in TypeScript: number, bigint, string, boolean, null, undefined, symbol.
How to declare a variable that holds an object.
let employee: object;
employee = {
firstName: 'John',
lastName: 'Doe',
age: 25,
jobTitle: 'Web Developer'
};
console.log(employee);
// Output
{
firstName: 'John',
lastName: 'Doe',
age: 25,
jobTitle: 'Web Developer'
}
employee = "Jane"; // Error TS2322: Type '"Jane"' is not assignable to type 'object'.
console.log(employee.hireDate);
// error TS2339: Property 'hireDate' does not exist on type 'object'.
To explicitly specify properties of the employee object
let employee: {
firstName: string;
lastName: string;
age: number;
jobTitle: string;
} = {
firstName: 'John',
lastName: 'Doe',
age: 25,
jobTitle: 'Web Developer'
};
object vs. Object
The object type represents all non-primitive values while the Object type describes the functionality of all objects.
The Object type has the toString() and valueOf() methods that can be accessible by any object.
The empty type {}
TypeScript has another type called empty type denoted by {} , which is quite similar to the object type.
let vacant: {};
vacant.firstName = 'John'; // compile time error
let vacant: {} = {};
console.log(vacant.toString()); // can access all properties and methods declared on
enum Month { Jan = 1,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec };
If we specify a member a number then consecutive members will add +1 to it and all will be affected.
You should use an enum when you:
Have a small set of closely related fixed values.
And these values are known at compile time.
enum ApprovalStatus {
draft,
submitted,
approved,
rejected
};
const request = {
id: 1,
status: ApprovalStatus.approved,
description: 'Please approve this request'
};
if(request.status === ApprovalStatus.approved) {
// send an email
console.log('Send email to the Applicant...');
}
Any
Sometimes, you may need to store a value in a variable. But you don’t know its type when writing the program. And the unknown value may come from a third-party API or user input.
you want to opt out of the type checking and allow the value to pass through the compile-time check.
let result: any;
result = 1;
console.log(result); // 1
result = 'Hello';
console.log(result); // Hello
result = [1, 2, 3];
const total = result.reduce((a: number, b: number) => a + b, 0);
// Output is json - {"latitude": 10.11, "longitude":12.12}
console.log(currentLocation.x); // undefined
The TypeScript compiler doesn’t complain or issue any errors, if it is undefined.
If you declare a variable with the object type, you can also assign any value to it. However, you cannot call a method on it even if the method exists. For example:
let result: object;
result = 10.123;
result.toFixed(); // Error
let result: any;
result = 10.123;
console.log(result.toFixed());
result.willExist(); // error on execution not on compile
Void
The void type denotes the absence of having any type at all. Typically, you use the void type as the return type of functions that do not return a value.
Notice that if you use the void type for a variable, you can only assign undefined to that variable.
let useless: void = undefined;
useless = 1; // error
If the --strictNullChecks flag is not specified, you can assign the useless to null.
useless = null; // OK if --strictNullChecks is not specified
Union ( | )
The union type allows you to combine multiple types into one type.
Means it can take any of them.
let result: number | string;
result = 10; // OK
result = 'Hi'; // also OK
result = false; // a boolean value, not OK
function add(a: number | string, b: number | string) : number | string {
if (typeof a === 'number' && typeof b === 'number') {
return a + b;
}
if (typeof a === 'string' && typeof b === 'string') {
return a.concat(b);
}
thrownew Error('Parameters must be numbers or strings');
}
Type Aliases
A type alias allows you to create a new name for an existing type.
Type aliases can be useful for:
Simplifying complex types.
Making code more readable.
Creating reusable types that can be used in many places in the codebase.
let add: (x: number, y: number) => number; // Function declaration
add = function (x: number, y: number) { // Function definition
return x + y;
};
// If you assign other functions whose type doesn’t match
// the add variable, TypeScript will issue an error
let add: (x: number, y: number) => number;
This declares a variable add whose type is a function that:
Takes two parameters (x and y) of type number
Returns a number
At this point, add is declared but not assigned.
add = function (x: number, y: number) { ... }
Here you assign an anonymous function to add that matches the declared type.
let add: (a: number, b: number) => number =
function (x: number, y: number) {
return x + y;
};
This is a single-step declaration and assignment.
let add: (a: number, b: number) => number = ...
Declares add with the same function type as before.
Immediately assigns a function to it.
Parameter names in the type annotation (a, b) do not have to match the parameter names in the actual function (x, y). Only the types and order must match.
Key Points
Function type syntax: (paramName: Type, paramName: Type) => ReturnType
Parameter names in type vs. implementation: Names can differ; only types and positions matter.
Two-step vs. one-step:
Two-step: Declare type first, assign later. ( Declare then assign )
One-step: Declare type and assign immediately. ( Declare and assign )
Type safety: TypeScript ensures that the assigned function matches the declared type.
Optional parameters
you can call a function without passing any arguments even though the function specifies parameters. Therefore, JavaScript supports the optional parameters by default.
The optional parameters must appear after the required parameters in the parameter list. Means at the end of required parameters.
function multiply(a: number, b: number, c?: number): number {
if (typeof c !== 'undefined') {
return a * b * c;
}
return a * b;
}
// check if the argument is passed to the function by using the
// expression typeof c !== 'undefined'.
// Note that if you use the expression if(c) to check if an argument
// is not initialized, you would find that the empty string or zero
// would be treated as undefined.
function multiply(a: number, b?: number, c: number): number {
// b gives error
if (typeof c !== 'undefined') {
return a * b * c;
}
return a * b;
}
Default parameters
We can pass default parameters in functions.
if you don’t pass arguments or pass the undefined into the function when calling it, the function will take the default initialized values for the omitted parameters. As below:
function applyDiscount(price, discount = 0.05) {
return price * (1 - discount);
}
console.log(applyDiscount(100)); // 95
Notice that you cannot include default parameters in function type definitions. The following code will result in an error:
let promotion: (price: number, discount: number = 0.05) => number; // ERROR
default parameters are optional means we can omit/ avoid the passing of the parameters in the function.
Optional parameters must come after the required parameters. However, default parameters don’t need to appear after the required parameters.
When a default parameter appears before a required parameter, you need to explicitly pass undefined to get the default initialized value.
Rest Parameters
A rest parameter allows a function to accept zero or more arguments of the specified type. In TypeScript, the rest parameters follow these rules:
A function has only one rest parameter.
The rest parameter appear last in the parameter list.
the number of required parameters must be the same. If an overload has more parameters than the other, you need to make the additional parameters optional. If an overload has more parameters than the other, you need to make the additional parameters optional. For example:
function sum(a: number, b: number): number;
function sum(a: number, b: number, c: number): number;
function sum(a: number, b: number, c?: number): number {
if (c) return a + b + c;
return a + b;
}
The third parameter is optional. If you don’t make it optional, you will get an error.
When a function is a property of a class, it is called a method. TypeScript also supports method overloading.
you cannot initialize the ssn with a number. The following code will result in an error:
let person = new Person(171280926, 'John', 'Doe'); // Error
TypeScript Access Modifiers
The private modifier limits the visibility to the same class only. You can access that property or method within the same class.
Any attempt to access private properties or methods outside the class will result in an error at compiled time.
The public modifier allows class properties and methods to be accessible from all locations. If you don’t specify any access modifier for properties and methods, they will take the public modifier by default.
The protectedmodifier allows properties and methods of a class to be accessible within the same class and subclasses.
When a class (child class) inherits from another class (parent class), it is a subclass of the parent class.
The TypeScript compiler will issue an error if you attempt to access the protected properties or methods from anywhere else.
To make the code shorter, TypeScript allows you to both declare properties and initialize them in the constructor like this:
class Person {
constructor(
protected ssn: string,
private firstName: string,
private lastName: string
) {}
getFullName(): string {
return`${this.firstName}${this.lastName}`;
}
}
Read Only
Use the readonly access modifier to mark a class property as immutable.
A readonly property must be initialized as a part of the declaration or in the constructor of the same class.
readonly vs. const
The following shows the differences between readonly and const:
readonly
const
Use for
Class properties
Variables
Initialization
In the declaration or in the constructor of the same class
In the declaration
class Person {
readonly birthDate: Date;
constructor(birthDate: Date) {
this.birthDate = birthDate;
}
}
let person = new Person(new Date(1990, 12, 25));
person.birthDate = new Date(1991, 12, 25); // Compile error
// can do like this
class Person {
constructor(readonly birthDate: Date) {
}
}
Getter and Setter
Use TypeScript getters/settersto control the access properties of a class.
The getter/setters are also known as accessors/mutators.
To avoid repeating the check, you can use setters and getters.
A class can reuse the properties and methods of another class. This is called inheritance in TypeScript. JavaScript uses prototypal inheritance, not classical inheritance like Java or C#. ES6 introduces the class syntax that is simply the syntactic sugar of the prototypal inheritance. TypeScript supports inheritance like ES6.
Constructor
Because the Person class has a constructor that initializes the firstName and lastName properties, you need to initialize these properties in the constructor of the Employee class by calling its parent class’ constructor.
To call the constructor of the parent class in the constructor of the child class, you use thesuper()syntax. For example:
Above super.describe() is the method overriding which uses the methods of parent class.
Static method and properties
Static property
Unlike an instance property, a static property is shared among all instances of a class. To declare a static property, you use the static keyword. To access a static property, you use the className.propertyName syntax. For example:
class Employee {
static headcount: number = 0;
constructor(
private firstName: string,
private lastName: string,
private jobTitle: string) {
Employee.headcount++;
}
}
Static methods
class Employee {
privatestatic headcount: number = 0;
constructor(
private firstName: string,
private lastName: string,
private jobTitle: string) {
Employee.headcount++;
}
publicstatic getHeadcount() {
return Employee.headcount;
}
}
In practice, you will find a library that contains many static properties and methods like the Math object. It has PI, E, … static properties and abs(), round(), etc., static methods.
Abstract Classes
An abstract class is typically used to define common behaviors for derived classes to extend.
Unlike a regular class, an abstract class cannot be instantiated directly. Typically, an abstract class contains one or more abstract methods.
An abstract method does not contain implementation. It only defines the signature of the method without including the method body. An abstract method must be implemented in the derived class.
To use an abstract class, you need to inherit it and provide the implementation for the abstract methods.
To describe a function type, you assign the interface to the function signature that contains the parameter list with types and returned types. For example:
interface StringFormat {
(str: string, isUpper: boolean): string
}
let format: StringFormat;
format = function (str: string, isUpper: boolean) {
An interface can extend one or multiple existing interfaces.
An interface also can extend a class. If the class contains private or protected members, the interface can only be implemented by the class or subclasses of that class.
But to make the generic function to take only the required details throws error if any data passed is changed. As below
function merge<U extends object, V extends object>(obj1: U, obj2: V) {
return {
...obj1,
...obj2
};
} // It works with object type only.
Using type parameters in generic constraints
TypeScript allows you to declare a type parameter constrained by another type parameter.
function prop<T, K>(obj: T, key: K) {
return obj[key];
} // compiler error
// Type 'K' cannot be used to index type 'T'.
// Error fixing
function prop<T, K extendskeyof T>(obj: T, key: K) {
return obj[key];
}
// Works
let str = prop({ name: 'John' }, 'name');
console.log(str);
However, if you pass a key that doesn’t exist on the first argument, the compiler will issue an error:
let str = prop({ name: 'John' }, 'age');
// Error
// Argument of type '"age"' is not assignable to parameter of type '"name"'.
TypeScript Generic Classes
class className<T>{
//...
}
class className<K,T>{
//...
// Multiple generic types
}
The generic constraints are also applied to the generic types in the class:
class className<T extends TypeA>{
//...
}
Placing the type parameter on the class allows you to develop methods and properties that work with the same type.
TypeScript Generic Interfaces
A generic interface allows you to create an interface that can work with different types while maintaining type safety.
A generic interface has a generic type parameter list in angle brackets <> following the name of the interface
interface interfaceName<T> {
// ...
}
interface interfaceName<U,V> {
// ...
}
The type parameter list can have one or multiple types.
1) Generic interfaces that describe object properties
interface Pair<K, V> {
key: K;
value: V;
}
let month: Pair<string, number> = {
key: 'Jan',
value: 1
};
console.log(month);
2) Generic interfaces that describe methods
interface Collection<T> {
add(o: T): void;
remove(o: T): void;
}
class List<T> implements Collection<T>{
private items: T[] = [];
add(o: T): void {
this.items.push(o);
}
remove(o: T): void {
let index = this.items.indexOf(o);
if (index > -1) {
this.items.splice(index, 1);
}
}
}
let list = new List<number>();
for (let i = 0; i < 10; i++) {
list.add(i);
}
3) Generic interfaces that describe index types
Generic interfaces with index types allow you to define objects whose property names are dynamic while keeping the values strongly typed. This is useful when you don't know the property names in advance but do know the type of the values.
No comments:
Post a Comment