DEV Community

Cover image for TypeScript | Beginner
Shubham Tiwari
Shubham Tiwari

Posted on • Edited on

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"));
Enter fullscreen mode Exit fullscreen mode
  • 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];
Enter fullscreen mode Exit fullscreen mode
  • 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}!`;
}
Enter fullscreen mode Exit fullscreen mode
  • 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.`);
    }
}
Enter fullscreen mode Exit fullscreen mode
  • 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");
Enter fullscreen mode Exit fullscreen mode
  • 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;
Enter fullscreen mode Exit fullscreen mode
  • 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;

Enter fullscreen mode Exit fullscreen mode
  • 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
    }
}
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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();
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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
}
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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;
Enter fullscreen mode Exit fullscreen mode
  • 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" />
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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'
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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 }
Enter fullscreen mode Exit fullscreen mode
  • 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 };
Enter fullscreen mode Exit fullscreen mode
  • 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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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];
Enter fullscreen mode Exit fullscreen mode
  • 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"];
Enter fullscreen mode Exit fullscreen mode
  • 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());
    }
}
Enter fullscreen mode Exit fullscreen mode
  • 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[];
}
Enter fullscreen mode Exit fullscreen mode
  • 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";
Enter fullscreen mode Exit fullscreen mode
  • 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;
    }
}
Enter fullscreen mode Exit fullscreen mode
  • 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());
}
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode
  • 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[]
Enter fullscreen mode Exit fullscreen mode
  • 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);
}
Enter fullscreen mode Exit fullscreen mode
  • 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];
}
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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
You can contact me on -
Instagram - https://www.instagram.com/supremacism__shubh/
LinkedIn - https://www.linkedin.com/in/shubham-tiwari-b7544b193/
Email - shubhmtiwri00@gmail.com

You can help me with some donation at the link below Thank you👇👇
☕ --> https://www.buymeacoffee.com/waaduheck <--

Also check these posts as well

Top comments (2)

Collapse
 
ahmadadibzad profile image
Ahmad Adibzad

Great post!

Collapse
 
brittosmonteiro profile image
Lucas

Awesome. This helped me a lot. Thank you!