# Problems with TypeScript, and how EX Script solves each

TypeScript succeeded because it added static types to JavaScript without changing
runtime semantics. But a decade of real-world use exposed deep problems. EX Script
addresses each one directly.

## 1. Confusing compiler errors

**TypeScript:**
```
error TS2322: Type 'string | undefined' is not assignable to type 'string'.
```

**The problem:** The message names types, not *problems*. It doesn't say where the
`undefined` came from, whether it's a bug or a design question, or how to fix it.

**EX Script** treats the diagnostic as the product. Every error has four parts:
what happened, the code frame, why it happened, and concrete fixes with examples:

```
error[E1007]: `username` may be missing here
  ┌─ src/main.xan:12:14
  │
12 │     greet(username)
  │          ^^^^^^^^ `username` has type `String?`
  │
  You're passing `username` to `greet`, which requires a guaranteed `String`.

  Possible fixes:
  1. Check that it exists first:
       if let name = username { greet(name) }
  2. Provide a fallback:
       greet(username or "Guest")
  3. Change `greet` to accept an optional value:
       fun greet(name: String?)
```

## 2. Excessive configuration

**TypeScript:** tsconfig with 30+ options that everyone copies verbatim and never
understands; separate configs for the bundler, the linter, the formatter, and the
test runner — each with its own plugin ecosystem and version churn.

**EX Script:** zero configuration. `ex init` creates a project with sensible defaults
that cover 95% of real projects. There is no config file at all in the common case.
Advanced options live in a single optional `project.xan` manifest. One toolchain does
everything, so there is nothing to wire together.

## 3. The type system fights you

**TypeScript:** variance annotations, conditional types, mapped types, template
literal types, overloads, declaration merging, `as const`, `satisfies`... each added
to patch a hole left by a previous feature.

**EX Script:** structural typing with inference, discriminated unions with exhaustive
matching, and a small set of utility types. If the compiler can infer it, you don't
write it. If a program type-checks in EX Script, the types describe what the program
actually does.

## 4. Slow type checking in large projects

**TypeScript:** the checker walks huge declaration files; incremental mode helps but
the design carries a persistent cost.

**EX Script:** modules are small by construction (each `.xan` file is a module), the
type system is simpler than TS's, and incremental builds cache checked modules. No
`node_modules`-sized type forests: packages ship compiled `.xan` artifacts plus a
compact type table, not megabytes of `.d.ts`.

## 5. Declaration file complexity

**TypeScript:** `.d.ts` files are a parallel universe — you write the implementation,
then write types for it again, by hand, for every package.

**EX Script:** types are part of the source. Packages publish a generated, compact
type table derived from the checked source. There is nothing separate to maintain.

## 6. Awkward null/undefined handling

**TypeScript:** `null`, `undefined`, `void`, optional chaining, non-null assertions,
`strictNullChecks` being off by default...

**EX Script:** `null` does not exist. A value is either present or `T?` (optional).
The compiler tracks optionality through the entire program. `.require("message")`
and `or` give you checked and defaulted access. The billion-dollar mistake is
removed, not managed.

## 7. Painful generics

**TypeScript:** generic constraints, default type parameters, inference edge cases,
`infer` gymnastics.

**EX Script:** generics are syntax-light — `fun first<T>(items: List<T>) -> T?`.
Inference is expected-argument-driven and rarely needs explicit arguments.
No variance annotations: structural subtyping makes them unnecessary.

## 8. Inconsistent JavaScript interop

**TypeScript:** `esModuleInterop`, `allowJs`, `checkJs`, `types`, `typeRoots`,
`paths`... and still runtime mismatches.

**EX Script:** importing JS is a first-class, uniform operation. JS values enter the
program as `Any` (dynamically typed) and must be validated with `schema` or narrowed
with a type test before use — closing the gap between compile-time assumptions and
runtime reality.

## 9. Runtime behavior differs from compile-time assumptions

**TypeScript:** `as` lies. `any` propagates lies. A package's `.d.ts` can promise
anything; the runtime may deliver something else.

**EX Script:** type assertions exist but are explicit and rare (`as` is reserved for
`Any`-boundary narrowing). Untrusted data goes through `schema` validators that are
generated from the same source as the static type. Compile-time and runtime
guarantees are clearly separated (see the Runtime Model doc).

## 10. Poor runtime validation

**TypeScript:** no runtime validation at all — hence zod, yup, ajv, io-ts, and
their ecosystem of duplicated, drifting type/validator pairs.

**EX Script:** `schema` declarations produce the static type *and* the runtime
validator from one source of truth. `Config.parse(json)`, `User.from(apiResponse)` —
typed in, validated out.

## 11. Excessive boilerplate

**TypeScript:** `interface` + `as const` + `satisfies` + a validator library to do
what should be one declaration.

**EX Script:** `schema User { name: String, age: Int (min: 0) }` is a type, a
validator, and a parse function in one line.

## 12. Complicated module configuration

**TypeScript:** `moduleResolution` × `module` × `baseUrl` × `paths` × bundler quirks.

**EX Script:** `import` is a path or a package name. That's all. The compiler
resolves it.

## 13. Difficult monorepo setups

**TypeScript:** project references, composite builds, three tsconfigs per package...

**EX Script:** workspaces are a single line in the manifest: `workspaces = ["packages/*"]`.
Dependency resolution, lockfiles, and incremental builds handle the rest.

## 14. Confusing async error handling

**TypeScript:** async/await + `throw` means exceptions can escape in unreadable ways;
unhandled rejections crash servers at 3am; `try/catch` around every await.

**EX Script:** `!` functions make failure part of the type. `?` propagates it
readably. `try/catch` remains for the JS boundary only. Structured combinators
(`all`, `race`, `timeout`) manage concurrency without nested promise gymnastics.

## 15. Type assertions hide bugs

**TypeScript:** `as string` on `string | undefined` is a lie that the compiler
celebrates.

**EX Script:** there is no blanket assertion. `.require("message")` checks at
runtime and produces a *good* error when wrong; `schema` validates untrusted data;
`as` only narrows `Any` to a checked type. Bugs surface, loudly, at the point of
the lie.

## 16. Ecosystem/version compatibility problems

**TypeScript:** "Cannot find module or its corresponding type declarations" — the
most-feared error in the ecosystem, caused by types living in a parallel package
(`@types/foo`) that can drift from `foo`.

**EX Script:** JS packages declare their own optional type table on publish. No
`@types` parallel universe. The compiler tells you exactly what to do when a package
lacks types (validate at the boundary with a schema).

## The summary table

| Problem | TypeScript | EX Script |
|---|---|---|
| Error messages | cryptic type names | what/why/fix with code frames |
| Config | tsconfig + 4 tools | zero config, one binary |
| `null`/`undefined` | both, everywhere | absent; `T?` only |
| Runtime validation | zod et al., drifting | `schema` = type + validator |
| `.d.ts` | hand-written parallel universe | generated type tables |
| Async errors | throw + try/catch | `T!` + `?` propagation |
| Monorepos | project references | workspaces manifest line |
| Boilerplate | high | minimal |