TypeScript para Programadores de Java/C#

TypeScript es una opción popular para programadores acostumbrados a otros lenguajes con tipado estático, como C# y Java.

El sistema de tipos de TypeScript ofrece muchos de los mismos beneficios, como una mejor finalización de código, detección temprana de errores y una comunicación más clara entre las partes de tu programa. Aunque TypeScript proporciona muchas características familiares para estos desarrolladores, vale la pena dar un paso atrás para ver cómo JavaScript (y por lo tanto TypeScript) difieren de los lenguajes POO tradicionales. Comprender estas diferencias te ayudará a escribir mejor código JavaScript y a evitar errores comunes en los que pueden caer los programadores que van directamente de C#/Java a TypeScript.

Co-aprendizaje de JavaScript

Si ya estás familiarizado con JavaScript pero eres principalmente un programador de Java o C#, esta página introductoria puede ayudar a explicar algunos de los conceptos erróneos y dificultades comunes a los que podrías ser susceptible. Algunas de las formas en que TypeScript modela los tipos son bastante diferentes de Java o C#, y es importante tenerlas en cuenta al aprender TypeScript.

Si eres un programador de Java o C# que es nuevo en JavaScript en general, te recomendamos aprender un poco de JavaScript sin tipos primero para comprender los comportamientos en tiempo de ejecución de JavaScript. Debido a que TypeScript no cambia cómo se ejecuta tu código, ¡todavía tendrás que aprender cómo funciona JavaScript para escribir código que realmente haga algo!

Es importante recordar que TypeScript usa el mismo entorno de ejecución que JavaScript, por lo que cualquier recurso sobre cómo lograr un comportamiento específico en tiempo de ejecución (convertir una cadena a un número, mostrar una alerta, escribir un archivo en disco, etc.) siempre se aplicará igualmente bien a los programas TypeScript. ¡No te limites a los recursos específicos de TypeScript!

Repensando la Clase

C# y Java son lo que podríamos llamar lenguajes de POO obligatoria. En estos lenguajes, la clase es la unidad básica de organización del código, y también el contenedor básico de todos los datos y comportamiento en tiempo de ejecución. Forzar que toda la funcionalidad y los datos se mantengan en clases puede ser un buen modelo de dominio para algunos problemas, pero no todos los dominios necesitan ser representados de esta manera.

Funciones Libres y Datos

En JavaScript, las funciones pueden vivir en cualquier lugar, y los datos pueden pasarse libremente sin estar dentro de una class o struct predefinida. Esta flexibilidad es extremadamente poderosa. Las funciones “libres” (aquellas no asociadas con una clase) que trabajan sobre datos sin una jerarquía POO implícita tienden a ser el modelo preferido para escribir programas en JavaScript.

Clases Estáticas

Además, ciertas construcciones de C# y Java como los singletons y las clases estáticas son innecesarias en TypeScript.

POO en TypeScript

Dicho esto, ¡todavía puedes usar clases si quieres! Algunos problemas se adaptan bien a ser resueltos por una jerarquía POO tradicional, y el soporte de TypeScript para las clases de JavaScript hará que estos modelos sean aún más poderosos. TypeScript admite muchos patrones comunes como la implementación de interfaces, la herencia y los métodos estáticos.

Cubriremos las clases más adelante en esta guía.

Repensando los Tipos

La comprensión de TypeScript de un tipo es en realidad bastante diferente de la de C# o Java. Exploremos algunas diferencias.

Sistemas de Tipos Nominales Reificados

En C# o Java, cualquier valor u objeto dado tiene un tipo exacto: null, un primitivo o un tipo de clase conocido. Podemos llamar a métodos como value.GetType() o value.getClass() para consultar el tipo exacto en tiempo de ejecución. La definición de este tipo residirá en una clase en algún lugar con algún nombre, y no podemos usar dos clases con formas similares en lugar de la otra a menos que haya una relación de herencia explícita o una interfaz comúnmente implementada.

Estos aspectos describen un sistema de tipos reificado y nominal. Los tipos que escribimos en el código están presentes en tiempo de ejecución, y los tipos se relacionan a través de sus declaraciones, no de sus estructuras.

Tipos como Conjuntos

En C# o Java, tiene sentido pensar en una correspondencia uno a uno entre los tipos en tiempo de ejecución y sus declaraciones en tiempo de compilación.

En TypeScript, es mejor pensar en un tipo como un conjunto de valores que comparten algo en común. Debido a que los tipos son solo conjuntos, un valor particular puede pertenecer a muchos conjuntos al mismo tiempo.

Una vez que comienzas a pensar en los tipos como conjuntos, ciertas operaciones se vuelven muy naturales. Por ejemplo, en C#, es incómodo pasar un valor que es o bien un string o un int, porque no hay un solo tipo que represente este tipo de valor.

En TypeScript, esto se vuelve muy natural una vez que te das cuenta de que cada tipo es solo un conjunto. ¿Cómo describes un valor que pertenece al conjunto string o al conjunto number? Simplemente pertenece a la unión de esos conjuntos: string | number.

TypeScript proporciona una serie de mecanismos para trabajar con tipos de manera teórico-conjuntista, y los encontrarás más intuitivos si piensas en los tipos como conjuntos.

Tipos Estructurales Borrados

En TypeScript, los objetos no son de un único tipo exacto. Por ejemplo, si construimos un objeto que satisface una interfaz, podemos usar ese objeto donde se espera esa interfaz aunque no hubiera una relación declarativa entre los dos.

ts
interface Pointlike {
x: number;
y: number;
}
interface Named {
name: string;
}
 
function logPoint(point: Pointlike) {
console.log("x = " + point.x + ", y = " + point.y);
}
 
function logName(x: Named) {
console.log("Hola, " + x.name);
}
 
const obj = {
x: 0,
y: 0,
name: "Origin",
};
 
logPoint(obj);
logName(obj);
Try

El sistema de tipos de TypeScript es estructural, no nominal: Podemos usar obj como Pointlike porque tiene propiedades x e y que son ambos números. Las relaciones entre tipos están determinadas por las propiedades que contienen, no si fueron declaradas con alguna relación particular.

El sistema de tipos de TypeScript tampoco es reificado: No hay nada en tiempo de ejecución que nos diga que obj es Pointlike. De hecho, el tipo Pointlike no está presente de ninguna forma en tiempo de ejecución.

Volviendo a la idea de tipos como conjuntos, podemos pensar que obj es miembro tanto del conjunto de valores Pointlike como del conjunto de valores Named.

Consecuencias del Tipado Estructural

Los programadores de POO a menudo se sorprenden por dos aspectos particulares del tipado estructural.

Tipos Vacíos

El primero es que el tipo vacío parece desafiar las expectativas:

ts
class Empty {}
 
function fn(arg: Empty) {
// ¿hacer algo?
}
 
// No hay error, pero esto no es un 'Empty' ?
fn({ k: 10 });
Try

TypeScript determina si la llamada a fn aquí es válida viendo si el argumento proporcionado es un Empty válido. Lo hace examinando la estructura de { k: 10 } y class Empty { }. Podemos ver que { k: 10 } tiene todas las propiedades que tiene Empty, porque Empty no tiene propiedades. ¡Por lo tanto, esta es una llamada válida!

Esto puede parecer sorprendente, pero en última instancia es una relación muy similar a la que se aplica en los lenguajes POO nominales. Una subclase no puede eliminar una propiedad de su clase base, porque hacerlo destruiría la relación de subtipo natural entre la clase derivada y su base. Los sistemas de tipos estructurales simplemente identifican esta relación implícitamente describiendo subtipos en términos de tener propiedades de tipos compatibles.

Tipos Idénticos

Otra fuente frecuente de sorpresa viene con tipos idénticos:

ts
class Car {
drive() {
// pisar el acelerador
}
}
class Golfer {
drive() {
// golpear la bola lejos
}
}
// ¿Sin error?
let w: Car = new Golfer();

Nuevamente, esto no es un error porque las estructuras de estas clases son las mismas. Aunque esto pueda parecer una fuente potencial de confusión, en la práctica, las clases idénticas que no deberían estar relacionadas no son comunes.

Aprenderemos más sobre cómo se relacionan las clases entre sí en el capítulo de Clases.

Reflexión

Los programadores de POO están acostumbrados a poder consultar el tipo de cualquier valor, incluso uno genérico:

csharp
// C#
static void LogType<T>() {
Console.WriteLine(typeof(T).Name);
}

Debido a que el sistema de tipos de TypeScript se borra por completo, la información sobre, por ejemplo, la instanciación de un parámetro de tipo genérico no está disponible en tiempo de ejecución.

JavaScript tiene algunas primitivas limitadas como typeof e instanceof, pero recuerda que estos operadores todavía funcionan sobre los valores tal como existen en el código de salida sin tipos. Por ejemplo, typeof (new Car()) será "object", no Car o "Car".

Próximos Pasos

Esto fue una breve descripción general de la sintaxis y las herramientas utilizadas en el código TypeScript cotidiano. Desde aquí, puedes:

The TypeScript docs are an open source project. Help us improve these pages by sending a Pull Request

Contributors to this page:
FKFabián Karaben  (6)

Last updated: 02 may 2025