Mejores Practicas
## Objetivos de Aprendizaje
- Escribir TypeScript limpio y mantenible
- Seguir convenciones de nomenclatura
- Dominar la seguridad de tipos
- Aplicar pruebas efectivas
- Migrar desde JavaScript
## Estilo de Codigo
### Convenciones de Nomenclatura
```typescript
// Clases: PascalCase
class BankAccount { }
// Interfaces: PascalCase (o a veces con prefijo I)
interface UserService { }
interface IUser { } // Alternativa: con prefijo I
// Variables y funciones: camelCase
let accountBalance: number;
function calculateInterest() { }
// Constantes: UPPER_SNAKE_CASE
const MAX_RETRY_COUNT = 3;
const API_BASE_URL = "https://api.example.com";
// Alias de tipo y enums: PascalCase
type UserRole = "admin" | "user";
enum HttpStatus { Ok, NotFound }
```
### Formato
```typescript
// Usar 2 o 4 espacios consistentemente
// Longitud de linea: ~100 caracteres maximo
// Una declaracion por linea
let age: number;
let name: string;
// Llaves siempre en la misma linea
if (condition) {
doSomething();
} else {
doOther();
}
```
## Seguridad de Tipos
### Modo Strict
```json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true
}
}
```
### Evitar Any
```typescript
// Mal
function processData(data: any): any {
return data;
}
// Bien
function processData(data: T): T {
return data;
}
// Si debes usar any
function processData(data: unknown): unknown {
return data;
}
```
### Inferencia de Tipos
```typescript
// TypeScript infiere: let x = 10 es number
let x = 10;
// x = "hola"; // Error
// Explicito cuando sea necesario
let a: number | string;
a = 10;
a = "hola";
```
## Interfaces vs Alias de Tipo
### Usar Interface para Objetos
```typescript
interface User {
id: number;
name: string;
email: string;
}
```
### Usar Type para Uniones y Tuplas
```typescript
type ID = string | number;
type Point = [number, number];
type Callback = () => void;
```
## Funciones
### Parametros y Retornos Tipados
```typescript
// Bien: tipos explicitos
function add(a: number, b: number): number {
return a + b;
}
// Mal: confiar en inferencia
const add = (a, b) => a + b;
```
### Parametros Opcionales
```typescript
// Opcional con ?
function greet(name?: string): string {
return name ? `Hola, ${name}!` : "Hola!";
}
// Valores por defecto
function createUser(name: string, role: string = "usuario") {
return { name, role };
}
```
## Clases
### Encapsulacion
```typescript
class BankAccount {
private balance: number = 0;
constructor(initialBalance: number = 0) {
this.balance = initialBalance;
}
deposit(amount: number): void {
if (amount > 0) {
this.balance += amount;
}
}
withdraw(amount: number): boolean {
if (amount > 0 && amount <= this.balance) {
this.balance -= amount;
return true;
}
return false;
}
getBalance(): number {
return this.balance;
}
}
```
### readonly para Constantes
```typescript
class Config {
readonly API_URL = "https://api.example.com";
readonly MAX_RETRIES = 3;
}
```
## Enums
### Usar Const Enums
```typescript
const enum Status {
Active,
Pending,
Completed
}
if (status === Status.Active) {
// ...
}
```
### Enums de Cadena para Flexibilidad
```typescript
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT"
}
```
## Genericos
### Restringir Cuando Sea Necesario
```typescript
// Generico con restriccion
function logLength(arg: T): T {
console.log(arg.length);
return arg;
}
logLength("hola"); // OK
logLength([1, 2, 3]); // OK
// logLength(123); // Error
```
### Usar Tipos de Utilidad
```typescript
// No reimplementar
type PartialUser = Partial;
type UserPreview = Pick;
type UserWithoutEmail = Omit;
```
## Manejo de Errores
### Tipos Especificos
```typescript
try {
JSON.parse(invalidJson);
} catch (error) {
if (error instanceof SyntaxError) {
console.error("Error de sintaxis JSON:", error.message);
} else {
throw error;
}
}
```
### Patron Result
```typescript
type Result =
| { success: true; value: T }
| { success: false; error: E };
function divide(a: number, b: number): Result {
if (b === 0) {
return { success: false, error: new Error("Division por cero") };
}
return { success: true, value: a / b };
}
```
## Manejo de Null
### Encadenamiento Opcional
```typescript
interface User {
address?: {
city: string;
};
}
let city = user?.address?.city; // undefined si falta
```
### Fusion Nula
```typescript
let name = userName ?? "Anonimo";
let count = items.length ?? 0;
```
### Evitar Asercion No-nula
```typescript
// Arriesgado
let name = value!;
// Mejor
if (value !== null && value !== undefined) {
let name = value;
}
```
## Migracion desde JavaScript
### Paso 1: Anadir tsconfig.json
```json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": false,
"allowJs": true
}
}
```
### Paso 2: Habilitar checkJs
```json
{
"compilerOptions": {
"checkJs": true
}
}
```
### Paso 3: Anadir Anotaciones de Tipo
```javascript
// Antes (JavaScript)
function add(a, b) {
return a + b;
}
```
```typescript
// Despues (TypeScript)
function add(a: number, b: number): number {
return a + b;
}
```
### Paso 4: Habilitar Modo Strict
```json
{
"compilerOptions": {
"strict": true
}
}
```
## Pruebas
### Con Jest
```typescript
// calculator.ts
export function add(a: number, b: number): number {
return a + b;
}
// calculator.test.ts
import { add } from "./calculator";
test("suma dos numeros", () => {
expect(add(2, 3)).toBe(5);
});
```
### Mocks Tipados
```typescript
interface UserService {
getUser(id: number): Promise;
}
function getUserWithCache(
id: number,
service: UserService
): Promise {
// Implementacion
}
```
## Documentacion
### JSDoc con Tipos
```typescript
/**
* Calcula el area de un rectangulo.
*
* @param width - El ancho del rectangulo
* @param height - La altura del rectangulo
* @returns El area (width * height)
* @throws {Error} Si width o height son negativos
*/
function rectangleArea(width: number, height: number): number {
if (width < 0 || height < 0) {
throw new Error("Las dimensiones deben ser positivas");
}
return width * height;
}
```
## Herramientas
### ESLint
```json
{
"parser": "@typescript-eslint/parser",
"rules": {
"no-unused-vars": "error",
"prefer-const": "error"
}
}
```
### Prettier
```json
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5"
}
```
## Resumen
- Habilitar `strict: true` para seguridad de tipos
- Preferir interfaces para formas de objetos
- Usar type para uniones, tuplas, primitivos
- Evitar `any`, usar `unknown` en su lugar
- Usar encadenamiento opcional y fusion nula
- Escribir funciones puras cuando sea posible
- Usar `readonly` para constantes
- Documentar APIs publicas con JSDoc
- Probar funciones tipadas
- Migrar gradualmente desde JavaScript
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →