TypeScript | Beginner

Safdar Ali
9 min readApr 4, 2024

--

TypeScript | Beginner

Hello my fellow frontend, backend and fullstack developer, today i will be covering some beginner Typescript concepts with examples.
Let’s get started…

1) Introduction to TypeScript
TypeScript is a superset of JavaScript that adds static typing to the language. It helps catch errors at compile time and provides better code organization.

function greet(name: string) {
return `Hello, ${name}!`;
}
console.log(greet("Alice"));
  • Tips and Tricks: Use TypeScript to define types for variables, functions, and more to improve code reliability.
  • Best Use Cases: Ideal for large-scale applications where type safety is crucial.

2) Basic Types
TypeScript includes basic types such as number, string, boolean, array, etc.

let age: number = 25;
let name: string = "Bob";
let isStudent: boolean = true;
let numbers: number[] = [1, 2, 3, 4, 5];
  • Tips and Tricks: Use type inference to let TypeScript automatically determine the type when possible.
  • Best Use Cases: Used for defining simple data types in your code.

3) Interfaces
Interfaces define the structure of an object in TypeScript.

interface Person {
name: string;
age: number;
}
function greet(person: Person) {
return `Hello, ${person.name}!`;
}
  • Tips and Tricks: Interfaces help enforce a consistent shape across objects.
  • Best Use Cases: Useful for defining data contracts in your application.

4) Classes
Classes in TypeScript allow you to use object-oriented programming concepts like inheritance and encapsulation.

class Animal {
constructor(public name: string) {}
move(distance: number) {
console.log(`${this.name} moved ${distance} meters.`);
}
}
  • Tips and Tricks: Use access modifiers like public, private, and protected for better encapsulation.
  • Best Use Cases: Great for modeling real-world entities in your code.

5) Generics
Generics allow you to create reusable components that can work with a variety of data types.

function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("hello");
  • Tips and Tricks: Use generics when you want a function or class to work with any data type.
  • Best Use Cases: Helpful for creating flexible and reusable code.

6) Enums
Enums in TypeScript allow you to define a set of named constants.

enum Direction {
Up,
Down,
Left,
Right
}
let heading: Direction = Direction.Up;
  • Tips and Tricks: Enums help make your code more readable by giving friendly names to numeric values.
  • Best Use Cases: Useful when you have a set of related constants.

7) Type Assertion
Type assertion is a way to tell the compiler about the type of a variable when TypeScript can’t infer it.

let input: any = "hello";
let length: number = (input as string).length;
  • Tips and Tricks: Use type assertion when you know more about a value than TypeScript does.
  • Best Use Cases: Helpful when working with data from external sources.

8) Decorators
Decorators are a special kind of declaration that can be attached to classes, methods, accessors, properties, or parameters.

function log(target: any, key: string) {
console.log(`Method ${key} called`);
}
class Example {
@log
someMethod() {
// Method implementation
}
}
  • Tips and Tricks: Decorators are widely used in frameworks like Angular for metadata reflection.
  • Best Use Cases: Useful for adding metadata to classes and members.

9) Modules
Modules in TypeScript help organize code into reusable units.

// math.ts
export function add(a: number, b: number): number {
return a + b;
}
// app.ts
import { add } from './math';
console.log(add(2, 3)); // Output: 5
  • Tips and Tricks: Use modules to keep your codebase clean and maintainable.
  • Best Use Cases: Essential for structuring large applications.

10) Namespaces
Namespaces allow you to organize code by grouping logically related objects.

namespace Geometry {
export class Circle {
// Circle implementation
}
}
let circle = new Geometry.Circle()

11) Type Inference
Type inference in TypeScript allows the compiler to determine types when they’re not explicitly specified.

let num = 10; // TypeScript infers the type as number
  • Tips and Tricks: TypeScript’s type inference can save time and make the code cleaner.
  • Best Use Cases: Useful for writing concise code without sacrificing type safety.

12) Type Guards
Type guards allow you to narrow down the type of a variable within a conditional block.

function isNumber(x: any): x is number {
return typeof x === "number";
}
if (isNumber(value)) {
// Inside this block, TypeScript knows 'value' is a number
}
  • Tips and Tricks: Type guards are helpful when dealing with union types.
  • Best Use Cases: Useful for working with complex or dynamic data types.

13) Union Types
Union types allow a variable to have multiple types.

let result: number | string;
result = 10; // Valid
result = "error"; // Also valid
  • Tips and Tricks: Use union types to handle different scenarios for a variable.
  • Best Use Cases: Great for representing diverse data types.

14) Intersection Types
Intersection types allow combining multiple types into one.

type A = { a: number };
type B = { b: string };
type C = A & B; // C has both a number property and a string property
  • Tips and Tricks: Intersection types are useful for combining different types for complex scenarios.
  • Best Use Cases: Helpful for creating complex data structures.

15) Type Aliases
Type aliases allow you to create a name for any data type.

type Age = number;
let userAge: Age = 25;
  • Tips and Tricks: Use type aliases to give descriptive names to complex types.
  • Best Use Cases: Useful for improving code readability and maintainability.

16) Triple-Slash Directives
Triple-slash directives are single-line comments containing a single XML tag.

/// <reference path="myModule.d.ts" />
  • Tips and Tricks: Triple-slash directives are often used to declare dependencies between files.
  • Best Use Cases: Useful when working with modules and declaration files.

17) Type Checking JavaScript Files
TypeScript can be used to check and type-check JavaScript files.

// @ts-check
let num: number = "not a number"; // TypeScript will throw a type error
  • Tips and Tricks: Type checking JavaScript files can catch bugs and improve code quality.
  • Best Use Cases: Useful for gradually migrating a JavaScript codebase to TypeScript.

18) Type Inference for Destructured Objects
TypeScript can infer types for destructured objects.

let person = { name: "Alice", age: 30 };
let { name, age } = person; // TypeScript infers the types of 'name' and 'age'
  • Tips and Tricks: Type inference for destructured objects can save time and reduce redundancy.
  • Best Use Cases: Helpful for working with complex data structures.

19) Conditional Types
Conditional types in TypeScript allow you to create types that depend on other types.

type NonNullable<T> = T extends null | undefined ? never : T;
type StringOrNumber = string | number;
type NonNullableStringOrNumber = NonNullable<StringOrNumber>; // Result: string | number
  • Tips and Tricks: Conditional types are powerful for creating flexible and conditional type definitions.
  • Best Use Cases: Useful for creating generic types that depend on conditions.

20) Mapped Types
Mapped types in TypeScript allow you to create new types from existing types.

type Flags = {
option1: boolean;
option2: boolean;
};
type NullableFlags = { [K in keyof Flags]: Flags[K] | null }; // Result: { option1: boolean | null, option2: boolean | null }
  • Tips and Tricks: Mapped types are useful for transforming existing types into new ones.
  • Best Use Cases: Helpful for creating variations of existing types.

21) Declaration Merging
Declaration merging in TypeScript allows combining multiple declarations for the same entity.

interface User {
name: string;
}
interface User {
age: number;
}
let newUser: User = { name: "Alice", age: 30 };
  • Tips and Tricks: Declaration merging is useful for extending existing types without modifying them directly.
  • Best Use Cases: Helpful for adding functionality to third-party libraries.

22) Type Guards with Classes
Type guards can also be used with classes to narrow down the type of an instance.

class Animal {
move() {
console.log("Moving...");
}
}
class Dog extends Animal {
bark() {
console.log("Woof!");
}
}
function isDog(animal: Animal): animal is Dog {
return (animal as Dog).bark !== undefined;
}
  • Tips and Tricks: Type guards with classes are useful for handling polymorphic behavior.
  • Best Use Cases: Great for dealing with inheritance and polymorphism.

23) Tuple Types
Tuple types in TypeScript allow expressing an array where the type of a fixed number of elements is known.

let coordinates: [number, number] = [10, 20];
  • Tips and Tricks: Use tuple types when working with fixed-length arrays with known element types.
  • Best Use Cases: Useful for representing structured data like coordinates, RGB values, etc.

24) Index Signatures
Index signatures allow defining how objects can be indexed.

interface StringArray {
[index: number]: string;
}
let myArray: StringArray = ["a", "b", "c"];
  • Tips and Tricks: Index signatures are useful for working with objects that behave like arrays.
  • Best Use Cases: Helpful for dealing with dynamic data structures.

25) Type Guards with typeof and instanceof
Type guards can be created using the typeof and instanceof operators.

function logValue(value: string | number) {
if (typeof value === "string") {
console.log(value.toUpperCase());
} else if (value instanceof Number) {
console.log(value.valueOf());
}
}
  • Tips and Tricks: Use typeof for primitive types and instanceof for checking instances of classes.
  • Best Use Cases: Useful for checking specific types in conditional blocks.

26) Recursive Types
TypeScript supports defining recursive types where a type refers to itself.

interface TreeNode {
value: string;
children: TreeNode[];
}
  • Tips and Tricks: Recursive types are useful for representing hierarchical data structures.
  • Best Use Cases: Great for modeling tree-like data.

27) String Literal Types
String literal types allow defining a type that can only have specific string values.

type Direction = "up" | "down" | "left" | "right";
let move: Direction = "up";
  • Tips and Tricks: String literal types help create specific and concise type definitions.
  • Best Use Cases: Useful for representing a fixed set of string values.

28) Namespace Merging
Namespace merging allows extending existing namespaces across multiple files.

// math.ts
namespace Math {
export function subtract(a: number, b: number): number {
return a - b;
}
}
// extendedMath.ts
namespace Math {
export function multiply(a: number, b: number): number {
return a * b;
}
}
  • Tips and Tricks: Namespace merging is useful for adding functionality to existing namespaces.
  • Best Use Cases: Helpful for modularizing code across multiple files.

29) Type Predicates
Type predicates are functions that return a type predicate to narrow down the type within a conditional block.

function isString(value: any): value is string {
return typeof value === "string";
}
if (isString(input)) {
console.log(input.toUpperCase());
}
  • Tips and Tricks: Type predicates are useful for creating reusable type narrowing functions.
  • Best Use Cases: Great for handling complex type checks.

30) Inference and Strict Mode
TypeScript’s strict mode enables additional type checking options to catch more errors.

// @ts-check
let num: number = "not a number"; // TypeScript in strict mode will throw a type

31) Type Guards with in Operator
Type guards can also be created using the in operator to check for the existence of a property in an object.

function hasName(obj: any): obj is { name: string } {
return "name" in obj;
}
if (hasName(user)) {
console.log(user.name);
}
  • Tips and Tricks: The in operator is useful for checking the presence of properties dynamically.
  • Best Use Cases: Helpful for checking object properties in a type-safe manner.

32) Type Inference for Arrays
TypeScript can infer the type of arrays based on the elements assigned to them.

numbers = [1, 2, 3]; // TypeScript infers 'numbers' as number[]
  • Tips and Tricks: Type inference for arrays can simplify code and improve readability.
  • Best Use Cases: Useful for working with arrays of known elements.

33) Promises and Async/Await
TypeScript supports Promises and the async/await syntax for handling asynchronous operations.

function fetchData(): Promise<string> {
return new Promise(resolve => {
setTimeout(() => {
resolve("Data fetched!");
}, 2000);
});
}
async function fetchDataAsync() {
const data = await fetchData();
console.log(data);
}
  • Tips and Tricks: Promises and async/await are essential for handling asynchronous code in a synchronous-like manner.
  • Best Use Cases: Great for managing asynchronous operations in a readable way.

34) Generics Constraints
Generics constraints allow restricting the types that can be used with a generic type parameter.

function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
  • Tips and Tricks: Generics constraints ensure type safety and provide more specific information to TypeScript.
  • Best Use Cases: Useful for working with generic types in a controlled way.

35) Type Inference for Default Values
TypeScript can infer types based on default values assigned to variables.

message = "Hello, World!"; // TypeScript infers 'message' as string
  • Tips and Tricks: Type inference for default values can be convenient for initializing variables without explicitly specifying types.
  • Best Use Cases: Useful for reducing verbosity in code and allowing TypeScript to infer types based on initial values.

THANK YOU FOR CHECKING THIS POST

I hope you liked the article! ❤️

Connect with me: LinkedIn.

Explore my YouTube Channel! If you find it useful.

Please give my GitHub Projects a star ⭐️

Happy Coding! 🚀
Thanks for 22097! 🤗

--

--

Safdar Ali

A Software Engineer dedicated to continuous learning and growth, discovering new knowledge every day.