Skip to content

feat(target-angular): emit cross-file types and generics - #78

Merged
alexgrozav merged 3 commits into
milestone-m5-uxf-443from
angular-types-generics-uxf-465
Oct 29, 2026
Merged

alexgrozav merged 3 commits into
milestone-m5-uxf-443from
angular-types-generics-uxf-465

Conversation

@alexgrozav

@alexgrozav alexgrozav commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

What changed

Cross-file types and generic components on Angular (ADR-0067, ADR-0068). Only Angular's output changes.

  • Angular: import type by name where the output reads the type: ./types or a package as written, and a child's types from its output (import Field, { type FieldProps } from "./field"). The output of a module's main component writes the module's type exports in source order among the types it declares. A member another declaration declares (inherited, intersected, picked or imported) is read from the props type by indexed access, input.required<BadgeProps["label"]>(), and with a default input<Exclude<BadgeProps["tone"], undefined>, BadgeProps["tone"]>(…). A generic component is a generic class, export default class Table<T extends { id: string; label: string }>, whose type parameters ngtsc infers from a consumer's inputs. A slot directive can't infer them, so a scoped slot's context reads each one as its constraint, or any without one (generic-slot-props, UF4004). A select's model of a state of a type parameter takes the chosen value through the template's $any(…), since a template writes no as.
  • Cases: Angular's goldens for types/ (8), generics/ (4) and diagnostics/generic-slot-props, with Angular consumer fixtures for the 12 rendering cases, including the type and generic misuses. No shared expectation changes.
  • Quarantine: Angular's list is empty. With its L3 entries gone, the browser stand-in no longer applies, so the 8 specs it served load Angular's real output, and their browser entries are gone too. The generic-slot-props gap is declared in CONSUMER_GAPS. Angular's test helpers read these cases again.
  • Fix round: a seeded ?? or ! over a member read by indexed access now reads it as NonNullable<…>, so NonNullable<BadgeProps["note"]> | string, which ngtsc accepts. Emit tests cover that with Angular's own check, the parentheses an inline props type takes, and the types a type parameter's constraint and default name.
  • Harness: the canary injector finds the body of a generic component class whose constraint holds braces (class Table<T extends { id: string }>), with a unit test.

Parity matrix

  • Every project, from a checkout whose path holds no .ts: 8829 passed, 242 skipped, 0 failed. The matrix is green, every project ran, no missing cell and no stale entry. Every Angular cell of the 13 new cases passes, or skips where every target does (L9 without a scripted interaction, a diagnostics case's browser layers).
  • pnpm test:update leaves the tree clean.
  • pnpm test:canaries: every canary caught on every case and target it corrupts, over two runs: L1 to L9 on the first commit, before a fix to the slot-context helper, and the six L10 to L13 canaries on the first pushed head. The fix commit after it changes no harness file and no golden output.

Checklist

  • The title follows Conventional Commits, as in feat(web): sell the next framework on the homepage.
  • pnpm format:check, pnpm lint, pnpm check-types and pnpm test pass. (The first three pass. In place of pnpm test: Angular's package tests, the repo tests, and the whole integration suite as above.)
  • Changes to a published package: a changeset from pnpm changeset (see .changeset/README.md).
  • Homepage changes: pnpm build, then pnpm --filter web lighthouse, still scores at least 0.95 in every category.

Angular's types lane (ADR-0067, ADR-0068). An output imports the types
its source imports, by name, and its main component's file writes the
module's type exports. A member another declaration declares is read
from the props type by indexed access, `input<ButtonProps["tone"]>()`.
A generic component is a generic class, and a scoped slot's context
reads each type parameter as its constraint, or `any` without one, as
ngtsc types a generic context it cannot infer (UF4004).

The types and generics cases leave Angular's quarantine, with their
consumer fixtures, the generic-slot-props gap, and the browser stand-in
retired for the eight cases it served.

Co-authored-by: multica-agent <github@multica.ai>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://angular-types-generics-uxf-465-unframework.blue-water-0397.workers.dev (commit 90ba4aa)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://2380f055-unframework.blue-water-0397.workers.dev 90ba4aa 2026-10-29T16:08:59.237Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://50c2c6de-unframework.blue-water-0397.workers.dev 361a164 2026-10-29T14:53:07.522Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://936d0d7e-unframework.blue-water-0397.workers.dev e3d229f 2026-10-29T08:37:26.515Z Visit the dashboard ↗

alexgrozav and others added 2 commits October 29, 2026 16:46
…them

A seeded `??` or `!` over a member read from the props type by indexed
access, `BadgeProps["note"]`, left its `undefined` in: the access is
opaque to the type's text, so it now reads as `NonNullable<…>`, as an
alias that admits a nullish value already does.

Emit tests cover the narrowing with Angular's own check, the
parentheses an inline props type takes, and the types a type
parameter's constraint and default name. The test helper reads
`types/exported-types` again, which imports nothing.

Co-authored-by: multica-agent <github@multica.ai>
…ar-types-generics-uxf-465

# Conflicts:
#	tests/integration/harness/canaries.ts
@alexgrozav
alexgrozav merged commit f86908e into milestone-m5-uxf-443 Oct 29, 2026
49 checks passed
@alexgrozav
alexgrozav deleted the angular-types-generics-uxf-465 branch October 29, 2026 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant