Skip to content

📙 Clase 16 — Angular con TypeScript: crear y entender el proyecto

TypeScript · 2026-08-07 · Carpeta: 02-Ejercicios/Angular/proyectoAngular ⬅️ Volver al índice de clases

🎯 Qué aprendí

  • Por qué Angular y TypeScript son socios naturales: todo proyecto Angular usa TypeScript desde el primer comando, sin configurar nada extra.
  • Cómo instalar el Angular CLI globalmente y verificarlo con ng version.
  • El flujo completo: ng newcdng servehttp://localhost:4200/.
  • Qué es un decorador (@Component) y cómo Angular usa standalone components hoy — sin AppModule, sin declarations, cada componente declara sus propios imports.
  • La cadena de tsconfig.jsontsconfig.app.json + tsconfig.spec.json (el mismo patrón de references que ya viste en las Clases 13 y 15).
  • 🔎 Un hallazgo real que contradice al material que estudié: el artículo dice que habilitar decoradores es "fundamental" — pero verifiqué que TypeScript 6.0 ya compila @Component sin experimentalDecorators, usando el nuevo estándar de decoradores de ECMAScript. Angular lo sigue incluyendo por costumbre/compatibilidad, no porque sea estrictamente obligatorio hoy.
  • 🔎 Otro hallazgo real, y en dirección opuesta a la Clase 15: ng serve revisa tipos en cada guardado (a diferencia de npm run dev de Vite, que no revisaba nada). Lo comprobé metiendo un error de tipos a propósito.
  • 🐛 Un tercer incidente real: TypeScript 6.0 exige rootDir explícito en cuanto defines outDir (antes lo inferia solo) — Angular CLI 22 generó los tsconfig sin rootDir, así que tronaban con error TS5011. Lo diagnostiqué y arreglé agregando una sola línea a cada archivo.

📚 Definiciones clave

TérminoQué esEjemplo de esta clase
ngComando del Angular CLI — crea, sirve y compila proyectos Angularng new, ng serve, ng build
@Component({...})Decorador que le dice a Angular cómo tratar una clase como componente visual@Component({ selector: 'app-root', ... })
Standalone componentComponente que declara sus propios imports, sin necesitar un NgModuleimports: [RouterOutlet]
signal(...)Primitiva reactiva de Angular — un valor que Angular sabe cuándo cambiótitle = signal('proyectoAngular')

📖 PARTE TEÓRICA

🤝 1. Por qué Angular y TypeScript son una combinación natural

Angular es uno de los frameworks más usados para construir interfaces — se destaca por manejar datos de forma eficiente y estructurada. TypeScript encaja perfecto porque su sistema de tipos permite validar esos datos antes de que lleguen a producción.

La ventaja concreta: todo proyecto Angular ya trae TypeScript integrado desde el día uno — no hay que instalar nada aparte ni tocar un tsconfig.json vacío como en otras clases. Eso trae:

  • Mayor seguridad en el código (errores de tipo detectados en el editor, no en el navegador).
  • Detección temprana de errores — antes de correr nada.
  • Autocompletado más preciso, porque el editor conoce la forma exacta de tus datos.

🧪 Entrevista: ¿Por qué Angular usa TypeScript de forma nativa? Porque el propio framework está escrito en TypeScript y su CLI genera todos los archivos ya tipados — no es una capa opcional como en otros frameworks, es parte del diseño desde el origen.

🛠️ 2. Instalar el Angular CLI y verificarlo

bash
sudo npm install -g @angular/cli
ng version

Verificado en este equipo — ng version muestra el logo en ASCII y la versión instalada, tal como describe el material:

zsh · ng version
$ ng version
     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
Angular CLI       : 22.1.3
Angular           : 22.1.0
Node.js           : 24.18.0

💡 El número de versión que veas tú probablemente sea distinto — Angular saca versiones mayores cada pocos meses. Lo importante es que aparezca el logo ASCII sin errores: confirma que el CLI quedó bien instalado.

🆕 3. Crear el proyecto: ng new

bash
ng new proyecto-angular

El asistente pregunta, en orden:

  1. ¿Qué hojas de estilo quieres usar? (CSS, SCSS, Sass, Less) — en este proyecto real se eligió CSS (confirmado: src/styles.css y src/app/app.css, sin ningún archivo .scss).
  2. ¿Habilitar Server-Side Rendering (SSR)? — en este proyecto real se respondió No (confirmado: no existe ningún server.ts ni configuración de SSR en angular.json).
bash
cd proyecto-angular
ng serve

Verificado en este equipo — el proyecto compila y queda escuchando en el puerto 4200:

zsh · proyectoAngular
$ ng serve
Application bundle generation complete. [0.814 seconds]
Watch mode enabled. Watching for file changes...
➜  Local:   http://localhost:4200/

💡 Detalle que no está en el material original: en el log aparece una línea que dice [vite] (client) Re-optimizing dependencies — el ng serve moderno usa Vite internamente para el servidor de desarrollo (desde Angular 17+). Es el mismo Vite que ya conoces de la Clase 15, solo que aquí Angular lo orquesta por ti, sin que tengas que tocar ningún vite.config.ts.

Abrí http://localhost:4200/ en el navegador y confirmé la página de bienvenida de Angular, sin errores en consola.

🗂️ 4. Estructura real del proyecto — y un nombre que cambió

proyectoAngular/
├── public/
│   └── favicon.ico
├── src/
│   ├── app/
│   │   ├── app.ts          ← lógica del componente (TypeScript)
│   │   ├── app.html         ← plantilla del componente
│   │   ├── app.css
│   │   ├── app.config.ts    ← configuración de la app (providers, router)
│   │   └── app.routes.ts    ← definición de rutas
│   ├── index.html
│   ├── main.ts               ← punto de entrada
│   └── styles.css
├── tsconfig.json
├── tsconfig.app.json
├── tsconfig.spec.json
└── angular.json

⚠️ Esto cambió respecto al material original. El artículo habla de app.component.ts y app.component.html — ese era el nombre clásico de Angular. En la versión del CLI instalada hoy (22.1.3), el schematic por defecto genera app.ts y app.html, sin el infijo .component.. El concepto es idéntico (lógica en .ts, plantilla en .html); solo cambió la convención de nombres.

🎭 5. El decorador @Component y los standalone components

typescript
// src/app/app.ts
import { Component, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet],
  templateUrl: './app.html',
  styleUrl: './app.css'
})
export class App {
  protected readonly title = signal('proyectoAngular');
}

Un decorador es una función especial (marcada con @) que se coloca justo encima de una clase, propiedad o método, y le agrega metadatos o modifica su comportamiento sin cambiar el código de la clase en sí. @Component le dice a Angular: "esta clase no es una clase cualquiera, es un componente visual — así es como debes tratarla".

🧪 Entrevista: ¿Qué es un decorador en TypeScript/Angular? Una función que se antepone con @ a una clase, propiedad o método para agregarle metadatos o modificar su comportamiento en tiempo de compilación, sin tocar la lógica interna de la clase.

Fíjate en tres detalles reales de este componente, comparados con lo que describe el material:

Propiedad del decoradorLo que dice el materialLo que hay en el proyecto real
EstilosstyleUrls: ['./app.component.css'] (plural, array)styleUrl: './app.css' (singular, string)
MóduloSe asume un AppModule con declarations: [AppComponent]No existe AppModule — es un standalone component
titletitle = 'proyecto-angular'; (propiedad simple)title = signal('proyectoAngular'); (signal reactiva)

imports: [RouterOutlet] es la clave de los standalone components: en vez de registrar el componente en un NgModule central (declarations, imports, bootstrap), cada componente declara sus propios imports directamente en su decorador. main.ts arranca la app sin pasar por ningún módulo:

typescript
// src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { App } from './app/app';

bootstrapApplication(App, appConfig)
  .catch((err) => console.error(err));

📝 Esto también es distinto del Angular "clásico" que describe el material — bootstrapApplication reemplaza al viejo platformBrowserDynamic().bootstrapModule(AppModule). Es la forma estándar desde Angular 17+; ya no hace falta un AppModule para una app simple como esta.

⚙️ 6. La cadena de tsconfig: mismo patrón que ya conoces

json
// tsconfig.json (fragmento real)
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "target": "ES2022",
    "module": "preserve"
  },
  "angularCompilerOptions": {
    "strictInjectionParameters": true,
    "strictInputAccessModifiers": true
  },
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.spec.json" }
  ]
}

Exactamente el mismo patrón de references que viste en la Clase 15 con Vite: tsconfig.json no compila nada por sí mismo, solo apunta a tsconfig.app.json (tu código de app, con outDir propio) y tsconfig.spec.json (los archivos de prueba, *.spec.ts).

💡 angularCompilerOptions es una sección aparte, exclusiva del compilador de Angular (@angular/compiler-cli) — no son opciones de TypeScript puro, son reglas extra que Angular revisa sobre tu código (por ejemplo, strictInputAccessModifiers evita que uses un @Input() privado de forma incorrecta desde una plantilla).

🐛 6.1. Un tercer incidente real: rootDir explícito obligatorio (TS 6.0)

Este es otro problema que apareció trabajando en el proyecto real, sin relación con Angular en sí — es un cambio de comportamiento de TypeScript 6.0.

Síntoma: tsc --build (o el propio ng build) marcaba error en la línea de outDir de tsconfig.app.json y tsconfig.spec.json.

Causa real: antes de TypeScript 6.0, si tenías outDir pero no rootDir, el compilador inferia automáticamente cuál era la carpeta raíz de tus fuentes. Desde TS 6.0 eso ya no pasa — si defines outDir, tienes que decir explícitamente rootDir también, o el compilador se niega a adivinar por ti:

zsh · proyectoAngular (sin rootDir)
$ tsc --build --force
tsconfig.app.json(6,5): error TS5011: The common source directory of 'tsconfig.app.json' is './src'. The 'rootDir' setting must be explicitly set to this or another path to adjust your output's file layout.
  Visit https://aka.ms/ts6 for migration information.

Angular CLI 22 genera sus tsconfig.app.json/tsconfig.spec.json sin rootDir — por eso el error aparece apenas actualizas a una versión de TypeScript que exige esta regla más estricta.

El fix, agregando una sola línea a cada archivo:

json
// tsconfig.app.json y tsconfig.spec.json
{
  "compilerOptions": {
    "outDir": "./out-tsc/app",
    "rootDir": "./src",
    "types": []
  }
}

Verificado: con rootDir presente, tsc --build --force termina con código de salida 0, sin ningún error.

💡 out-tsc/ (la carpeta de salida que genera este build interno de TypeScript, separado del dist/ de ng build) ya viene incluida en el .gitignore que trae el proyecto por defecto — no ensucia el repositorio aunque la generes varias veces.

🧪 Tip de entrevista: ¿qué relación hay entre outDir y rootDir en TypeScript? rootDir le dice al compilador cuál es la carpeta que contiene TODOS tus archivos fuente — outDir reproduce esa misma estructura de carpetas, pero compilada, dentro de la carpeta de salida. Sin rootDir explícito, TypeScript tiene que adivinar esa carpeta común a partir de tus include; desde TS 6.0, ya no adivina — lo exige por escrito.

🔎 7. Hallazgo real: ¿son los decoradores realmente "fundamentales"?

El material dice: "Habilitación de decoradores, que son fundamentales para la comunicación entre Angular y TypeScript" — dando a entender que sin experimentalDecorators: true el proyecto no compilaría. Lo puse a prueba.

Quité la línea "experimentalDecorators": true de tsconfig.json, borré la caché (.angular/cache) para estar seguro, y corrí ng build de nuevo:

zsh · proyectoAngular (sin experimentalDecorators)
$ ng build
✔ Building...
Application bundle generation complete. [2.188 seconds]  ← compiló IGUAL, sin la bandera

Compiló sin problema. La razón, verificada por separado con tsc puro (fuera de Angular): TypeScript 6.0 ya soporta el nuevo estándar de decoradores de ECMAScript (stage 3, nativo desde TS 5.0) — no necesita ninguna bandera especial. experimentalDecorators solo activa el sistema antiguo, específico de TypeScript, que existía antes de que el estándar se aprobara. Comparé el JavaScript generado para el mismo decorador, con y sin la bandera:

javascript
// CON experimentalDecorators: true (sistema legacy)
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  // usa Reflect.decorate(...)
};
Saludo = __decorate([MiDecorador], Saludo);
javascript
// SIN experimentalDecorators (estándar ECMAScript, TS 5.0+)
var __esDecorate = (this && this.__esDecorate) || function (...) {
  // usa Symbol.metadata, __runInitializers(...)
};

Son dos implementaciones de decoradores completamente distintas por dentro, generando código diferente — y ambas hacen que @Component compile.

⚠️ Esto no significa que debas borrar la línea de tu propio proyecto — Angular sigue incluyéndola por defecto, probablemente porque su sistema de inyección de dependencias (que usa metadatos de tipos en tiempo de ejecución) confía en el comportamiento específico del sistema legacy, no solo en que el componente compile. La lección real es otra: verificar antes de repetir una afirmación — "es fundamental" resultó ser más matizado de lo que decía el material.

🔎 8. Hallazgo real: ng serve SÍ revisa tipos (al revés que Vite)

En la Clase 15 verifiqué que npm run dev de Vite nunca revisa tipos — solo transpila. Con Angular probé exactamente lo mismo, y el resultado fue opuesto.

Con ng serve corriendo, agregué a propósito una línea con un tipo incorrecto en app.ts:

typescript
protected readonly numero: number = "esto no es un number";
zsh · ng serve
❯ Changes detected. Rebuilding...
Application bundle generation failed. [0.133 seconds]
✘ [ERROR] TS2322: Type 'string' is not assignable to type 'number'.
    src/app/app.ts:12:21:

El bundle falló de inmediato — a diferencia de Vite, que dejaba correr la app igual con el error de tipos adentro. Quité la línea y el servidor se recuperó solo ("Page reload sent to client(s)").

Vite + React (Clase 15)Angular CLI (esta clase)
¿dev/serve revisa tipos?No — solo transpila — falla el build ante cualquier error
¿Por qué?Usa esbuild/SWC (solo quita tipos)Usa @angular/compiler-cli, compilación AOT completa
¿Cuándo lo descubres?Recién en npm run buildAl instante, en cada guardado

💡 Esto tiene una consecuencia práctica real: en Angular es mucho más difícil arrastrar un error de tipos sin darte cuenta — el propio servidor de desarrollo te lo muestra de inmediato, sin esperar a un build de producción.

✍️ 9. Editar la plantilla inicial

Cambiar el mensaje de bienvenida es tan simple como editar app.html:

html
<!-- antes -->
<h1>Hello, {​{ title() }​}</h1>

<!-- después -->
<h1>Hola, felicidades {​{ title() }​}</h1>

📝 Fíjate en title() — con llamada a función, no title a secas. Como title es una signal (sección 5), hay que "leerla" invocándola como función dentro de la plantilla — es la sintaxis que Angular usa para saber exactamente cuándo ese valor cambió y solo actualizar esa parte del DOM.

Verificado en Chrome real: guardé el cambio con ng serve corriendo, y el texto se actualizó sin recargar la página — el mismo concepto de HMR que ya viste en Vite (Clase 12, Clase 15), aquí bajo el nombre "Component update sent to client(s)" en el log de Angular.

🆕 10. Resumen: lo que cambió desde que se grabó el material original

En el material originalVerificado en este equipo (Angular 22)
app.component.ts / app.component.htmlapp.ts / app.html (sin el infijo .component.)
styleUrls: [...] (plural, array)styleUrl: '...' (singular, string)
Se asume un AppModuleNo existe — standalone components, bootstrapApplication
title = 'proyecto-angular';title = signal('proyectoAngular'); (reactividad con signals)
"Los decoradores son fundamentales" (implica que la bandera es obligatoria)Compila igual sin experimentalDecorators — TS 6.0 ya soporta decoradores estándar
No menciona qué corre por dentro ng serveUsa Vite internamente desde Angular 17+
No se menciona rootDirTypeScript 6.0 lo exige explícito en cuanto hay outDir — sin él, error TS5011

💻 PARTE PRÁCTICA

Carpeta trabajada: 02-Ejercicios/Angular/proyectoAngular/ (creada con el CLI real, ng serve y ng build ya verificados arriba).

bash
ng version
cd 02-Ejercicios/Angular/proyectoAngular
npm start        # equivale a "ng serve"

Verificado en Chrome real, sirviendo el proyecto en http://localhost:4200/:

Chrome · proyectoAngular
Hello, proyectoAngular
Congratulations! Your app is running. 🎉

Build de producción verificado también:

zsh · proyectoAngular
$ npm run build
Application bundle generation complete. [2.564 seconds]
Output location: dist/proyectoAngular

Todo verificado y corriendo. ✅


🏋️ EJERCICIOS CON SOLUCIÓN

Ejercicio 1 — Verificar la instalación con ng version

Corre ng version en tu terminal y confirma que aparece el logo ASCII y un número de versión, sin errores.

💡 ¿Sabías que…? — `ng version` también funciona DENTRO de un proyecto

Corrido dentro de la carpeta de un proyecto Angular, ng version muestra información extra: qué paquetes de @angular/* tiene instalados ese proyecto específico, además de la versión global del CLI.

Ver solución
bash
ng version

Salida esperada: el logo ASCII de Angular, seguido de una tabla con Angular CLI, Angular, Node.js, Package Manager y sus versiones.

Ejercicio 2 — Generar un segundo componente con el CLI

Usa ng generate component saludo (o ng g c saludo) dentro del proyecto. ¿Qué archivos crea, y qué nombre usan (con o sin .component.)?

💡 ¿Sabías que…? — el CLI genera Y registra el componente en un solo paso

ng generate component no solo crea los archivos — también actualiza automáticamente cualquier configuración necesaria para que el componente quede listo para usarse (en un proyecto standalone, simplemente queda disponible para importar).

Ver solución
bash
cd 02-Ejercicios/Angular/proyectoAngular
ng generate component saludo

Archivos generados (con la convención moderna, sin .component.):

src/app/saludo/saludo.ts
src/app/saludo/saludo.html
src/app/saludo/saludo.css
src/app/saludo/saludo.spec.ts

Ejercicio 3 — Editar app.html y confirmar el HMR

Cambia el texto del <h1> en app.html (con ng serve corriendo) y confirma que el navegador se actualiza sin que tú recargues la página.

💡 ¿Sabías que…? — Angular llama a esto "Component update", no "HMR"

El concepto es el mismo que viste con Vite en la Clase 12 y Clase 15, pero el log de Angular lo describe distinto: "Component update sent to client(s)" en vez de "hmr update".

Ver solución
html
<!-- app.html -->
<h1>Hola, felicidades {​{ title() }​}</h1>

Terminal esperada:

zsh · ng serve
Component update sent to client(s).

Ejercicio 4 — Cambiar el valor de una signal con un evento de clic

Agrega un <button> en app.html que, al hacer clic, cambie el valor de title usando title.set(...).

💡 ¿Sabías que…? — las signals tienen `.set()` y `.update()`

title.set('nuevo valor') reemplaza el valor completo. title.update(actual => actual + '!') lo calcula a partir del valor anterior — útil para contadores o acumuladores, parecido a la forma funcional de setCount que ya viste con useState en React (Clase 15).

Ver solución
typescript
// app.ts
protected cambiarTitulo(): void {
  this.title.set('¡Cambiado desde un clic!');
}
html
<!-- app.html -->
<button (click)="cambiarTitulo()">Cambiar título</button>

Al hacer clic, el <h1> cambia sin recargar la página — la signal notifica a Angular exactamente qué parte del DOM actualizar.

Ejercicio 5 — Forzar un error de tipos con ng serve corriendo

Agrega protected readonly x: number = "texto"; en app.ts, con ng serve corriendo. Anticipa qué esperas ver, y compáralo con lo que pasaba en Vite (Clase 15).

💡 ¿Sabías que…? — Angular usa compilación AOT también en desarrollo

A diferencia de esbuild/SWC (que solo transpilan), el compilador de Angular (@angular/compiler-cli) hace Ahead-of-Time compilation real incluso en ng serve — por eso detecta errores de tipos al instante, no solo en producción.

Ver solución
typescript
protected readonly x: number = "texto";

Terminal real — el bundle falla de inmediato:

zsh · ng serve
Application bundle generation failed.
✘ [ERROR] TS2322: Type 'string' is not assignable to type 'number'.

💡 En Vite/React, este mismo error NO detenía npm run dev — solo npm run build lo detectaba. Angular es más estricto desde el desarrollo mismo.

Ejercicio 6 — Leer la cadena de tsconfig del proyecto Angular

Abre tsconfig.app.json y compáralo con tsconfig.app.json de tu proyecto React de la Clase 15. ¿Qué tienen en común?

💡 ¿Sabías que…? — el patrón `references` no es exclusivo de un framework

Tanto Vite como Angular CLI adoptaron el mismo patrón de TypeScript: un tsconfig.json "orquestador" con references a configs específicas (app, node/spec). No es casualidad — es una característica de TypeScript que cualquier herramienta puede aprovechar, no algo inventado por React o Angular.

Ver solución

Ambos usan "files": [] + "references": [...] en el tsconfig.json raíz, y ambos tienen un outDir propio en su config de aplicación. La diferencia: Angular separa app de spec (pruebas), mientras Vite separaba app de node (el entorno de vite.config.ts) — cada framework divide según lo que necesita, pero el patrón de fondo es el mismo.

Ejercicio 7 — Probar sin experimentalDecorators en tu propio proyecto

Quita la línea "experimentalDecorators": true de tsconfig.json y corre ng build. Anticipa el resultado antes de probarlo.

💡 ¿Sabías que…? — verificar > memorizar lo que dice un tutorial

Este es exactamente el ejercicio que hice yo para escribir la sección 7 de la teoría. La lección no es "quita la bandera de tu proyecto real" (Angular la deja por una razón), sino aprender a comprobar afirmaciones en vez de repetirlas de memoria.

Ver solución
bash
rm -rf .angular/cache dist
ng build

Compila sin errores, exactamente igual que con la bandera presente — confirma que TypeScript 6.0 no depende de ella para procesar @Component.

⚠️ Restaura la línea después del ejercicio — no hay motivo para quitarla de tu proyecto real, solo era para comprobar la afirmación.

Ejercicio 8 — Comparar decoradores legacy vs estándar, aislado

Fuera de Angular, en un archivo demo.ts con un decorador simple de clase, compila una vez con tsc --experimentalDecorators demo.ts y otra vez con tsc demo.ts (sin la bandera). Compara el .js generado.

💡 ¿Sabías que…? — los helpers delatan qué sistema de decoradores se usó

Busca __decorate + Reflect.decorate (sistema legacy, con la bandera) contra __esDecorate + Symbol.metadata (estándar ECMAScript, sin la bandera) — son la huella dactilar de cada sistema en el JavaScript compilado.

Ver solución
typescript
// demo.ts
function MiDecorador(constructor: Function) {
  console.log("Decorador aplicado a:", constructor.name);
}

@MiDecorador
class Saludo {
  mensaje = "hola";
}

Con --experimentalDecorators: el .js usa __decorate y Reflect.decorate. Sin la bandera: usa __esDecorate, __runInitializers y Symbol.metadata. Ambos producen un programa funcional — pero son implementaciones distintas por dentro.

Ejercicio 9 — Explorar el dist/ de producción

Corre ng build y revisa la carpeta dist/proyectoAngular/ generada.

💡 ¿Sabías que…? — Angular también genera archivos con hash, como Vite

Igual que viste en la Clase 15, los archivos de dist/ llevan un hash en el nombre (main-3RJTBKW5.js) para evitar que el navegador sirva una versión vieja desde caché.

Ver solución
bash
ng build
find dist/proyectoAngular -maxdepth 2

Terminal esperada:

zsh · proyectoAngular
dist/proyectoAngular/browser/index.html
dist/proyectoAngular/browser/main-3RJTBKW5.js
dist/proyectoAngular/browser/styles-5INURTSO.css

Ejercicio 10 — Integrador: un componente nuevo con signal y evento

Crea un componente contador (ng g c contador) con una signal inicializada en 0 y un botón que la incremente en 1 cada clic. Úsalo dentro de app.html.

💡 ¿Sabías que…? — este ejercicio junta TODO lo de la clase

ng generate para crear el componente, @Component con imports (standalone), signal + .update() para el estado reactivo, y un evento (click) en la plantilla — el mismo patrón que ya armaste con React y useState en la Clase 15, ahora con las herramientas propias de Angular.

Ver solución
bash
ng generate component contador
typescript
// src/app/contador/contador.ts
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-contador',
  templateUrl: './contador.html',
  styleUrl: './contador.css'
})
export class Contador {
  protected readonly valor = signal(0);

  incrementar(): void {
    this.valor.update(actual => actual + 1);
  }
}
html
<!-- src/app/contador/contador.html -->
<p>Valor: {​{ valor() }​}</p>
<button (click)="incrementar()">+1</button>
typescript
// app.ts — agregar el import
import { Contador } from './contador/contador';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, Contador],
  templateUrl: './app.html',
  styleUrl: './app.css'
})
html
<!-- app.html — usarlo en cualquier parte -->
<app-contador></app-contador>

Al hacer clic en el botón, el número sube sin recargar la página.

❓ Preguntas y respuestas (autoevaluación)

1. ¿Por qué Angular usa TypeScript de forma nativa, sin configuración extra?

Porque el propio framework está construido en TypeScript, y su CLI genera todos los archivos de un proyecto nuevo ya tipados desde el primer comando.

2. ¿Cuál es el comando para instalar el Angular CLI de forma global?

sudo npm install -g @angular/cli. Se verifica con ng version.

3. ¿Qué comando crea un proyecto Angular nuevo, y qué preguntas hace?

ng new nombre-proyecto — pregunta qué hoja de estilos usar (CSS, SCSS…) y si quieres habilitar Server-Side Rendering.

4. ¿En qué puerto corre ng serve por defecto?

4200http://localhost:4200/.

5. ¿Qué es un decorador, en una frase?

Una función que se antepone con @ a una clase (o propiedad/método) para agregarle metadatos o modificar su comportamiento, sin tocar su lógica interna.

6. ¿Por qué el proyecto real de esta clase no tiene ningún AppModule?

Porque usa standalone components — cada componente declara sus propios imports directamente en @Component, y main.ts arranca la app con bootstrapApplication en vez de registrar un módulo central.

7. ¿ng serve revisa errores de tipos mientras desarrollas?

Sí — a diferencia de npm run dev de Vite (Clase 15), Angular usa compilación AOT real incluso en desarrollo, así que un error de tipos hace fallar el bundle de inmediato.

8. ¿Es experimentalDecorators: true estrictamente obligatorio para que @Component compile hoy?

No, verificado: TypeScript 6.0 soporta el nuevo estándar de decoradores de ECMAScript sin esa bandera. Angular la sigue incluyendo por defecto, pero quitarla no rompe la compilación básica.

9. ¿Qué diferencia hay entre title = 'texto' y title = signal('texto')?

La primera es una propiedad normal — Angular no sabe cuándo cambia sin revisar todo el componente. La segunda es una signal: un valor reactivo que Angular puede "escuchar" para saber exactamente qué actualizar en el DOM, sin revisar el resto.

10. En app.html, ¿por qué se escribe {​{ title() }​} y no {​{ title }​}?

Porque title es una signal, no un valor plano — hay que "leerla" invocándola como función. Escribir {​{ title }​} mostraría la signal en sí (su representación interna), no el valor que contiene.

📎 Apuntes relacionados

➡️ Siguiente

Utility typesPartial, Pick, Omit, Record… transformar tipos existentes sin reescribirlos.