microsoft / microsoft/TypeScript

Design Meeting Notes, 2026-09-01

Open
#64,129 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Design Notes
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

# Conditional Distributions Allow Constraint Violation

https://github.com/microsoft/TypeScript/issues/63708

```ts
type Issue =
A extends unknown ?
B extends unknown ?
Show :
never :
never;

type Show = [A, B] & {};

type X = Issue<0 | 1, 0 | 1>;
// ^?
```

* The problem is that when we distribute on `A` and `B`, then we should create a new type parameter for each one with an identical name.
* So we are prototyping this.
* This is a little complicated by the fact that this now also becomes a declaration site rather than just a usage.
* We would need to introduce something in the binder for this.
* So that would say "this is a distribution site with a new type variable".
* But that would have broader implications beyond just type variables on the left side of `extends`.
* Doing something like this would changing the semantics of type distribution because we'd now operate over any identifier-named type.

```ts
// TODO
```
* When explaining to others, we've explained this as "there's a new type variable that is created for distribution", so surprised it didn't work this way.
* Really there's a mix of substitution types and other instantiation mechanics at play here.
* Do we need substitution types?
* Yes. You need "learned information"/"conditional narrowing" to be tracked along to satisfy other constraints in true branches.
* Wait really? Why can't `T extends number` become `T' extends T & number`?
* How does this work over type references that alias bare identifiers

```ts
type Foo = T;

type Blah = Foo extends any ? { b: Foo } : undefined;

type What = Blah<1 | 0>;
```
* Today, this *does* distribute; it no longer would with the suggested change...
* Oh... that's probably very breaky!
* Who does this?
* Someone, guaranteed.
* Feels like people should at least have a way to distribute explicitly since we might be changing things here?
* You are binding a new type parameter at the top; there's no reason you couldn't other than efficiency.
* No - we use substitution types to track learned information and conditional narrowing.
* There are differences in type comparisons when you have substitution types, and they differ in how they act on type comparisons.
* So while they feel similar, type parameters with added constraints are treated differently in practice.
* `T'' extends T' & (1 | 2) extends T & number` isn't reasoned about quite the same as `T'' & (1 | 2) & number`.
* Why don't we just introduce syntax to avoid breaking people?
* The problem is that we are trying to fix a soundness hole and we want that to be fixed by default.
* Prototype will tell us what breaks in top 999.

# API Bikeshed

```ts
import { version, versionMajorMinor } from "typescript";
import * as ts from "typescript/async"; // or sync
import * as ast from "typescript/ast";
```

* Currently there's a bunch of subpath exports in the `typescript` package.
* `ast`
* `factory`
* `is`
* ...
* As we prototyped replacements with the new API, we noticed old API usage was annoying to go through different imports.
* Feedback from early API users is that it's less overwhelming than having one import with everything.
* One of the things we don't like with the "siloing" is that it kind of draws boundaries that we might be fixing ourselves into.
* Can't really have a single barrel given the sync/async split.
* If you want a single barrel, you can do it yourself!

```ts
// ./src/typescript.ts

// Single barrel export for convenience
export * from "typescript";
export * from "typescript/ast";
export * from "typescript/async";
```

```json5
// package.json
{
// ...
"imports": {
"#ts": "./dist/typescript.js"
}
}
```

```ts
// Other usage
import * as ts from "#ts";
```
* What about identical types imported through different paths?
* e.g. `ModuleKind` comes from both `"typescript"` and `"typescript/async"`.
* What do people want from this?
* Do the names overlap between sync/async modules?
* Yes, they have identical names.
* It'd be annoying if they all had an `Async` postifx.
* Could we just simplify this all into `typescript/async` and `typescript/sync`, and re-export common stuff from both?
* What do you do if you have a function that acts only on the common stuff?
* Any AST walker.
* Aside: do we have `forEachChildAsync`?
* Currently a problem because `forEachChild` short-circuits on truthy results. `Promise`s are always truthy, so a walk always exits early.
* If we had the FIFO prototype resurrected (come back to this @JakeBailey?), would people even want async because the context switching is so high?
* Async is just painful
* But it's the only way to *easily* do things concurrently.
* Wonder how easy it is to use workers and just use the sync layer.
* Out of time - really need to build some prototypes and get feedback from others.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked TypeScript issue 63708 and the conditional-distribution examples, then review the notes on binder changes, substitution types, and compatibility. Separately, examine the proposed sync/async and AST entry points and the top-999 prototype plan. There is no defined implementation scope or completion criterion yet; prototypes and external feedback are the stated next steps.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.