Skip to content

Instantly share code, notes, and snippets.

@Jviejo
Created July 27, 2025 08:37
Show Gist options
  • Save Jviejo/c2f89aba2034199114312253062cbd02 to your computer and use it in GitHub Desktop.
Save Jviejo/c2f89aba2034199114312253062cbd02 to your computer and use it in GitHub Desktop.
Guia Typescript vs Dart by Cloude

Guía Completa: Dart vs TypeScript

Características, Estructuras de Datos y Comparaciones


1. Introducción y Sintaxis Básica

Dart

void main() {
  String nombre = "Flutter";
  int edad = 7;
  double version = 3.0;
  bool esModerno = true;
  
  print('$nombre tiene $edad años y versión $version');
}

TypeScript

function main(): void {
  const nombre: string = "Angular";
  const edad: number = 14;
  const version: number = 17.0;
  const esModerno: boolean = true;
  
  console.log(`${nombre} tiene ${edad} años y versión ${version}`);
}

Diferencias clave:

  • Dart usa void main() como punto de entrada
  • TypeScript requiere configuración adicional para ejecutar
  • Dart tiene interpolación de strings con $variable
  • TypeScript usa template literals con ${variable}

2. Estructuras de Datos

2.1 Listas/Arrays

Dart

void ejemploListas() {
  // Lista dinámica
  List<int> numeros = [1, 2, 3, 4, 5];
  List<String> frutas = ['manzana', 'banana', 'naranja'];
  
  // Lista fija
  List<int> numerosFijos = List.filled(3, 0);
  
  // Operaciones
  numeros.add(6);
  numeros.insert(0, 0);
  numeros.removeAt(1);
  
  // Métodos funcionales
  var pares = numeros.where((n) => n % 2 == 0).toList();
  var duplicados = numeros.map((n) => n * 2).toList();
  
  print('Números: $numeros');
  print('Pares: $pares');
}

TypeScript

function ejemploArrays(): void {
  // Arrays dinámicos
  const numeros: number[] = [1, 2, 3, 4, 5];
  const frutas: string[] = ['manzana', 'banana', 'naranja'];
  
  // Array con longitud fija (simulado)
  const numerosFijos: number[] = new Array(3).fill(0);
  
  // Operaciones
  numeros.push(6);
  numeros.unshift(0);
  numeros.splice(1, 1);
  
  // Métodos funcionales
  const pares = numeros.filter(n => n % 2 === 0);
  const duplicados = numeros.map(n => n * 2);
  
  console.log('Números:', numeros);
  console.log('Pares:', pares);
}

2.2 Mapas/Objetos

Dart

void ejemploMapas() {
  // Map tipado
  Map<String, int> edades = {
    'Ana': 25,
    'Luis': 30,
    'María': 28
  };
  
  // Operaciones
  edades['Carlos'] = 35;
  edades.remove('Luis');
  
  // Iteración
  edades.forEach((nombre, edad) {
    print('$nombre tiene $edad años');
  });
  
  // Métodos útiles
  var nombres = edades.keys.toList();
  var soloEdades = edades.values.toList();
  var adultos = edades.entries
      .where((entry) => entry.value >= 30)
      .map((entry) => entry.key)
      .toList();
}

TypeScript

function ejemploObjetos(): void {
  // Objeto/Map tipado
  const edades: Record<string, number> = {
    'Ana': 25,
    'Luis': 30,
    'María': 28
  };
  
  // Usando Map nativo
  const edadesMap = new Map<string, number>([
    ['Ana', 25],
    ['Luis', 30],
    ['María', 28]
  ]);
  
  // Operaciones
  edades['Carlos'] = 35;
  delete edades['Luis'];
  
  // Iteración
  Object.entries(edades).forEach(([nombre, edad]) => {
    console.log(`${nombre} tiene ${edad} años`);
  });
  
  // Métodos útiles
  const nombres = Object.keys(edades);
  const soloEdades = Object.values(edades);
  const adultos = Object.entries(edades)
    .filter(([, edad]) => edad >= 30)
    .map(([nombre]) => nombre);
}

2.3 Sets (Conjuntos)

Dart

void ejemploSets() {
  Set<String> colores = {'rojo', 'verde', 'azul'};
  Set<int> numeros = {1, 2, 3, 3, 4}; // Automáticamente elimina duplicados
  
  // Operaciones
  colores.add('amarillo');
  colores.addAll(['violeta', 'naranja']);
  
  // Operaciones de conjuntos
  Set<int> otrosNumeros = {3, 4, 5, 6};
  var union = numeros.union(otrosNumeros);
  var interseccion = numeros.intersection(otrosNumeros);
  var diferencia = numeros.difference(otrosNumeros);
  
  print('Unión: $union');
  print('Intersección: $interseccion');
}

TypeScript

function ejemploSets(): void {
  const colores = new Set<string>(['rojo', 'verde', 'azul']);
  const numeros = new Set<number>([1, 2, 3, 3, 4]); // Elimina duplicados
  
  // Operaciones
  colores.add('amarillo');
  ['violeta', 'naranja'].forEach(color => colores.add(color));
  
  // Operaciones de conjuntos
  const otrosNumeros = new Set<number>([3, 4, 5, 6]);
  const union = new Set([...numeros, ...otrosNumeros]);
  const interseccion = new Set([...numeros].filter(x => otrosNumeros.has(x)));
  const diferencia = new Set([...numeros].filter(x => !otrosNumeros.has(x)));
  
  console.log('Unión:', union);
  console.log('Intersección:', interseccion);
}

3. Serialización

3.1 JSON

Dart

import 'dart:convert';

class Persona {
  String nombre;
  int edad;
  List<String> hobbies;
  
  Persona({required this.nombre, required this.edad, required this.hobbies});
  
  // Serialización a JSON
  Map<String, dynamic> toJson() {
    return {
      'nombre': nombre,
      'edad': edad,
      'hobbies': hobbies,
    };
  }
  
  // Deserialización desde JSON
  factory Persona.fromJson(Map<String, dynamic> json) {
    return Persona(
      nombre: json['nombre'] as String,
      edad: json['edad'] as int,
      hobbies: List<String>.from(json['hobbies']),
    );
  }
}

void ejemploSerializacion() {
  var persona = Persona(
    nombre: 'Ana',
    edad: 25,
    hobbies: ['lectura', 'programación']
  );
  
  // A JSON
  String jsonString = jsonEncode(persona.toJson());
  print('JSON: $jsonString');
  
  // Desde JSON
  Map<String, dynamic> jsonData = jsonDecode(jsonString);
  var personaDeserializada = Persona.fromJson(jsonData);
  print('Nombre: ${personaDeserializada.nombre}');
}

TypeScript

class Persona {
  constructor(
    public nombre: string,
    public edad: number,
    public hobbies: string[]
  ) {}
  
  // Serialización a JSON (automática en TypeScript)
  toJSON(): object {
    return {
      nombre: this.nombre,
      edad: this.edad,
      hobbies: this.hobbies
    };
  }
  
  // Método estático para deserialización
  static fromJSON(json: any): Persona {
    return new Persona(
      json.nombre,
      json.edad,
      json.hobbies
    );
  }
}

function ejemploSerializacion(): void {
  const persona = new Persona('Ana', 25, ['lectura', 'programación']);
  
  // A JSON
  const jsonString = JSON.stringify(persona);
  console.log('JSON:', jsonString);
  
  // Desde JSON
  const jsonData = JSON.parse(jsonString);
  const personaDeserializada = Persona.fromJSON(jsonData);
  console.log('Nombre:', personaDeserializada.nombre);
}

4. Manejo de Ficheros

Dart

import 'dart:io';
import 'dart:convert';

Future<void> ejemploFicheros() async {
  final archivo = File('datos.txt');
  
  try {
    // Escribir archivo
    await archivo.writeAsString('Hola desde Dart\nSegunda línea');
    
    // Leer archivo completo
    String contenido = await archivo.readAsString();
    print('Contenido: $contenido');
    
    // Leer línea por línea
    List<String> lineas = await archivo.readAsLines();
    for (var i = 0; i < lineas.length; i++) {
      print('Línea $i: ${lineas[i]}');
    }
    
    // Trabajar con JSON
    var datos = {'nombre': 'Flutter', 'version': 3.0};
    var archivoJson = File('datos.json');
    await archivoJson.writeAsString(jsonEncode(datos));
    
    // Leer JSON
    String jsonContent = await archivoJson.readAsString();
    Map<String, dynamic> datosLeidos = jsonDecode(jsonContent);
    print('Datos JSON: $datosLeidos');
    
  } catch (e) {
    print('Error al manejar archivos: $e');
  }
}

TypeScript (Node.js)

import { promises as fs } from 'fs';
import { readFileSync, writeFileSync } from 'fs';

async function ejemploFicheros(): Promise<void> {
  const nombreArchivo = 'datos.txt';
  
  try {
    // Escribir archivo
    await fs.writeFile(nombreArchivo, 'Hola desde TypeScript\nSegunda línea');
    
    // Leer archivo completo
    const contenido = await fs.readFile(nombreArchivo, 'utf8');
    console.log('Contenido:', contenido);
    
    // Leer línea por línea
    const lineas = contenido.split('\n');
    lineas.forEach((linea, i) => {
      console.log(`Línea ${i}: ${linea}`);
    });
    
    // Trabajar con JSON
    const datos = { nombre: 'Node.js', version: 18.0 };
    await fs.writeFile('datos.json', JSON.stringify(datos, null, 2));
    
    // Leer JSON
    const jsonContent = await fs.readFile('datos.json', 'utf8');
    const datosLeidos = JSON.parse(jsonContent);
    console.log('Datos JSON:', datosLeidos);
    
  } catch (error) {
    console.error('Error al manejar archivos:', error);
  }
}

5. Futuros y Programación Asíncrona

5.1 Futures/Promises básicos

Dart

import 'dart:async';
import 'dart:math';

Future<String> operacionAsincrona() async {
  await Future.delayed(Duration(seconds: 2));
  return 'Operación completada';
}

Future<int> calcularFactorial(int n) async {
  if (n <= 1) return 1;
  await Future.delayed(Duration(milliseconds: 100));
  return n * await calcularFactorial(n - 1);
}

void ejemploFutures() async {
  print('Iniciando operaciones...');
  
  try {
    // Future simple
    String resultado = await operacionAsincrona();
    print(resultado);
    
    // Future con transformación
    var futureNumero = Future.value(42);
    var numeroTransformado = await futureNumero.then((valor) => valor * 2);
    print('Número transformado: $numeroTransformado');
    
    // Múltiples futures en paralelo
    var futures = [
      calcularFactorial(5),
      calcularFactorial(4),
      calcularFactorial(3)
    ];
    
    List<int> resultados = await Future.wait(futures);
    print('Factoriales: $resultados');
    
  } catch (e) {
    print('Error: $e');
  }
}

TypeScript

function operacionAsincrona(): Promise<string> {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('Operación completada');
    }, 2000);
  });
}

async function calcularFactorial(n: number): Promise<number> {
  if (n <= 1) return 1;
  await new Promise(resolve => setTimeout(resolve, 100));
  return n * await calcularFactorial(n - 1);
}

async function ejemploPromises(): Promise<void> {
  console.log('Iniciando operaciones...');
  
  try {
    // Promise simple
    const resultado = await operacionAsincrona();
    console.log(resultado);
    
    // Promise con transformación
    const promiseNumero = Promise.resolve(42);
    const numeroTransformado = await promiseNumero.then(valor => valor * 2);
    console.log('Número transformado:', numeroTransformado);
    
    // Múltiples promises en paralelo
    const promises = [
      calcularFactorial(5),
      calcularFactorial(4),
      calcularFactorial(3)
    ];
    
    const resultados = await Promise.all(promises);
    console.log('Factoriales:', resultados);
    
  } catch (error) {
    console.error('Error:', error);
  }
}

5.2 Streams

Dart

import 'dart:async';

Stream<int> generarNumeros() async* {
  for (int i = 1; i <= 5; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

Stream<String> procesarStream(Stream<int> input) async* {
  await for (int numero in input) {
    yield 'Procesado: $numero';
  }
}

void ejemploStreams() async {
  // Stream básico
  var streamNumeros = generarNumeros();
  
  await for (int numero in streamNumeros) {
    print('Recibido: $numero');
  }
  
  // Stream con transformaciones
  var streamProcesado = procesarStream(generarNumeros());
  
  streamProcesado.listen(
    (dato) => print(dato),
    onError: (error) => print('Error: $error'),
    onDone: () => print('Stream completado'),
  );
}

TypeScript (con RxJS-like pattern)

class SimpleStream<T> {
  private listeners: ((value: T) => void)[] = [];
  
  subscribe(callback: (value: T) => void): void {
    this.listeners.push(callback);
  }
  
  emit(value: T): void {
    this.listeners.forEach(callback => callback(value));
  }
}

async function* generarNumeros(): AsyncGenerator<number> {
  for (let i = 1; i <= 5; i++) {
    await new Promise(resolve => setTimeout(resolve, 1000));
    yield i;
  }
}

async function ejemploStreams(): Promise<void> {
  // Usando generators
  console.log('Iniciando stream...');
  
  for await (const numero of generarNumeros()) {
    console.log('Recibido:', numero);
  }
  
  // Simulando stream con eventos
  const stream = new SimpleStream<string>();
  
  stream.subscribe((dato) => console.log('Stream:', dato));
  
  setTimeout(() => stream.emit('Primer mensaje'), 1000);
  setTimeout(() => stream.emit('Segundo mensaje'), 2000);
}

6. Control de Errores

Dart

// Excepciones personalizadas
class ErrorPersonalizado implements Exception {
  final String mensaje;
  ErrorPersonalizado(this.mensaje);
  
  @override
  String toString() => 'ErrorPersonalizado: $mensaje';
}

Future<double> dividir(double a, double b) async {
  if (b == 0) {
    throw ErrorPersonalizado('División por cero no permitida');
  }
  return a / b;
}

void ejemploControlErrores() async {
  // Try-catch básico
  try {
    double resultado = await dividir(10, 2);
    print('Resultado: $resultado');
    
    // Esto lanzará una excepción
    await dividir(10, 0);
    
  } on ErrorPersonalizado catch (e) {
    print('Error personalizado capturado: $e');
  } catch (e, stackTrace) {
    print('Error general: $e');
    print('Stack trace: $stackTrace');
  } finally {
    print('Bloque finally ejecutado');
  }
  
  // Manejo con Result pattern
  var resultado = await ejecutarSinExcepciones();
  if (resultado.isSuccess) {
    print('Éxito: ${resultado.value}');
  } else {
    print('Error: ${resultado.error}');
  }
}

class Result<T> {
  final T? value;
  final String? error;
  final bool isSuccess;
  
  Result.success(this.value) : error = null, isSuccess = true;
  Result.failure(this.error) : value = null, isSuccess = false;
}

Future<Result<String>> ejecutarSinExcepciones() async {
  try {
    await Future.delayed(Duration(milliseconds: 100));
    return Result.success('Operación exitosa');
  } catch (e) {
    return Result.failure(e.toString());
  }
}

TypeScript

// Errores personalizados
class ErrorPersonalizado extends Error {
  constructor(mensaje: string) {
    super(mensaje);
    this.name = 'ErrorPersonalizado';
  }
}

async function dividir(a: number, b: number): Promise<number> {
  if (b === 0) {
    throw new ErrorPersonalizado('División por cero no permitida');
  }
  return a / b;
}

async function ejemploControlErrores(): Promise<void> {
  // Try-catch básico
  try {
    const resultado = await dividir(10, 2);
    console.log('Resultado:', resultado);
    
    // Esto lanzará una excepción
    await dividir(10, 0);
    
  } catch (error) {
    if (error instanceof ErrorPersonalizado) {
      console.log('Error personalizado capturado:', error.message);
    } else {
      console.log('Error general:', error);
      console.log('Stack trace:', (error as Error).stack);
    }
  } finally {
    console.log('Bloque finally ejecutado');
  }
  
  // Manejo con Result pattern
  const resultado = await ejecutarSinExcepciones();
  if (resultado.isSuccess) {
    console.log('Éxito:', resultado.value);
  } else {
    console.log('Error:', resultado.error);
  }
}

type Result<T> = {
  value?: T;
  error?: string;
  isSuccess: boolean;
};

async function ejecutarSinExcepciones(): Promise<Result<string>> {
  try {
    await new Promise(resolve => setTimeout(resolve, 100));
    return { value: 'Operación exitosa', isSuccess: true };
  } catch (error) {
    return { error: error instanceof Error ? error.message : String(error), isSuccess: false };
  }
}

7. Orientación a Objetos

7.1 Clases y Herencia

Dart

// Clase abstracta
abstract class Animal {
  String nombre;
  int edad;
  
  Animal(this.nombre, this.edad);
  
  // Método abstracto
  void hacerSonido();
  
  // Método concreto
  void dormir() {
    print('$nombre está durmiendo');
  }
  
  // Getter
  String get info => '$nombre tiene $edad años';
}

// Interfaz (usando abstract class)
abstract class Volador {
  void volar();
}

// Clase concreta con herencia
class Perro extends Animal {
  String raza;
  
  Perro(String nombre, int edad, this.raza) : super(nombre, edad);
  
  @override
  void hacerSonido() {
    print('$nombre ladra: ¡Guau guau!');
  }
  
  void buscarPelota() {
    print('$nombre está buscando la pelota');
  }
}

// Clase con múltiples interfaces
class Pajaro extends Animal implements Volador {
  double envergaduraAlas;
  
  Pajaro(String nombre, int edad, this.envergaduraAlas) : super(nombre, edad);
  
  @override
  void hacerSonido() {
    print('$nombre canta: ¡Pío pío!');
  }
  
  @override
  void volar() {
    print('$nombre vuela con sus alas de ${envergaduraAlas}cm');
  }
}

// Mixins
mixin Nadador {
  void nadar() {
    print('Nadando en el agua');
  }
}

class Pato extends Animal with Nadador implements Volador {
  Pato(String nombre, int edad) : super(nombre, edad);
  
  @override
  void hacerSonido() {
    print('$nombre hace: ¡Cuac cuac!');
  }
  
  @override
  void volar() {
    print('$nombre vuela sobre el agua');
  }
}

void ejemploOOP() {
  var perro = Perro('Rex', 3, 'Labrador');
  var pajaro = Pajaro('Tweety', 1, 15.5);
  var pato = Pato('Donald', 2);
  
  List<Animal> animales = [perro, pajaro, pato];
  
  for (var animal in animales) {
    print(animal.info);
    animal.hacerSonido();
    animal.dormir();
    
    if (animal is Volador) {
      animal.volar();
    }
    
    if (animal is Nadador) {
      animal.nadar();
    }
    
    print('---');
  }
}

TypeScript

// Clase abstracta
abstract class Animal {
  constructor(
    protected nombre: string,
    protected edad: number
  ) {}
  
  // Método abstracto
  abstract hacerSonido(): void;
  
  // Método concreto
  dormir(): void {
    console.log(`${this.nombre} está durmiendo`);
  }
  
  // Getter
  get info(): string {
    return `${this.nombre} tiene ${this.edad} años`;
  }
}

// Interfaz
interface Volador {
  volar(): void;
}

// Clase concreta con herencia
class Perro extends Animal {
  constructor(nombre: string, edad: number, private raza: string) {
    super(nombre, edad);
  }
  
  hacerSonido(): void {
    console.log(`${this.nombre} ladra: ¡Guau guau!`);
  }
  
  buscarPelota(): void {
    console.log(`${this.nombre} está buscando la pelota`);
  }
}

// Clase con interfaz
class Pajaro extends Animal implements Volador {
  constructor(
    nombre: string,
    edad: number,
    private envergaduraAlas: number
  ) {
    super(nombre, edad);
  }
  
  hacerSonido(): void {
    console.log(`${this.nombre} canta: ¡Pío pío!`);
  }
  
  volar(): void {
    console.log(`${this.nombre} vuela con sus alas de ${this.envergaduraAlas}cm`);
  }
}

// Mixins simulation with composition
class MixinNadador {
  nadar(): void {
    console.log('Nadando en el agua');
  }
}

class Pato extends Animal implements Volador {
  private nadadorMixin = new MixinNadador();
  
  constructor(nombre: string, edad: number) {
    super(nombre, edad);
  }
  
  hacerSonido(): void {
    console.log(`${this.nombre} hace: ¡Cuac cuac!`);
  }
  
  volar(): void {
    console.log(`${this.nombre} vuela sobre el agua`);
  }
  
  nadar(): void {
    this.nadadorMixin.nadar();
  }
}

function ejemploOOP(): void {
  const perro = new Perro('Rex', 3, 'Labrador');
  const pajaro = new Pajaro('Tweety', 1, 15.5);
  const pato = new Pato('Donald', 2);
  
  const animales: Animal[] = [perro, pajaro, pato];
  
  animales.forEach(animal => {
    console.log(animal.info);
    animal.hacerSonido();
    animal.dormir();
    
    // Type checking
    if ('volar' in animal) {
      (animal as Volador).volar();
    }
    
    if ('nadar' in animal) {
      (animal as any).nadar();
    }
    
    console.log('---');
  });
}

8. Expresiones Lambda (Funciones Anónimas)

Dart

void ejemploLambdas() {
  // Funciones lambda básicas
  var sumar = (int a, int b) => a + b;
  var multiplicar = (int a, int b) {
    return a * b;
  };
  
  print('Suma: ${sumar(5, 3)}');
  print('Multiplicación: ${multiplicar(4, 6)}');
  
  // Con listas
  List<int> numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  
  // Map
  var cuadrados = numeros.map((n) => n * n).toList();
  var cubos = numeros.map((numero) {
    return numero * numero * numero;
  }).toList();
  
  // Filter
  var pares = numeros.where((n) => n % 2 == 0).toList();
  var mayoresA5 = numeros.where((numero) => numero > 5).toList();
  
  // Reduce
  var suma = numeros.reduce((a, b) => a + b);
  var producto = numeros.fold<int>(1, (prev, element) => prev * element);
  
  // Sort
  List<String> nombres = ['Ana', 'Carlos', 'Beatriz', 'Daniel'];
  nombres.sort((a, b) => a.compareTo(b));
  
  print('Cuadrados: $cuadrados');
  print('Números pares: $pares');
  print('Suma total: $suma');
  print('Nombres ordenados: $nombres');
  
  // Funciones de orden superior
  ejecutarOperacion(10, 5, (a, b) => a + b);
  ejecutarOperacion(10, 5, (a, b) => a - b);
  
  // Closures
  var contador = crearContador();
  print('Contador: ${contador()}'); // 1
  print('Contador: ${contador()}'); // 2
  print('Contador: ${contador()}'); // 3
  
  // Función que retorna función
  var multiplicadorPor3 = crearMultiplicador(3);
  print('5 * 3 = ${multiplicadorPor3(5)}');
}

void ejecutarOperacion(int a, int b, int Function(int, int) operacion) {
  var resultado = operacion(a, b);
  print('Resultado de la operación: $resultado');
}

Function crearContador() {
  int count = 0;
  return () => ++count;
}

Function(int) crearMultiplicador(int factor) {
  return (int numero) => numero * factor;
}

TypeScript

function ejemploLambdas(): void {
  // Funciones lambda básicas
  const sumar = (a: number, b: number) => a + b;
  const multiplicar = (a: number, b: number) => {
    return a * b;
  };
  
  console.log('Suma:', sumar(5, 3));
  console.log('Multiplicación:', multiplicar(4, 6));
  
  // Con arrays
  const numeros: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  
  // Map
  const cuadrados = numeros.map(n => n * n);
  const cubos = numeros.map(numero => {
    return numero * numero * numero;
  });
  
  // Filter
  const pares = numeros.filter(n => n % 2 === 0);
  const mayoresA5 = numeros.filter(numero => numero > 5);
  
  // Reduce
  const suma = numeros.reduce((a, b) => a + b, 0);
  const producto = numeros.reduce((prev, element) => prev * element, 1);
  
  // Sort
  const nombres: string[] = ['Ana', 'Carlos', 'Beatriz', 'Daniel'];
  nombres.sort((a, b) => a.localeCompare(b));
  
  console.log('Cuadrados:', cuadrados);
  console.log('Números pares:', pares);
  console.log('Suma total:', suma);
  console.log('Nombres ordenados:', nombres);
  
  // Funciones de orden superior
  ejecutarOperacion(10, 5, (a, b) => a + b);
  ejecutarOperacion(10, 5, (a, b) => a - b);
  
  // Closures
  const contador = crearContador();
  console.log('Contador:', contador()); // 1
  console.log('Contador:', contador()); // 2
  console.log('Contador:', contador()); // 3
  
  // Función que retorna función
  const multiplicadorPor3 = crearMultiplicador(3);
  console.log('5 * 3 =', multiplicadorPor3(5));
}

function ejecutarOperacion(a: number, b: number, operacion: (a: number, b: number) => number): void {
  const resultado = operacion(a, b);
  console.log('Resultado de la operación:', resultado);
}

function crearContador(): () => number {
  let count = 0;
  return () => ++count;
}

function crearMultiplicador(factor: number): (numero: number) => number {
  return (numero: number) => numero * factor;
}

9. Características Avanzadas

9.1 Generics

Dart

// Clase genérica
class Caja<T> {
  T _contenido;
  
  Caja(this._contenido);
  
  T get contenido => _contenido;
  set contenido(T valor) => _contenido = valor;
  
  void mostrar() {
    print('Contenido: $_contenido (${_contenido.runtimeType})');
  }
}

// Función genérica
T intercambiar<T>(T valor1, T valor2, bool condicion) {
  return condicion ? valor1 : valor2;
}

// Lista genérica personalizada
class MiLista<T> {
  List<T> _items = [];
  
  void agregar(T item) => _items.add(item);
  T? obtener(int indice) => indice < _items.length ? _items[indice] : null;
  int get longitud => _items.length;
  
  List<U> mapear<U>(U Function(T) transformador) {
    return _items.map(transformador).toList();
  }
}

void ejemploGenerics() {
  // Usando la clase genérica
  var cajaString = Caja<String>('Hola Dart');
  var cajaInt = Caja<int>(42);
  
  cajaString.mostrar();
  cajaInt.mostrar();
  
  // Función genérica
  var resultado = intercambiar<String>('A', 'B', true);
  print('Intercambio: $resultado');
  
  // Lista personalizada
  var miLista = MiLista<int>();
  miLista.agregar(1);
  miLista.agregar(2);
  miLista.agregar(3);
  
  var strings = miLista.mapear<String>((n) => 'Número: $n');
  print('Strings: $strings');
}

TypeScript

// Clase genérica
class Caja<T> {
  private _contenido: T;
  
  constructor(contenido: T) {
    this._contenido = contenido;
  }
  
  get contenido(): T {
    return this._contenido;
  }
  
  set contenido(valor: T) {
    this._contenido = valor;
  }
  
  mostrar(): void {
    console.log(`Contenido: ${this._contenido} (${typeof this._contenido})`);
  }
}

// Función genérica
function intercambiar<T>(valor1: T, valor2: T, condicion: boolean): T {
  return condicion ? valor1 : valor2;
}

// Lista genérica personalizada
class MiLista<T> {
  private _items: T[] = [];
  
  agregar(item: T): void {
    this._items.push(item);
  }
  
  obtener(indice: number): T | undefined {
    return indice < this._items.length ? this._items[indice] : undefined;
  }
  
  get longitud(): number {
    return this._items.length;
  }
  
  mapear<U>(transformador: (item: T) => U): U[] {
    return this._items.map(transformador);
  }
}

function ejemploGenerics(): void {
  // Usando la clase genérica
  const cajaString = new Caja<string>('Hola TypeScript');
  const cajaInt = new Caja<number>(42);
  
  cajaString.mostrar();
  cajaInt.mostrar();
  
  // Función genérica
  const resultado = intercambiar<string>('A', 'B', true);
  console.log('Intercambio:', resultado);
  
  // Lista personalizada
  const miLista = new MiLista<number>();
  miLista.agregar(1);
  miLista.agregar(2);
  miLista.agregar(3);
  
  const strings = miLista.mapear<string>(n => `Número: ${n}`);
  console.log('Strings:', strings);
}

9.2 Extensiones de Clases

Dart

// Extensions en Dart
extension StringExtensions on String {
  bool get esVacio => isEmpty;
  bool get noEsVacio => isNotEmpty;
  
  String capitalizar() {
    if (isEmpty) return this;
    return this[0].toUpperCase() + substring(1).toLowerCase();
  }
  
  String invertir() {
    return split('').reversed.join('');
  }
  
  bool esPalindromo() {
    var limpio = replaceAll(RegExp(r'[^a-zA-Z0-9]'), '').toLowerCase();
    return limpio == limpio.split('').reversed.join('');
  }
}

extension ListExtensions<T> on List<T> {
  T? get primero => isEmpty ? null : first;
  T? get ultimo => isEmpty ? null : last;
  
  List<T> shuffle() {
    var nueva = List<T>.from(this);
    nueva.shuffle();
    return nueva;
  }
  
  Map<K, List<T>> agruparPor<K>(K Function(T) selector) {
    var mapa = <K, List<T>>{};
    for (var item in this) {
      var clave = selector(item);
      mapa.putIfAbsent(clave, () => []).add(item);
    }
    return mapa;
  }
}

void ejemploExtensions() {
  // String extensions
  String texto = 'hola mundo';
  print('Capitalizado: ${texto.capitalizar()}');
  print('Invertido: ${texto.invertir()}');
  print('¿Es palíndromo "radar"? ${'radar'.esPalindromo()}');
  
  // List extensions
  List<int> numeros = [1, 2, 3, 4, 5];
  print('Primer elemento: ${numeros.primero}');
  print('Lista mezclada: ${numeros.shuffle()}');
  
  List<String> palabras = ['casa', 'carro', 'computadora', 'celular'];
  var agrupadas = palabras.agruparPor<String>((p) => p[0]);
  print('Agrupadas por primera letra: $agrupadas');
}

TypeScript

// TypeScript no tiene extensions nativas, pero se pueden simular con declaration merging
declare global {
  interface String {
    esVacio(): boolean;
    noEsVacio(): boolean;
    capitalizar(): string;
    invertir(): string;
    esPalindromo(): boolean;
  }
  
  interface Array<T> {
    primero(): T | undefined;
    ultimo(): T | undefined;
    shuffle(): T[];
    agruparPor<K>(selector: (item: T) => K): Map<K, T[]>;
  }
}

// Implementación de las extensiones
String.prototype.esVacio = function(): boolean {
  return this.length === 0;
};

String.prototype.noEsVacio = function(): boolean {
  return this.length > 0;
};

String.prototype.capitalizar = function(): string {
  if (this.length === 0) return this.toString();
  return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
};

String.prototype.invertir = function(): string {
  return this.split('').reverse().join('');
};

String.prototype.esPalindromo = function(): boolean {
  const limpio = this.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
  return limpio === limpio.split('').reverse().join('');
};

Array.prototype.primero = function<T>(this: T[]): T | undefined {
  return this.length > 0 ? this[0] : undefined;
};

Array.prototype.ultimo = function<T>(this: T[]): T | undefined {
  return this.length > 0 ? this[this.length - 1] : undefined;
};

Array.prototype.shuffle = function<T>(this: T[]): T[] {
  const nueva = [...this];
  for (let i = nueva.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [nueva[i], nueva[j]] = [nueva[j], nueva[i]];
  }
  return nueva;
};

Array.prototype.agruparPor = function<T, K>(this: T[], selector: (item: T) => K): Map<K, T[]> {
  const mapa = new Map<K, T[]>();
  for (const item of this) {
    const clave = selector(item);
    if (!mapa.has(clave)) {
      mapa.set(clave, []);
    }
    mapa.get(clave)!.push(item);
  }
  return mapa;
};

function ejemploExtensions(): void {
  // String extensions
  const texto = 'hola mundo';
  console.log('Capitalizado:', texto.capitalizar());
  console.log('Invertido:', texto.invertir());
  console.log('¿Es palíndromo "radar"?', 'radar'.esPalindromo());
  
  // Array extensions
  const numeros = [1, 2, 3, 4, 5];
  console.log('Primer elemento:', numeros.primero());
  console.log('Lista mezclada:', numeros.shuffle());
  
  const palabras = ['casa', 'carro', 'computadora', 'celular'];
  const agrupadas = palabras.agruparPor(p => p[0]);
  console.log('Agrupadas por primera letra:', agrupadas);
}

10. Comparación de Características

Tabla Comparativa

Característica Dart TypeScript
Tipado Fuertemente tipado, inferencia Tipado estático opcional
Null Safety Null safety nativo Strict null checks
Compilación AOT/JIT a código nativo Transpila a JavaScript
Plataformas Mobile, Web, Desktop, Server Web, Node.js, Desktop
Sintaxis Similar a Java/C# Superset de JavaScript
Async/Await Future/Stream nativos Promise/async-await
Orientación a Objetos Clases, mixins, interfaces Clases, interfaces
Funcional Funciones de primera clase Funciones de primera clase
Extensions Extensions nativas Declaration merging
Generics Soporte completo Soporte completo
Ecosistema pub.dev npm

Ventajas de Dart

  1. Rendimiento: Compilación AOT para mejor rendimiento
  2. Flutter: Desarrollo multiplataforma nativo
  3. Null Safety: Incorporado por defecto
  4. Hot Reload: Desarrollo rápido
  5. Sintaxis limpia: Menos verboso que TypeScript
  6. Mixins: Composición más flexible

Ventajas de TypeScript

  1. Ecosistema: Acceso a todo el ecosistema JavaScript
  2. Gradual: Adopción incremental en proyectos JS
  3. Herramientas: Mejor soporte de IDEs
  4. Comunidad: Más grande y madura
  5. Flexibilidad: Más opciones de configuración
  6. Interoperabilidad: Funciona con librerías JS existentes

11. Casos de Uso Recomendados

Usar Dart cuando:

  • Desarrollas aplicaciones móviles con Flutter
  • Necesitas aplicaciones de alto rendimiento
  • Quieres una sola base de código para múltiples plataformas
  • Prefieres un lenguaje más estructurado y menos permisivo
  • El null safety es crítico en tu aplicación

Usar TypeScript cuando:

  • Desarrollas aplicaciones web
  • Tienes un equipo con experiencia en JavaScript
  • Necesitas integrar con librerías JavaScript existentes
  • Quieres migrar gradualmente desde JavaScript
  • Desarrollas aplicaciones Node.js

12. Conclusión

Tanto Dart como TypeScript son lenguajes modernos y potentes, cada uno con sus fortalezas específicas. Dart brilla en el desarrollo móvil multiplataforma con Flutter, mientras que TypeScript es excelente para desarrollo web y aplicaciones Node.js. La elección entre ellos depende principalmente del tipo de aplicación que estés desarrollando y del ecosistema en el que prefieras trabajar.

Ambos lenguajes ofrecen características modernas como tipado fuerte, programación asíncrona, orientación a objetos, y programación funcional, lo que los hace opciones sólidas para el desarrollo de aplicaciones modernas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment