microsoft / microsoft/TypeScript

Cascade type narrowing to child constants

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

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

Awaiting More Feedback Suggestion
主要言語
Go
スター
111k
フォーク
14.4k
平均マージ
1日 19時間
マージ済み PR(30日)
117

説明

Suggestion

🔍 Search Terms

narrow child variable related

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Constants that were assigned a child value of another variable or constant should have their type narrowed alongside their parent.

📃 Motivating Example

Consider the following code:

type SomeUnion = (
  | {type: "string", values: readonly string[]}
  | {type: "number", values: readonly number[]}
  | {type: "date", values: readonly Date[]}
);

function someFunction(union:SomeUnion, index:number): void {
  const value = union.values[index];
  if (union.type === "string") {
    union.values; // readonly string[]
    value; // string | number | Date
  }
}

In someFunction, TypeScript keeps the type of value as it was originally assigned (string | number | Date).

What if dev expects value to have been narrowed to string?

This already happens with the root keys of an object

type AnotherUnion = (
    | {type: "string", sibling: string, foo: {bar: string}}
    | {type: "number", sibling: number, foo: {bar: number}}
    | {type: "date", sibling: Date, foo: {bar: Date}}
);

function manualDestructure(union: AnotherUnion): void {
  const {type, sibling, foo: {bar}} = union;
  if (type === "string") {
    sibling; // string ✔️ 
    bar; // string | number | Date 😿 
  }
}

function argumentDestructure({type, sibling, foo: {bar}}: AnotherUnion): void {
  if (type === "string") {
    sibling; // string ✔️ 
    bar; // string | number | Date 😿 
  }
}

This is observable in both examples.

💻 Use Cases

Use case: Working with discriminated unions holding nested objects and arrays

To achieve such narrowing, one currently must adjust coding style. ie:

function adjustedScope(union:SomeUnion, index:number): void {
  if (union.type === "string") {
    const value = union.values[index];
    value; // string
  } else if (union.type === "number") {
    const value = union.values[index]; // 😭 
    value; // number
  }
}

// or

function typeAssertion(union:SomeUnion, index:number): void {
  const value = union.values[index];
  if (union.type === "string") {
    (value as string); // string
  } else if (union.type === "number") {
    (value as number); // number 😭 & risky?
  }
}

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

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

はじめの一歩

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

調査の方向性

Issue の動機となっている TypeScript の例から始め、特にインデックス付きの子の値とネストされた分割代入のケースを確認します。判別共用体の絞り込みが子プロパティから派生した値にどのような影響を与えるべきかを判断し、その後、例が示されているとおり string、number、または Date に絞り込まれることを検証します。

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

評価

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

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

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