Translate

Mostrando las entradas con la etiqueta typescript. Mostrar todas las entradas
Mostrando las entradas con la etiqueta typescript. Mostrar todas las entradas

martes, 5 de noviembre de 2024

Pattern Matching en Typescript con ts-pattern


En TypeScript, a menudo necesitamos simplificar la lógica de condiciones, y aunque existen alternativas como if-else o switch, estas pueden volverse confusas en estructuras más complejas. Aquí es donde ts-pattern entra en juego, ofreciendo una forma poderosa y funcional de hacer pattern matching. Inspirado en técnicas de lenguajes como Haskell y Scala, ts-pattern permite coincidir patrones de datos y manejar casos de manera clara y estructurada.

ts-pattern es una biblioteca que lleva el pattern matching a TypeScript, haciéndolo flexible y adecuado para manejar varios tipos de datos y patrones complejos. Esto ayuda a escribir código más conciso y fácil de mantener, especialmente en proyectos grandes donde las condiciones exhaustivas son comunes.

Para instalar ts-pattern en tu proyecto, simplemente ejecuta:


npm install ts-pattern



Luego, impórtalo en tu archivo TypeScript:


import { match } from 'ts-pattern';


Veamos un ejemplo básico para entender cómo funciona match en strings:


const saludo = match('hola')

  .with('hola', () => '¡Hola Mundo!')

  .with('adiós', () => '¡Hasta luego!')

  .otherwise(() => 'Desconocido');

console.log(saludo);  // Salida: ¡Hola Mundo!


ts-pattern también permite manejar objetos anidados o arrays, facilitando el trabajo con estructuras más detalladas. Supongamos que tenemos un estado de carga y queremos manejarlo de manera exhaustiva:


type Estado = 'cargando' | 'éxito' | 'error';


const mensaje = match<Estado>('éxito')

  .with('cargando', () => 'Cargando...')

  .with('éxito', () => '¡Datos cargados!')

  .with('error', () => 'Hubo un problema.')

  .exhaustive();

console.log(mensaje);  // Salida: ¡Datos cargados!


Con .exhaustive(), ts-pattern se asegura de que todos los posibles valores de `Estado` están cubiertos, ayudándote a evitar errores futuros.

ts-pattern simplifica el manejo de múltiples condiciones en TypeScript, mejorando la claridad y la mantenibilidad del código. 

Dejo link: https://github.com/gvergnaud/ts-pattern

miércoles, 23 de octubre de 2024

Tipos Unión en Python


Python introdujo una forma más sencilla de manejar los tipos unión a partir de la versión 3.10, utilizando el operador `|`.

Antes de Python 3.10, las uniones de tipos se representaban usando `Union` del módulo `typing`. Por ejemplo:


from typing import Union


def procesar_valor(valor: Union[int, float]) -> None:

    print(valor)



Esto indica que `valor` puede ser un `int` o un `float`. Sin embargo, esta sintaxis fue simplificada en Python 3.10 con el uso del operador `|`, lo que mejora la legibilidad:


def procesar_valor(valor: int | float) -> None:

    print(valor)


Ambas formas son equivalentes, pero la nueva es más concisa y fácil de leer.

 Ejemplo práctico:


def manejar_respuesta(respuesta: str | None) -> str:

    if respuesta is None:

        return "No hay respuesta"

    return respuesta


TypeScript, un superconjunto de JavaScript, también soporta tipos unión, permitiendo que una variable pueda tener más de un tipo. Al igual que Python, utiliza un símbolo para definir uniones, en este caso también el `|`:


function procesarValor(valor: number | string): void {

    console.log(valor);

}


TypeScript es un lenguaje estáticamente tipado, lo que significa que el compilador verifica los tipos en tiempo de compilación. Si una operación no es válida para uno de los tipos en la unión, el compilador lanzará un error. Python, por otro lado, realiza la verificación de tipos solo cuando se ejecuta (en tiempo de ejecución).

Intersecciones: TypeScript tiene algo llamado tipos intersección (`&`), donde un valor debe cumplir con todas las condiciones de varios tipos al mismo tiempo, lo que no existe en Python.


function combinar(valores: string | number[]): number {

    if (typeof valores === 'string') {

        return valores.length;

    } else {

        return valores.reduce((a, b) => a + b, 0);

    }

}


En este caso, `valores` puede ser una cadena o un arreglo de números, y se maneja cada tipo de forma separada.

El concepto de tipos unión es fundamental para manejar situaciones en las que un valor puede ser de varios tipos. En Python, con la introducción del operador `|` en la versión 3.10, el manejo de uniones se ha vuelto más simple y elegante. 

Los tipos unión son una excelente herramienta en lenguajes con tipado opcional o estático, permitiendo crear código más robusto y manejable cuando se espera que los datos puedan variar en su forma.

viernes, 11 de octubre de 2024

Función map, reduce y filter en Typescript


La función map te permite transformar cada elemento de un array aplicando una función sobre ellos. El resultado es un nuevo array con los valores transformados.


const numeros = [1, 2, 3, 4, 5];

const duplicados = numeros.map((num) => num * 2);

console.log(duplicados); // Salida: [2, 4, 6, 8, 10]


filter se utiliza para eliminar elementos de un array que no cumplan una condición específica. El resultado es un nuevo array solo con los elementos que pasen esa condición.


const numeros = [1, 2, 3, 4, 5];

const pares = numeros.filter((num) => num % 2 === 0);

console.log(pares); // Salida: [2, 4]


reduce es una función poderosa que permite reducir un array a un único valor aplicando una función acumuladora a sus elementos.


const numeros = [1, 2, 3, 4, 5];

const suma = numeros.reduce((acumulador, num) => acumulador + num, 0);

console.log(suma); // Salida: 15


Este ejemplo reduce el array sumando todos los números, resultando en 15. El 0 es el valor inicial del acumulador.

Usar técnicas de programación funcional como map, filter y reduce mejora la legibilidad del código al evitar bucles explícitos y efectos secundarios. Este estilo declarativo hace que sea más fácil razonar sobre el comportamiento de tu aplicación, especialmente cuando trabajas con grandes cantidades de datos o flujos.



viernes, 4 de octubre de 2024

Pattern Matching en TypeScript


Pattern Matching es una característica poderosa que permite comparar una estructura de datos con un patrón y ejecutar el código dependiendo de cómo coincidan. Aunque TypeScript no tiene un soporte nativo de pattern matching al estilo de lenguajes como Scala o Haskell, pero se puede simular de manera efectiva utilizando algunas características como los tipos discriminados y el refinamiento de tipos para implementar pattern matching. Estos tipos combinan un campo común discriminante que puede diferenciar uniones de tipos de forma segura.

Veamos un ejemplo:


type Shape = 

  | { kind: 'circle', radius: number }

  | { kind: 'square', sideLength: number }

  | { kind: 'rectangle', width: number, height: number };


function area(shape: Shape): number {

  switch (shape.kind) {

    case 'circle':

      return Math.PI * shape.radius ** 2;

    case 'square':

      return shape.sideLength ** 2;

    case 'rectangle':

      return shape.width * shape.height;

  }

}


const myCircle: Shape = { kind: 'circle', radius: 5 };

console.log(area(myCircle)); // 78.53981633974483


Otra forma de hacer pattern matching es mediante guard clauses, que son condiciones específicas para cada caso. Aquí tienes un ejemplo:


function printNumber(x: number | string): string {

  if (typeof x === 'number') {

    return `Es un número: ${x}`;

  } else if (typeof x === 'string') {

    return `Es una cadena de texto: ${x}`;

  } else {

    return `Valor no soportado`;

  }

}


// Uso

console.log(printNumber(42));   // Es un número: 42

console.log(printNumber('42')); // Es una cadena de texto: 42


TypeScript también permite un estilo de pattern matching mediante desestructuración de objetos y arrays.


type Person = { name: string, age: number };

type Animal = { species: string };


function describe(input: Person | Animal): string {

  if ('name' in input) {

    return `Persona: ${input.name}, Edad: ${input.age}`;

  } else {

    return `Especie: ${input.species}`;

  }

}


// Uso

const person: Person = { name: 'John', age: 30 };

const animal: Animal = { species: 'Dog' };


console.log(describe(person)); // Persona: John, Edad: 30

console.log(describe(animal)); // Especie: Dog


El uso de `switch` puede complementarse con guardias para realizar un matching más fino de patrones, filtrando por condiciones adicionales.


function classifyNumber(x: number): string {

  switch (true) {

    case x < 0:

      return 'Número negativo';

    case x === 0:

      return 'Cero';

    case x > 0:

      return 'Número positivo';

    default:

      return 'Valor desconocido';

  }

}


console.log(classifyNumber(-5));  // Número negativo

console.log(classifyNumber(0));   // Cero

console.log(classifyNumber(10));  // Número positivo


Si bien TypeScript no tiene soporte nativo para el pattern matching al nivel de otros lenguajes funcionales, podemos simularlo utilizando sus características de refinamiento de tipos, tipos discriminados, guard clauses y desestructuración.

Con estos enfoques, puedes aplicar las ideas de pattern matching de forma clara y eficiente en TypeScript. Este tipo de técnica puede mejorar la legibilidad de tu código y hacerlo más fácil de mantener.


miércoles, 2 de octubre de 2024

Covarianza, Contravarianza e Invarianza en TypeScript


En el contexto de tipos genéricos, la varianza define cómo los subtipos y supertipos de un tipo afectan las relaciones entre otros tipos.

Existen tres tipos principales de varianza:

Covarianza: Un tipo genérico mantiene la relación de subtipos que tiene el tipo con el que trabaja. Es decir, si A es un subtipo de B, entonces Caja<A> será subtipo de Caja<B>.

Contravarianza: Ocurre cuando la relación de subtipos es inversa. Si A es subtipo de B, entonces Caja<B> es subtipo de Caja<A>.

Invarianza: No existe relación entre Caja<A> y Caja<B>, incluso si A es un subtipo de B.

En TypeScript, exiten tipos que son covariantes por defecto. Esto significa que si un tipo genérico tiene un subtipo, esa relación se mantiene con el tipo genérico.

Veamos un ejemplo sencillo para entender esto mejor:


class Animal {

    nombre: string;

    constructor(nombre: string) {

        this.nombre = nombre;

    }

}


class Perro extends Animal {

    ladrar(): void {

        console.log("Guau!");

    }

}


class Gato extends Animal {

    maullar(): void {

        console.log("Miau!");

    }

}


function imprimirAnimales(animales: Animal[]): void {

    animales.forEach(animal => console.log(animal.nombre));

}


const perros: Perro[] = [new Perro("Max"), new Perro("Rex")];

imprimirAnimales(perros); // Esto es válido, ya que Perro es subtipo de Animal


En este ejemplo, Perro[] es subtipo de Animal[], por lo que la función imprimirAnimales puede recibir una lista de perros sin problema. Esto es covarianza.

Contravarianza significa que un tipo más general (supertipo) puede ser pasado donde se espera un tipo más específico (subtipo). En TypeScript, esto es común al trabajar con funciones.


class Carnivoro extends Animal {}

class Herviboro extends Animal {}


function alimentarAnimal(fn: (a: Carnivoro) => void): void {

    const leon = new Carnivoro("León");

    fn(leon);

}


function alimentarCualquierAnimal(a: Animal): void {

    console.log(`Alimentando a un ${a.nombre}`);

}


alimentarAnimal(alimentarCualquierAnimal); // Funciona gracias a la contravarianza

Aquí, la función alimentarCualquierAnimal puede ser utilizada donde se espera una función que trabaje con Carnivoro, ya que Animal es un supertipo de Carnivoro. Esto es contravarianza.

Si un tipo es invariante, no puedes intercambiar subtipos y supertipos, incluso si existe una relación de herencia entre ellos. Este comportamiento es menos común en TypeScript.


Ejemplo de invarianza:


class Caja<T> {

    contenido: T;

    constructor(contenido: T) {

        this.contenido = contenido;

    }

}


const cajaAnimal: Caja<Animal> = new Caja(new Animal("Elefante"));

const cajaPerro: Caja<Perro> = new Caja(new Perro("Max"));


// Esto genera error porque Caja<Perro> no es subtipo de Caja<Animal>

// cajaAnimal = cajaPerro; 


En este caso, Caja<Perro> no es subtipo de Caja<Animal>, aunque Perro sea subtipo de Animal. Esta es la invarianza, donde no hay compatibilidad entre tipos genéricos con diferentes parámetros de tipo.

En TypeScript, no puedes hacer que Caja<Animal> sea compatible con Caja<Perro> directamente debido a la invarianza de los tipos genéricos. Sin embargo, hay formas de aproximar este comportamiento utilizando tipos genéricos más flexibles, como lo son los tipos comodín (similares a los de Java) o utilizando el modificador readonly para hacer la relación covariante.

Se puede hacer que Caja<T> sea covariante para las lecturas si el contenido de la caja solo es accesible de forma de lectura y no de escritura. Esto se logra declarando las propiedades como `readonly`.


class Caja<out T> {

    readonly contenido: T;

    constructor(contenido: T) {

        this.contenido = contenido;

    }

}


const cajaAnimal: Caja<Animal> = new Caja(new Animal("Elefante"));

const cajaPerro: Caja<Perro> = new Caja(new Perro("Max"));

// Como la propiedad es de solo lectura, es covariante

const otraCajaAnimal: Caja<Animal> = cajaPerro; // Funciona


En este caso, al usar readonly, puedes asignar una Caja<Perro> a una Caja<Animal> porque las cajas son covariantes en la lectura. No obstante, ya no podrías modificar el contenido de la caja.

Otra opción es usar la palabra clave extends para indicar que puedes trabajar con cualquier subtipo de Animal.


class Caja<T extends Animal> {

    contenido: T;

    constructor(contenido: T) {

        this.contenido = contenido;

    }

}


function procesarCajaAnimal(caja: Caja<Animal>) {

    console.log(`Animal: ${caja.contenido.nombre}`);

}


const cajaPerro: Caja<Perro> = new Caja(new Perro("Max"));

procesarCajaAnimal(cajaPerro); // Funciona


Aquí estamos diciendo que Caja<T> puede aceptar cualquier tipo que extienda de Animal. Esto permite que una Caja<Perro> sea pasada a una función que espera una Caja<Animal>.

sábado, 28 de septiembre de 2024

Clases Genéricas en TypeScript


Las clases genéricas en TypeScript nos permiten escribir código flexible y reutilizable. En lugar de definir clases específicas para cada tipo de dato, podemos crear clases genéricas que trabajen con múltiples tipos, manteniendo la seguridad de tipos que ofrece TypeScript.

Una clase genérica es aquella que acepta uno o más parámetros de tipo. Estos parámetros de tipo funcionan como marcadores de posición que representan un tipo de dato específico en el momento de la instanciación de la clase.

Veamos un ejemplo: 


class Caja<T> {

    contenido: T;


    constructor(contenido: T) {

        this.contenido = contenido;

    }


    obtenerContenido(): T {

        return this.contenido;

    }

}



En este ejemplo, T es un parámetro de tipo que se utiliza en la clase Caja. No sabemos qué tipo de dato es T hasta que se instancie la clase con un tipo específico.

Puedes instanciar la clase `Caja` con cualquier tipo de dato:


const cajaDeNumero = new Caja<number>(123);

console.log(cajaDeNumero.obtenerContenido()); // 123

const cajaDeTexto = new Caja<string>('Hola');

console.log(cajaDeTexto.obtenerContenido()); // Hola


El tipo `T` se reemplaza con el tipo proporcionado (`number` o `string`), manteniendo el control de tipos y evitando errores durante la compilación.

También es posible utilizar múltiples parámetros de tipo en una clase genérica. Por ejemplo:


class Par<K, V> {

    clave: K;

    valor: V;


    constructor(clave: K, valor: V) {

        this.clave = clave;

        this.valor = valor;

    }


    obtenerClave(): K {

        return this.clave;

    }


    obtenerValor(): V {

        return this.valor;

    }

}


const par = new Par<string, number>('edad', 30);

console.log(par.obtenerClave()); // 'edad'

console.log(par.obtenerValor()); // 30


En este caso, `Par` es una clase genérica que acepta dos parámetros de tipo: `K` para la clave y `V` para el valor. Luego, se instanció con `string` como clave y `number` como valor.

A veces queremos limitar los tipos que un parámetro genérico puede aceptar. Esto se hace usando la palabra clave `extends` para aplicar una restricción al parámetro de tipo.


class LimitarCaja<T extends number | string> {

    contenido: T;


    constructor(contenido: T) {

        this.contenido = contenido;

    }


    mostrarContenido(): void {

        console.log(`Contenido: ${this.contenido}`);

    }

}


const cajaTexto = new LimitarCaja('Hola');

cajaTexto.mostrarContenido(); // Contenido: Hola


const cajaNumero = new LimitarCaja(123);

cajaNumero.mostrarContenido(); // Contenido: 123


Aquí, la clase LimitarCaja solo acepta tipos que sean number o string, lo que añade una restricción al tipo T.

Como ventaja de las clases genericas podemos nombrar: 

  • Reutilización del código: Puedes usar la misma clase para diferentes tipos sin necesidad de duplicar código.
  • Seguridad de tipos: TypeScript asegura que se utilicen los tipos correctos, evitando errores en tiempo de ejecución.
  • Flexibilidad: Permiten que el código sea más flexible, ya que puedes trabajar con cualquier tipo sin perder el control del sistema de tipos.

Las clases genéricas en TypeScript son una herramienta poderosa para crear código flexible y reutilizable. Al permitir que las clases trabajen con múltiples tipos, puedes crear soluciones que se adapten a diferentes escenarios sin sacrificar la seguridad de tipos que hace que TypeScript sea tan útil.



jueves, 11 de julio de 2024

Como podemos manejar las referencias nulas?


El error más frecuente en Java es NullPointerException y me imagino que en otros lenguajes alguno similar...  Para abordar esto, se han introducido estructuras y operadores que ayudan a manejar la ausencia de valores de manera más segura y explícita. 

Por ejemplo en Java se introdujo la clase `Optional` en la versión 8 para manejar valores potencialmente nulos de una manera más segura. `Optional` es un contenedor que puede o no contener un valor no nulo.

import java.util.Optional;


public class OptionalExample {

    public static void main(String[] args) {

        Optional<String> optional = Optional.of("Hello, World!");

        

        // Verificar si hay un valor presente

        if (optional.isPresent()) {

            System.out.println(optional.get());

        }

        

        // Uso del método ifPresent

        optional.ifPresent(System.out::println);

        

        // Proveer un valor predeterminado

        String value = optional.orElse("Default Value");

        System.out.println(value);

        

        // Proveer un valor predeterminado usando un Supplier

        value = optional.orElseGet(() -> "Default Value from Supplier");

        System.out.println(value);

    }

}


Scala utiliza la clase `Option` para representar un valor opcional. `Option` tiene dos subclases: `Some` y `None`, lo que proporciona una forma elegante y funcional de manejar valores que pueden estar ausentes. Esta idea es similar a la monada `Maybe` en Haskell.


object OptionExample extends App {

  val someValue: Option[String] = Some("Hello, World!")

  val noneValue: Option[String] = None


  // Uso de getOrElse

  println(someValue.getOrElse("Default Value"))

  println(noneValue.getOrElse("Default Value"))


  // Uso del patrón de coincidencia (Pattern Matching)

  someValue match {

    case Some(value) => println(value)

    case None => println("No value")

  }


  noneValue match {

    case Some(value) => println(value)

    case None => println("No value")

  }

}


Scala "copio" esta forma de Haskell. Haskell utiliza el tipo de datos `Maybe` para manejar valores opcionales `Maybe` puede ser `Just` un valor o `Nothing`.


main :: IO ()

main = do

    let someValue = Just "Hello, World!"

    let noneValue = Nothing


    -- Uso de fromMaybe

    putStrLn (fromMaybe "Default Value" someValue)

    putStrLn (fromMaybe "Default Value" noneValue)


    -- Uso del patrón de coincidencia (Pattern Matching)

    case someValue of

        Just value -> putStrLn value

        Nothing -> putStrLn "No value"


    case noneValue of

        Just value -> putStrLn value

        Nothing -> putStrLn "No value"


Kotlin es similar a Scala en muchos aspectos pero no en este. Kotlin introduce el operador `?` para facilitar la gestión de valores nulos. Este operador se utiliza para declarar tipos de datos que pueden ser nulos y para realizar operaciones seguras contra nulos.


fun main() {

    var nullableString: String? = "Hello, World!"


    // Uso del operador ?. para llamadas seguras

    println(nullableString?.length)


    // Uso del operador ?: para proporcionar un valor predeterminado

    val length = nullableString?.length ?: 0

    println(length)


    nullableString = null


    // Uso de let para ejecutar código solo si el valor no es nulo

    nullableString?.let {

        println(it)

    }

}


C# ha incluido varias características para manejar valores nulos, como el operador `?`, que facilita el manejo seguro de tipos que pueden ser nulos.


using System;


class Program

{

    static void Main()

    {

        string? nullableString = "Hello, World!";

        

        // Uso del operador ?. para llamadas seguras

        Console.WriteLine(nullableString?.Length);


        // Uso del operador ?? para proporcionar un valor predeterminado

        int length = nullableString?.Length ?? 0;

        Console.WriteLine(length);


        nullableString = null;


        // Uso de pattern matching para verificar nulos

        if (nullableString is string nonNullString)

        {

            Console.WriteLine(nonNullString);

        }

    }

}


Rust maneja la ausencia de valores y los errores de una manera robusta utilizando los tipos `Option` y `Result`. `Option` puede ser `Some` o `None`, mientras que `Result` puede ser `Ok` o `Err`.


fn main() {

    let some_value: Option<String> = Some("Hello, World!".to_string());

    let none_value: Option<String> = None;


    // Uso de unwrap_or

    println!("{}", some_value.unwrap_or("Default Value".to_string()));

    println!("{}", none_value.unwrap_or("Default Value".to_string()));


    // Uso del patrón de coincidencia (Pattern Matching)

    match some_value {

        Some(value) => println!("{}", value),

        None => println!("No value"),

    }


    match none_value {

        Some(value) => println!("{}", value),

        None => println!("No value"),

    }

}


Go no tiene un tipo de datos específico para manejar valores opcionales, pero utiliza la convención de retornar múltiples valores, incluyendo un valor y un `error`. Que la verdad no me gusta, te pasas preguntando todo el tiempo si hay error o si los valores son nulos. 


package main


import (

    "errors"

    "fmt"

)


func getValue() (string, error) {

    return "Hello, World!", nil

}


func getNullableValue() (string, error) {

    return "", errors.New("no value")

}


func main() {

    value, err := getValue()

    if err != nil {

        fmt.Println("Error:", err)

    } else {

        fmt.Println("Value:", value)

    }


    nullableValue, err := getNullableValue()

    if err != nil {

        fmt.Println("Error:", err)

    } else {

        fmt.Println("Value:", nullableValue)

    }

}


Python utiliza la palabra clave `None` para representar la ausencia de valor. Aunque no tiene una estructura específica como `Optional`, los desarrolladores pueden utilizar condicionales y manejo de excepciones.


def get_value():

    return "Hello, World!"


def get_nullable_value():

    return None


value = get_value()

nullable_value = get_nullable_value()


if value is not None:

    print(value)

else:

    print("Default Value")


if nullable_value is not None:

    print(nullable_value)

else:

    print("Default Value")


Ruby utiliza `nil` para representar la ausencia de valor. Al igual que en Python, no tiene una estructura específica para valores opcionales, pero proporciona métodos para manejar `nil`.


value = "Hello, World!"

nullable_value = nil


# Uso del operador ||

puts value || "Default Value"

puts nullable_value || "Default Value"


# Uso de condicionales

puts value.nil? ? "Default Value" : value

puts nullable_value.nil? ? "Default Value" : nullable_value


C++ utiliza punteros inteligentes (`smart pointers`) para gestionar la memoria y prevenir errores relacionados con punteros nulos. Los punteros inteligentes, como `std::unique_ptr` y `std::shared_ptr`, se encargan de la gestión automática de la memoria.


#include <iostream>

#include <memory>


int main() {

    std::unique_ptr<int> uniquePtr(new int(42));

    if (uniquePtr) {

        std::cout << *uniquePtr << std::endl;

    }


    std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);

    if (sharedPtr) {

        std::cout << *sharedPtr << std::endl;

    }


    // Uso de weak_ptr para evitar ciclos de referencia

    std::weak_ptr<int> weakPtr = sharedPtr;

    if (auto lockedPtr = weakPtr.lock()) {

        std::cout << *lockedPtr << std::endl;

    }


    return 0;

}


TypeScript, un superconjunto de JavaScript, permite tipos opcionales y tiene un soporte robusto para manejar valores `null` y `undefined`.


let nullableString: string | null = "Hello, World!";


// Uso del operador ? para llamadas seguras

console.log(nullableString?.length ?? 0);


// Uso de if para asegurar valores no nulos

if (nullableString !== null) {

    console.log(nullableString);

}


TypeScript utiliza tipos opcionales para manejar valores que pueden ser `null` o `undefined`, proporcionando un enfoque seguro para evitar errores comunes relacionados con valores nulos. El operador `?.` permite realizar llamadas seguras, y el operador `??` proporciona valores predeterminados en caso de valores `null` o `undefined`.

En fin, aunque la gestión de valores nulos varía entre lenguajes, la idea subyacente es la misma: proporcionar mecanismos más seguros y expresivos para manejar la ausencia de valores. Ya sea mediante clases contenedoras como `Optional` en Java y `Option` en Scala, tipos de datos como `Maybe` en Haskell, operadores específicos como `?` en Kotlin y C#, punteros inteligentes en C++, o enfoques específicos en Rust, Go, Python y Ruby, estos enfoques ayudan a reducir los errores y a escribir un código más robusto y mantenible.


sábado, 28 de enero de 2023

Node + Typescript, configuraciones y scripts útiles


Si ya configuraste el entorno como un campeon, veamos unos trucos para que todo vaya como piña. 

Recarga en frío: La recarga en frío es buena para el desarrollo local. Para hacer esto, tendremos que instalar y configurar en un par de paquetes más: ts-node para ejecutar el código de mecanografiado directamente sin tener que esperar a que se compilara, y nodemon, para observar los cambios en nuestro código y reiniciar automáticamente cuando un el archivo ha cambiado.


npm install --save-dev ts-node nodemon


Ahora debemos agregar una configuración de nodemon.json.


{

  "watch": ["src"],

  "ext": ".ts,.js",

  "ignore": [],

  "exec": "npx ts-node ./src/index.ts"

}


Y luego, para ejecutar el proyecto, todo lo que tenemos que hacer es ejecutar nodemon. Agreguemos un script para eso.


"start:dev": "npx nodemon",


Al ejecutar npm run start:dev, npx nodemon iniciará nuestra aplicación usando npx ts-node ./src/index.ts, buscando cambios en los archivos .ts y .js desde /src.


Para limpiar y compilar el proyecto para la producción, podemos agregar un script de compilación.

Instalamos rimraf, una herramienta multiplataforma que actúa como el comando rm -rf (simplemente borra todo lo que le indique).


npm install --save-dev rimraf


Y luego, agregamos esto en paquete.json.


"build": "rimraf ./build && tsc",


Ahora, cuando ejecutamos npm run build, rimraf eliminará nuestra carpeta de compilación anterior antes de que el compilador de TypeScript emita código nuevo para dist.


Para iniciar la aplicación en producción, todo lo que debemos hacer es ejecutar primero el comando de compilación y luego ejecutar el JavaScript compilado en build/index.js. El script de inicio se ve así.

"start": "npm run build && node build/index.js"


Y listo. 



martes, 24 de enero de 2023

Como crear un proyecto en Node + Typescript?


Empecemos, vamos a una cosola y creamos una carpeta : 

mkdir typescript-starter

cd typescript-starter


Ahora tenemos que crear el package.json

npm init -y


Luego agregamos Typescript como una dependencia de dev : 

npm install typescript --save-dev


Luego de instalar esto podemos usar el comando tsc. Además TypeScript tiene tipos implícitos, explícitos y ambientales. Los tipos ambientales son tipos que se agregan al ámbito de ejecución global. Dado que estamos usando Node, sería bueno si pudiéramos obtener seguridad de tipo y finalización automática en las API de Node como archivo, ruta, proceso, etc. Eso es lo que hará la instalación de la definición de tipo DefinitelyTyped para Node:

npm install @types/node --save-dev


tsconfig.json es el archivo donde typescript tiene todas sus definiciones y lo creamos con este comando: 

npx tsc --init --rootDir src --outDir build \

--esModuleInterop --resolveJsonModule --lib es6 \

--module commonjs --allowJs true --noImplicitAny true


  • rootDir: Aquí es donde TypeScript busca nuestro código. Lo hemos configurado para buscar en la carpeta src/. Ahí es donde escribiremos nuestro TypeScript.
  • outDir: Donde TypeScript pone nuestro código compilado. Queremos que vaya a una carpeta build/.
  • esModuleInterop: si estuvimos en el espacio de JavaScript durante los últimos años, es posible que haya reconocido que los sistemas de módulos se han descontrolado un poco (AMD, SystemJS, ES Modules, etc.). Para un tema que requiere una discusión mucho más larga, si usamos commonjs como nuestro sistema de módulos (para las aplicaciones de Node, debería hacerlo), entonces necesitamos que esto se establezca en verdadero.
  • resolveJsonModule: si usamos JSON en este proyecto, esta opción permite que TypeScript lo use.
  • lib: esta opción agrega tipos ambientales a nuestro proyecto, lo que nos permite confiar en funciones de diferentes versiones de Ecmascript, bibliotecas de prueba e incluso la API DOM del navegador. 
  • módulo: commonjs es el sistema de módulo de Nodo estándar en 2019. Usemos eso.
  • allowJs: si está convirtiendo un proyecto antiguo de JavaScript a TypeScript, esta opción le permitirá incluir archivos .js entre los .ts.
  • noImplicitAny: en los archivos TypeScript, no permita que se especifique un tipo de forma no explícita. Cada tipo debe tener un tipo específico o declararse explícitamente cualquiera. Sin anys implícitos.

Luego de ejecutar este comando, tenemos un archivo tsconfig.json algo así: 

{

  "compilerOptions": {

    /* Visit https://aka.ms/tsconfig to read more about this file */


    /* Projects */

    // "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */

    // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */

    // "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */

    // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */

    // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */

    // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */


    /* Language and Environment */

    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */

    "lib": ["es6"],                                      /* Specify a set of bundled library declaration files that describe the target runtime environment. */

    // "jsx": "preserve",                                /* Specify what JSX code is generated. */

    // "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */

    // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */

    // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */

    // "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */

    // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */

    // "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */

    // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */

    // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */

    // "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */


    /* Modules */

    "module": "commonjs",                                /* Specify what module code is generated. */

    "rootDir": "src",                                    /* Specify the root folder within your source files. */

    // "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */

    // "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */

    // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */

    // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */

    // "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */

    // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */

    // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */

    // "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */

    "resolveJsonModule": true,                           /* Enable importing .json files. */

    // "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */


    /* JavaScript Support */

    "allowJs": true,                                     /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */

    // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */

    // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */


    /* Emit */

    // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */

    // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */

    // "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */

    // "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */

    // "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */

    "outDir": "build",                                   /* Specify an output folder for all emitted files. */

    // "removeComments": true,                           /* Disable emitting comments. */

    // "noEmit": true,                                   /* Disable emitting files from a compilation. */

    // "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */

    // "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */

    // "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */

    // "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */

    // "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */

    // "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */

    // "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */

    // "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */

    // "newLine": "crlf",                                /* Set the newline character for emitting files. */

    // "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */

    // "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */

    // "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */

    // "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */

    // "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */

    // "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */


    /* Interop Constraints */

    // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */

    // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */

    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */

    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */

    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */


    /* Type Checking */

    "strict": true,                                      /* Enable all strict type-checking options. */

    "noImplicitAny": true,                               /* Enable error reporting for expressions and declarations with an implied 'any' type. */

    // "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */

    // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */

    // "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */

    // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */

    // "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */

    // "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */

    // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */

    // "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */

    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */

    // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */

    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */

    // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */

    // "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */

    // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */

    // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */

    // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */

    // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */


    /* Completeness */

    // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */

    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */

  }

}

Regrande y con muchas opciones, pero si borramos los comentarios queda así : 

{

  "compilerOptions": {

    "target": "es5",                          

    "module": "commonjs",                    

    "lib": ["es6"],                     

    "allowJs": true,

    "outDir": "build",                          

    "rootDir": "src",

    "strict": true,         

    "noImplicitAny": true,

    "esModuleInterop": true,

    "resolveJsonModule": true

  }

}


Ahora vamos a probar si anda, para eso creamos la carpeta src y un archivo de prueba: 


mkdir src

touch src/index.ts


Y ponemos algo de código, para eso abrimos el archivo y escribimos: 

console.log('Hello world!')


Y ahora compilamos : 

npx tsc


Luego de compilar vamos a tener una carpeta llamada build que va contener un archivo index.js que contiene : 


"use strict";

console.log('Hello world!');


Y listo!! 



sábado, 25 de junio de 2022

Resultado de la encuesta 2022 de StackOverflow

StackOverflow hace una encuesta cada año a los desarrolladores y es un buen termómetro para ver como va evolucionando la industria: 

Cosas a destacar o que me llamaron la atención:

Lenguajes de programación más usados: Javascript, Python, Typescript y Java. Acá me llamo la atención que Typescript le gane a java. 


Base de datos: Mysql, postgresql, SQLite, mongodb. SQLite le gana a mongo, eso es raro.


Más amados y más odiados aparezca Delphi


Y por último entre los lenguajes mejor pagos, hay 6 lenguajes funcionales (no me llamo la atención pero lo quiero marcar) :  

Y te llamo la atención algo más? 

Dejo link: 

https://survey.stackoverflow.co/2022/?utm_source=so-owned&utm_medium=announcement-banner&utm_campaign=dev-survey-2022&utm_content=results

sábado, 12 de febrero de 2022

Typescript handbook


Si empezas con typescript un libro super recomendado es el handbook que se puede acceder de forma gratuita. 

En la introducción nos da una perspectiva del libro para diferentes desarrolladores:

También se puede bajar como Pdf : https://www.typescriptlang.org/assets/typescript-handbook.pdf

Dejo link: https://www.typescriptlang.org/docs/handbook/intro.html

sábado, 5 de diciembre de 2020

Top 10 de los lenguajes de programación más populares en GitHub

 


Github liberó el resultado de una métrica que indica cual es el lenguaje más utilizado en su repositorio. Y el ganador por tercer año consecutivo es Javascript. 

El que sorprendió fue typescript, recuperándose de un 2019 no tan bondadoso. 

Y listo, no hay otras novedades...

sábado, 22 de agosto de 2020

Se liberó TypeScript 4.0


Bueno una pequeña noticia y que los cambios en Typescript 4.0 no son tan importantes según lo dicho por el amigo Daniel en su post : 

"Con TypeScript 4.0, no hay grandes cambios importantes. De hecho, si es nuevo en el idioma, ahora es el mejor momento para comenzar a usarlo. La comunidad ya está aquí y está creciendo, con un código de trabajo y excelentes recursos nuevos para aprender. Y una cosa a tener en cuenta: a pesar de todas las cosas buenas que traemos en 4.0, ¡realmente solo necesita conocer los conceptos básicos de TypeScript para ser productivo!"

Sin más dejo link de la noticia: https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/?fbclid=IwAR0aL7qtBCxgg7CAUYnq2gQInc8yzpepkJUP0IwHk2l_-Vj0zF4EmKLO0dQ


sábado, 27 de junio de 2020

Unión de tipos en Typescript


La unión de tipos están estrechamente relacionados con la intersección de tipos, pero se usan de manera muy diferente. Ocasionalmente,nos encontraremos con una librería que espera que un parámetro sea un número o una cadena. Por ejemplo, la siguiente función:

/**
 * Takes a string and adds "padding" to the left.
 * If 'padding' is a string, then 'padding' is appended to the left side.
 * If 'padding' is a number, then that number of spaces is added to the left side.
 */
function padLeft(value: string, padding: any) {
    if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}

padLeft("Hello world", 4); // returns "    Hello world"

Al tipar padding como any no estamos utilizando las ventajas del lenguaje de tipado estático y corremos el riesgo que se lance el error dado que han utilizado dicha función con otro tipo. 

En lugar de any, podemos usar un tipo de unión para el parámetro:

/**
 * Takes a string and adds "padding" to the left.
 * If 'padding' is a string, then 'padding' is appended to the left side.
 * If 'padding' is a number, then that number of spaces is added to the left side.
 */
function padLeft(value: string, padding: string | number) {
    // ...
}

let indentedString = padLeft("Hello world", true); // esto no compila

Un tipo de unión describe un valor que puede ser uno de varios tipos. Usamos la barra vertical (|) para separar cada tipo, por lo tanto string | number es el tipo de valor y que puede ser un número o una cadena.

Si tenemos un valor que tiene un tipo de unión, solo podemos acceder a los miembros que son comunes a todos los tipos de la unión.

La diferencia con la intersección es que este parámetro puede ser uno o el otro tipo. Pero en la intersección debe ser los dos, es un y. 

Dejo link : 
https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types

jueves, 25 de junio de 2020

Intersección de tipos en Typescript

Poco he hablado de Typescript, la verdad que fruto de mi ignorancia, no me llamo la atención este lenguaje y me parecia que aportaba poco o nada al mundo de los lenguajes. Y me equivoque feo.

Typescript tiene un conjunto de características por demás interesantes, que voy a ver en este post y en una serie de post que voy a escribir (si no me aburro) 

Ahora vamos a hablar de Intersección de tipos en Typescript.

Un tipo de intersección combina múltiples tipos en uno. Esto le permite agregar tipos existentes para obtener un solo tipo que tenga todas las características que necesita. Por ejemplo, Person & Serializable & Loggable es Person, Serializable y Loggable. Eso significa que un objeto de este tipo tendrá todos los miembros de los tres tipos.

Los tipos de intersección son utilizados mayormente para mixins y otros conceptos que no encajan en el molde clásico orientado a objetos. (¡Hay muchos de estos en JavaScript!) Aquí hay un ejemplo simple que muestra cómo crear un mixin:

function extend<First, Second>(first: First, second: Second): First & Second {
    const result: Partial<First & Second> = {};
    for (const prop in first) {
        if (first.hasOwnProperty(prop)) {
            (result as First)[prop] = first[prop];
        }
    }
    for (const prop in second) {
        if (second.hasOwnProperty(prop)) {
            (result as Second)[prop] = second[prop];
        }
    }
    return result as First & Second;
}

class Person {
    constructor(public name: string) { }
}

interface Loggable {
    log(name: string): void;
}

class ConsoleLogger implements Loggable {
    log(name) {
        console.log(`Hello, I'm ${name}.`);
    }
}

const jim = extend(new Person('Jim'), ConsoleLogger.prototype);
jim.log(jim.name);

Puff, super potente!!  

jueves, 25 de octubre de 2018

The State of the Octoverse



Github publico el informe anual Octoverse, en el cual se pueden ver datos interesantes de los proyecto. El informe es grande y tiene muchos detalles.

En especial me intereso los lenguajes más utilizados:


No tengo mucho análisis para hacer javascript viene primero y es indiscutido. Y como lenguajes con mayor crecimiento podemos ver como impacto la decisión de Android de dar soporte a Kotlin :



Me llamo la atención no ver a Scala, pero bueno... dejen su opinión.

Dejo link: https://octoverse.github.com/

jueves, 15 de febrero de 2018

Top de lenguajes que tenes que aprender en el 2018



Me encontre un post sobre los lenguajes que debes aprender este 2018, si no lo sabes. Y en resumen los lenguajes son los siguientes :

  1. JavaScript
  2. TypeScript (si bien no es un lenguaje, es un framework que suma características a un lenguaje, por lo tanto es valido)
  3. Python
  4. Java
  5. Kotlin
  6. C & C++
  7. PHP
  8. Swift
  9. Scala
Lindo ranking veo algunos lenguajes viejos, pero salvo algunos puntos, estoy de acuerdo. 



lunes, 16 de octubre de 2017

Los 15 lenguajes más utilizados en github

Leí un articulo sobre los 15 lenguajes más usados en github y no me lleve ninguna sorpresa, el primero es javascript. Sin duda el lenguaje más de moda en la comunidad open source.

Veamos todos:

RankLanguagePull Requests
1JavaScript2.3M
2Python1M
3Java986K
4Ruby870K
5PHP559K
6C++413K
7CSS335K
8C#326K
9Go285K
10C239K
11TypeScript207K
12Shell206K
13Swift107K
14Scala99K
15Objective C66K
Como dice el articulo: "De la lista anterior, podemos inferir que JavaScript es el lenguaje más utilizado entre los desarrolladores de todo el mundo, seguido por Python. GitHub mencionó que Python reemplazó a Java como el segundo idioma más popular en su plataforma, con un 40 por ciento más de solicitudes de extracción abiertas este año que el pasado."

GitHub también ha compartido algunos puntos de vista sobre la mayoría de las organizaciones y proyectos activos por la cantidad de colaboradores únicos, forks y comentarios. Y aquí están los datos sobre ellos.





Un vistazo a los datos anuales de GitHub:

  • 24 millones de usuarios totales
  • 1,5 millones de organizaciones
  • 67 millones de depósitos totales
  • 1 mil millones de compromisos públicos desde septiembre de 2016
  • 25,3 millones de depósitos activos desde septiembre de 2016
  • 1,3 millones de nuevas solicitudes desde septiembre de 2016


Dejo el articulo: https://curiouspost.com/list-15-popular-programming-languages-used-coders-github/