microsoft / microsoft/TypeScript

Suggestion: Distributive code inference "as dist"

オープン
#62,631 コメント 3 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

Awaiting More Feedback Suggestion
主要言語
Go
スター
111k
フォーク
14.3k
平均マージ
2日 4時間
マージ済み PR(30日)
132

説明

🔍 Search Terms
  • distributive inference
  • distributed function
  • as dist
  • as distributed
✅ Viability Checklist
⭐ Suggestion

Add dist / distributed keyword to enable inference in a distributive manner.

Concept 1: Inline as dist

Handle the inference by distributing over all types used by the expression:

const transform = (val: [1, 1] | [2, 2]) => [val[0], val[1]] as dist;
typeof transform; // (val: [1, 1] | [2, 2]) => [1, 1] | [2, 2]

//

const add = (...[a, b]: [number, number] | [bigint, bigint]) => a + b as dist;
typeof add; // (...[a, b]: [number, number] | [bigint, bigint]) => number | bigint
Concept 2: Function-level dist return type

Allow distribution across an entire function body:

const f = <T extends [number, number] | [bigint, bigint]>(...[a, b]: T): dist => {
    const sum = a + b; // number | bigint
    const arr = [a, b]; // [number, number] | [bigint, bigint];
    return {
        sum,
        arr
    } // {sum: number, arr: [number, number]} | {sum: bigint, arr: [bigint, bigint]}
}

const res = f(3,7); // {sum: number, arr: [number, number]}
const rez = f(3n,7n); // {sum: bigint, arr: [bigint, bigint]}

typeof f; // (<T extends [number, number]>(...[a, b]: T) => { sum: number; arr: [number, number]; }) & (<T extends [bigint, bigint]>(...[a, b]: T) => { sum: bigint; arr: [bigint, bigint]; })

Concept 1 could also be supported on function expressions with equivalent behavior to Concept 2:

const f2 = (<T extends [number, number] | [bigint, bigint]>(...[a, b]: T) => {/*code*/}) as dist; // Equivalent to "f"
Notes

These are early-stage concepts meant to illustrate the core idea. The specific syntax and behaviors should be refined during implementation.

Open questions: Interaction with as const, generics, nested functions, opt-out mechanisms, etc. will need consideration.
Potential breaking change: Codebases with a type called dist would conflict, though this would go against TypeScript's PascalCase naming convention for types.

📃 Motivating Example

Currently, code is inferred in a non-distributive manner:

Example inspired by geon

const transform = (val: [1, 1] | [2, 2]) => [val[0], val[1]] as const;
// Inferred return type: readonly [1 | 2, 1 | 2]
// Expected return type: readonly [1, 1] | readonly [2, 2]

This can be addressed by duplicating your code logic within a distributive context in the typesystem:

const transform = <T extends [1, 1] | [2, 2]>(val: T) => [val[0], val[1]] as T extends unknown
    ? [T[0], T[1]]
    : never
;

const res = transform(undefined! as [1, 1] | [2, 2]); // [1, 1] | [2, 2]
const rez = transform([1, 1]); // [1, 1]

But this is very repetitive and awkward. Besides that, this only works if the automatic inference doesn't cause a conflict to begin with and can be simply translated into the type system:

Example inspired by Emilio Platzer and Mudloop

// @ts-expect-error: Operator '+' cannot be applied to types 'number | bigint' and 'number | bigint'.(2365)
const add = (...[a, b]: [number, number] | [bigint, bigint]) => a + b;
💻 Use Cases
  1. What do you want to use this for?
    Preserving precise type relationships when transforming union types, and enabling operations across union branches that are currently rejected by the type checker.
  2. What shortcomings exist with current approaches?
    Current workarounds require manually duplicating code logic in the type system using conditional types. This is verbose, error-prone, and fails when TypeScript rejects the runtime-aligned code itself
  3. What workarounds are you using in the meantime?
    Manually encoding distributive logic using conditional types

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

実装ファイル、テスト、またはコンパイラのエントリーポイントは特定されていません。まず、提案されている2つの構文、動機となった例、ジェネリクス、ネストした関数、オプトアウトに関する未解決の問題を確認してください。実装のスコープを決める前に設計を確定させる必要があり、それが完了条件となります。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
typescript
領域
compilers
issue の種類
機能追加
難易度
5/5
見積もり時間
1週間以上
活発さ
停滞
明瞭さ
説明が足りない
初心者へのやさしさ
20/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。