You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Sure! Here's a basic tutorial on TypeScript to help you get started. π
Introduction to TypeScript
TypeScript is a superset of JavaScript that adds static typing and other powerful features, making it easier to develop scalable and robust applications.
classAnimal{name: string;constructor(name: string){this.name=name;}speak(): void{console.log(`${this.name} makes a noise.`);}}letdog=newAnimal("Dog");dog.speak();// Output: Dog makes a noise.
Generics
functionidentity<T>(arg: T): T{returnarg;}console.log(identity<number>(5));// Output: 5console.log(identity<string>("Hi"));// Output: Hi
4. Compiling and Watching Files
Watch Mode
Automatically recompile files when changes are made:
Here's a tutorial for basic types in TypeScript! π
TypeScript Basic Types Tutorial
TypeScript provides static typing, allowing you to define variable types explicitly. This feature helps catch errors during development. Let's explore the basic types in TypeScript.
1. Boolean
Represents a true or false value.
letisDone: boolean=true;// β CorrectletisPending: boolean=false;// β Correct// let status: boolean = "true"; β Error: Type 'string' is not assignable to type 'boolean'.
2. Number
Represents both integers and floating-point numbers.
letreadonlyNumbers: readonlynumber[]=[1,2,3];// readonlyNumbers.push(4); β Error: Cannot add elements to a readonly array.
5. Tuple
Represents a fixed-size array with known types at each position.
letperson: [string,number]=["Alice",25];console.log(person[0]);// Output: Aliceconsole.log(person[1]);// Output: 25// person[0] = 30; β Error: Type 'number' is not assignable to type 'string'.
π‘ Tip: Avoid using any unless absolutely necessary. It reduces the benefits of TypeScript.
8. Unknown
Similar to any, but safer. Type checks must be performed before using it.
letvalue: unknown="Hello";// value.toUpperCase(); β Error: Object is of type 'unknown'.if(typeofvalue==="string"){console.log(value.toUpperCase());// β Safe usage}
Hereβs a tutorial for functions and objects in TypeScript! π
Functions and Objects in TypeScript
TypeScript provides static typing and type annotations to functions and objects, ensuring better readability and fewer runtime errors. Letβs dive in!
Parameter Types: Enforces that a and b must be numbers.
Return Type: Enforces the function to return a number.
1.2 Optional Parameters
Use ? to mark a parameter as optional.
functiongreet(name: string,age?: number): string{if(age!==undefined){return`Hello ${name}, you are ${age} years old.`;}return`Hello ${name}`;}console.log(greet("Alice"));// Output: Hello Aliceconsole.log(greet("Bob",30));// Output: Hello Bob, you are 30 years old.
1.3 Default Parameters
Provide default values if no arguments are passed.
functiongreet(name: string="Guest"): string{return`Hello ${name}`;}console.log(greet());// Output: Hello Guestconsole.log(greet("Alice"));// Output: Hello Alice
1.4 Rest Parameters
Accepts an infinite number of arguments as an array.
letperson: {readonlyid: number;name: string}={id: 101,name: "Alice",};// person.id = 102; β Error: Cannot assign to 'id' because it is a read-only property.
2.4 Nested Objects
letemployee: {name: string;address: {city: string;country: string;};}={name: "John",address: {city: "New York",country: "USA",},};console.log(employee.address.city);// Output: New York
2.5 Object with Methods
letcar: {model: string;start(): void;}={model: "Tesla",start(){console.log("Car started!");},};car.start();// Output: Car started!
interfaceProduct{readonlyid: number;name: string;price?: number;// Optional}letproduct: Product={id: 1,name: "Laptop",};// product.id = 2; β Error: Cannot assign to 'id' because it is a read-only property.
3.3 Extending Interfaces
interfaceAnimal{name: string;}interfaceDogextendsAnimal{breed: string;}letdog: Dog={name: "Buddy",breed: "Golden Retriever",};console.log(dog.breed);// Output: Golden Retriever
Hereβs a tutorial for custom functions and custom objects in TypeScript! π
Custom Functions and Custom Objects in TypeScript
Custom functions and objects allow you to create reusable components with defined structures and behavior. This tutorial covers how to define, use, and extend custom functions and objects effectively in TypeScript.
1. Custom Functions
Custom functions in TypeScript can be designed with specific types, parameters, and return values.
π‘ Key Takeaway: Function overloading allows you to handle different types of input with a single function.
2. Custom Objects
Custom objects in TypeScript are created using interfaces, type aliases, or classes.
2.1 Custom Object with Type Alias
typePerson={name: string;age: number;greet: ()=>string;};constuser: Person={name: "Alice",age: 25,greet(){return`Hello, my name is ${this.name}`;},};console.log(user.greet());// Output: Hello, my name is Alice
π‘ Key Takeaway: Use interfaces to define reusable structures for objects.
2.3 Optional and Readonly Properties
interfaceCar{readonlyid: number;// Can't be changedmodel: string;price?: number;// Optional property}constcar: Car={id: 1,model: "Tesla Model S",};// car.id = 2; β Error: Cannot assign to 'id' because it is a read-only property.console.log(car.model);// Output: Tesla Model S
2.4 Nested Objects
interfaceEmployee{name: string;position: string;address: {city: string;country: string;};}constemp: Employee={name: "John",position: "Developer",address: {city: "New York",country: "USA",},};console.log(emp.address.city);// Output: New York
Classes in TypeScript allow for blueprints to create objects with methods and properties.
3.1 Basic Class Example
classAnimal{name: string;constructor(name: string){this.name=name;}speak(): void{console.log(`${this.name} makes a noise.`);}}constdog=newAnimal("Dog");dog.speak();// Output: Dog makes a noise.
3.2 Inheritance in Classes
classAnimal{name: string;constructor(name: string){this.name=name;}speak(): void{console.log(`${this.name} makes a noise.`);}}classDogextendsAnimal{bark(): void{console.log(`${this.name} barks.`);}}constdog=newDog("Buddy");dog.speak();// Output: Buddy makes a noise.dog.bark();// Output: Buddy barks.
3.3 Getters and Setters
classPerson{private_age: number;constructor(age: number){this._age=age;}getage(): number{returnthis._age;}setage(value: number){if(value>0){this._age=value;}else{thrownewError("Age must be positive!");}}}constperson=newPerson(25);console.log(person.age);// Output: 25person.age=30;console.log(person.age);// Output: 30
Hereβs a tutorial for classes in TypeScript! π
Classes in TypeScript
Classes in TypeScript provide a blueprint for creating objects with properties and methods. They bring object-oriented programming (OOP) concepts like inheritance, encapsulation, and polymorphism into TypeScript.
1. Creating a Basic Class
classPerson{name: string;// Propertyage: number;// Property// Constructorconstructor(name: string,age: number){this.name=name;this.age=age;}// Methodgreet(): string{return`Hello, my name is ${this.name} and I am ${this.age} years old.`;}}constperson1=newPerson("Alice",25);console.log(person1.greet());// Output: Hello, my name is Alice and I am 25 years old.
Explanation:
Properties: Variables inside the class (name, age).
Constructor: Initializes properties when creating a new object.
Methods: Functions that operate on the class (greet()).
2. Access Modifiers
TypeScript provides modifiers to control the visibility of class members:
Modifier
Accessibility
public
Accessible everywhere (default behavior).
private
Accessible only inside the class.
protected
Accessible inside the class and derived (child) classes.
Example
classPerson{publicname: string;// Accessible everywhereprivateage: number;// Accessible only within this classprotectedjob: string;// Accessible within this and derived classesconstructor(name: string,age: number,job: string){this.name=name;this.age=age;this.job=job;}publicdisplayInfo(): string{return`${this.name} is a ${this.job}.`;}}constperson1=newPerson("Alice",25,"Developer");console.log(person1.displayInfo());// β Output: Alice is a Developer.// console.log(person1.age); β Error: 'age' is private
3. Readonly Properties
Properties marked as readonly cannot be modified after initialization.
classBook{readonlytitle: string;constructor(title: string){this.title=title;}displayTitle(): string{returnthis.title;}}constbook=newBook("TypeScript Handbook");console.log(book.displayTitle());// Output: TypeScript Handbook// book.title = "JavaScript Guide"; β Error: Cannot assign to 'title' because it is readonly.
4. Getters and Setters
Getters and setters control access to private properties.
Hereβs a tutorial for interfaces in TypeScript! π
Interfaces in TypeScript
An interface in TypeScript defines the structure or contract for an object, class, or function. It helps enforce type checking and ensures consistency in code.
1. Creating a Basic Interface
Example:
interfacePerson{name: string;age: number;}constperson1: Person={name: "Alice",age: 25,};console.log(person1.name);// Output: Alice
π‘ Key Points:
The Person interface defines the required structure.
The object person1 must match the shape defined by the interface.
Hereβs a tutorial for namespaces in TypeScript! π
Namespaces in TypeScript
Namespaces in TypeScript are used to group related code (variables, functions, interfaces, and classes) under a single namespace to avoid naming conflicts. They are particularly useful in large codebases where multiple modules or libraries might define variables or types with the same name.
Use nested namespaces to organize related code into subgroups.
Access nested namespaces with dot notation.
3. Merging Namespaces
Namespaces with the same name automatically merge their declarations.
Example:
namespaceLibrary{exportclassBook{title: string;constructor(title: string){this.title=title;}}}namespaceLibrary{exportfunctiongetBooks(): string[]{return["1984","Brave New World"];}}// Access merged namespaceconstbook=newLibrary.Book("The Catcher in the Rye");console.log(book.title);// Output: The Catcher in the Ryeconstbooks=Library.getBooks();console.log(books);// Output: [ '1984', 'Brave New World' ]
π‘ Key Points:
Multiple namespace declarations with the same name are merged into one.
Useful for extending functionality without modifying the original namespace.
4. Namespace Aliases
Use aliases for easier access to deeply nested namespaces.
Hereβs a tutorial for Generics in TypeScript! π
Generics in TypeScript
Generics in TypeScript allow you to write reusable and type-safe code by creating components (functions, classes, or interfaces) that work with multiple data types. They provide type flexibility while maintaining type checking.
1. Why Use Generics?
Consider the following function that returns the first element of an array:
functiongetFirstElement(arr: any[]): any{returnarr[0];}console.log(getFirstElement([1,2,3]));// Output: 1console.log(getFirstElement(["a","b","c"]));// Output: "a"
Problems:
No Type Safety: The return type is any, so TypeScript cannot enforce type checking.
Loss of Intellisense: Autocomplete and type hints are lost.
Solution with Generics
functiongetFirstElement<T>(arr: T[]): T{returnarr[0];}console.log(getFirstElement<number>([1,2,3]));// Output: 1console.log(getFirstElement<string>(["a","b"]));// Output: "a"
π‘ Key Points:
<T> defines a generic type variable.
T is used as a placeholder for the actual type.
Type inference automatically detects types, improving type safety.
Hereβs a tutorial for decorators in TypeScript! π
Decorators in TypeScript
Decorators in TypeScript are special functions that can be attached to classes, methods, properties, accessors, and parameters to add metadata or functionality at runtime.
Decorators are heavily used in Angular, NestJS, and other frameworks to simplify code and enhance reusability.
A class decorator is applied to an entire class to modify or enhance its behavior.
Example: Adding Metadata
functionLogger(constructor: Function){console.log("Logging...");console.log(constructor);}
@LoggerclassPerson{constructor(publicname: string){console.log(`Person created: ${this.name}`);}}constperson=newPerson("Alice");// Output:// Logging...// class Person { ... }// Person created: Alice
π‘ Key Points:
@Logger is the decorator applied to the Person class.
Decorator logs method calls, parameters, and results.
Useful for debugging and analytics.
5. Property Decorators
A property decorator is used to add metadata or validation to a property.
Example: Adding Metadata
functionReadOnly(target: any,propertyKey: string){Object.defineProperty(target,propertyKey,{writable: false,});}classUser{
@ReadOnlyusername: string="admin";}constuser=newUser();// user.username = "newAdmin"; β Error: Cannot assign to 'username' because it is read-only.console.log(user.username);// Output: admin
π‘ Key Points:
Prevents modifications to read-only properties.
Commonly used for validation rules.
6. Accessor Decorators
An accessor decorator modifies the behavior of getters and setters.
Example: Validating Setter Values
functionPositive(target: any,propertyKey: string,descriptor: PropertyDescriptor){constoriginalSet=descriptor.set;descriptor.set=function(value: number){if(value<0){thrownewError("Value must be positive!");}originalSet!.call(this,value);};}classAccount{private_balance: number=0;
@Positivesetbalance(value: number){this._balance=value;}getbalance(): number{returnthis._balance;}}constacc=newAccount();acc.balance=100;// β Allowedconsole.log(acc.balance);// Output: 100// acc.balance = -50; β Error: Value must be positive!
π‘ Key Points:
Used for input validation and data constraints.
7. Parameter Decorators
A parameter decorator is used to track or validate parameters of a method.
Example: Parameter Metadata
functionLogParameter(target: any,propertyKey: string,parameterIndex: number){console.log(`Parameter index: ${parameterIndex} in method: ${propertyKey}`);}classPerson{greet(@LogParametermessage: string){console.log(message);}}constp=newPerson();p.greet("Hello");// Output: Parameter index: 0 in method: greet
π‘ Key Points:
Tracks and logs metadata for method parameters.
8. Combining Decorators
Multiple decorators can be stacked on a single target.
Hereβs a list of the most used decorators in Next.js (with TypeScript) and related frameworks like NestJS for backend development. π
1. Next.js Decorators
Although Next.js does not natively support decorators like NestJS, decorators are commonly used in combination with class-based controllers or custom handlers when integrating libraries such as type-graphql or tRPC.
exportfunctionRole(role: string){returnfunction(target: any,key: string,descriptor: PropertyDescriptor){constoriginalMethod=descriptor.value;descriptor.value=asyncfunction(req: any,res: any){constuserRole=req.headers.role;// Assume role is sent in headersif(userRole!==role){res.status(403).json({error: "Forbidden"});thrownewError("Access Denied");}returnoriginalMethod.apply(this,[req,res]);};};}// Example Usage:classAPIController{
@Role("admin")asyncdeleteUser(req: any,res: any){res.status(200).json({message: "User deleted."});}}
2. Common Decorators in NestJS (often used with Next.js API Routes)
2.1 Controller Decorator (@Controller)
Defines a controller to handle incoming requests.
@Controller('users')exportclassUserController{
@Get()findAll(){return"List of users";}}
2.2 HTTP Method Decorators (@Get, @Post, etc.)
Used to map routes to methods inside a controller.
@Controller('users')exportclassUserController{
@Get(':id')// HTTP GET requestfindOne(@Param('id')id: string){return`User with ID: ${id}`;}
@Post()// HTTP POST requestcreate(@Body()data: any){return{message: "User created", data };}}
Decorators son mΓ‘s comunes en arquitecturas orientadas a clases como NestJS o Angular.
React (y por lo tanto Next.js) promueve un enfoque basado en funciones y hooks como useState, useEffect y useContext para manejar el estado, efectos secundarios y lΓ³gica compartida.
Los hooks reemplazan la necesidad de decoradores en la mayorΓa de los casos al permitir composiciΓ³n de lΓ³gica reutilizable.
Este enfoque ofrece flexibilidad sin depender de decoradores experimentales.
5. IntegraciΓ³n con APIs Modernas como tRPC y GraphQL
En lugar de usar decoradores, Next.js promueve el uso de bibliotecas modernas como tRPC o GraphQL, que ofrecen tipado seguro sin necesidad de decoradores.
6. Frameworks Orientados a Decorators: NestJS y TypeORM
Si necesitas trabajar con decorators, frameworks como NestJS o TypeORM pueden ser mejores opciones. Por ejemplo:
NestJS Decorators para API REST:
@Controller('users')exportclassUserController{
@Get()getAllUsers(){return"List of users";}
@Post()createUser(@Body()body: any){return{message: "User created",data: body};}}
7. Simplicidad y OptimizaciΓ³n para SSR/SSG
Next.js prioriza caracterΓsticas como:
Server-Side Rendering (SSR).
Static Site Generation (SSG).
Incremental Static Regeneration (ISR).
Estas caracterΓsticas requieren un enfoque mΓ‘s declarativo y basado en funciones en lugar de clases con decoradores.
ConclusiΓ³n: ΒΏDeberΓas usar decoradores en Next.js?
Aunque puedes habilitar y usar decoradores en Next.js configurando experimentalDecorators en tsconfig.json, no es recomendable porque:
No es compatible nativamente con React y Next.js.
Alternativas como hooks y HOCs son mΓ‘s naturales en aplicaciones modernas basadas en React.
Decorators siguen siendo experimentales y podrΓan cambiar en el futuro.
LibrerΓas modernas como tRPC, class-validator y Zod manejan validaciones y tipado de manera mΓ‘s efectiva.
RecomendaciΓ³n
Si deseas aprovechar decorators en aplicaciones grandes con lΓ³gica backend avanzada, usa NestJS junto con Next.js. Para aplicaciones centradas en frontend y SSR, sigue usando hooks, HOCs, y middlewares en Next.js.
π¨π€π§ ΒΏNecesitas ejemplos especΓficos para alguna implementaciΓ³n? Β‘AvΓsame!
Hereβs how to replace decorators in Next.js for:
API Request Validation
Role-Based Access Control (RBAC)
Logging Middleware
Dependency Injection (DI)
Using functional programming patterns (Middlewares, Higher-Order Functions, and Utility Functions), you can achieve similar results without relying on experimental decorators. π
1. API Request Validation
Instead of decorators, use middleware functions or validation libraries like Zod or Yup.
Example Using Zod for Validation
npm install zod
import{NextApiRequest,NextApiResponse}from"next";import{z}from"zod";// Validation schemaconstuserSchema=z.object({name: z.string().min(3,"Name must be at least 3 characters long"),email: z.string().email("Invalid email format"),});// Validation middlewareconstvalidateRequest=(schema: any)=>(handler: any)=>async(req: NextApiRequest,res: NextApiResponse)=>{try{schema.parse(req.body);// Validate request bodyreturnhandler(req,res);}catch(err){returnres.status(400).json({error: err.errors});}};// Usageconsthandler=async(req: NextApiRequest,res: NextApiResponse)=>{res.status(200).json({message: "User created successfully!"});};exportdefaultvalidateRequest(userSchema)(handler);
Why it works?
Achieves type-safe validation without decorators.
Uses Zod for schema-based validation, providing autocomplete and error handling.
Flexible and reusable middleware for validation.
2. Role-Based Access Control (RBAC)
Instead of decorators, use Higher-Order Functions (HOCs) for RBAC middleware.
RBAC Middleware Example
constwithRole=(allowedRoles: string[])=>(handler: any)=>async(req: NextApiRequest,res: NextApiResponse)=>{constuserRole=req.headers.role;// Simulate user role from headersif(!allowedRoles.includes(userRoleasstring)){returnres.status(403).json({error: "Forbidden"});}returnhandler(req,res);};// Usage Exampleconsthandler=async(req: NextApiRequest,res: NextApiResponse)=>{res.status(200).json({message: "Access granted!"});};exportdefaultwithRole(["admin"])(handler);
Why it works?
Implements RBAC without decorators.
Reusable for multiple endpoints.
Allows dynamic roles based on route requirements.
3. Logging Middleware
Instead of decorators, use logging middleware with a Higher-Order Function.
Combines validation, RBAC, and logging seamlessly.
Flexible, reusable, and supports modularity.
6. Summary Table: Decorators vs Middleware
Use Case
Decorator Example
Middleware Replacement
Validation
@Validate()
Middleware with Zod schema validation.
RBAC (Role-Based Access)
@Role('admin')
Middleware with role-based access using headers.
Logging
@Log()
Middleware for request and response logging.
Dependency Injection (DI)
@Injectable() and @Inject()
Manual DI using contexts (React) or factories (API).
7. Final Thoughts
While decorators are useful in frameworks like NestJS, Next.js focuses on functional programming patterns, favoring middlewares, HOCs, and custom hooks.
You can achieve similar behavior without decorators using these modern approaches:
Zod or Yup for validation.
HOCs and Middlewares for RBAC and logging.
Dependency Injection via contexts or factories.
When to Use Decorators?
If your project requires class-based design and decorators, consider combining Next.js with NestJS for backend APIs and Next.js for the frontend.
π¨π€π§ Need more examples or explanations? Let me know!