microsoft / microsoft/TypeScript
Suggesion: Allow recursive/self-references in `satisfies` constraint
まだ誰も着手していません。
- 主要言語
- Go
- スター
- 111k
- フォーク
- 14.4k
- 平均マージ
- 1日 19時間
- マージ済み PR(30日)
- 117
説明
Suggestion
🔍 Search Terms
satisfies keyword, recursion, recursive, constraint, self referential
✅ Viability Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
It would only make previously rejected type assertions possible as it only widens the set of valid type restrictions.
- This wouldn't change the runtime behavior of existing JavaScript code
satisfiesonly exists during type checking and does not in generated JavaScript - This could be implemented without emitting different JS based on the types of the expressions
As above
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
As above
- This feature would agree with the rest of TypeScript's Design Goals.
The same is already possible using a constrained identity function
⭐ Suggestion
It is currently not possible to reference the type of a variable that the satisfies constraint is applied on. Enabling this functionality should not be hard, as the same behavior can be recreated using a constrained identity function, see below. This would enable more precise verification of structures by the type checker.
📃 Motivating Example
// I define a (very simplified) state machine by the following type:
type States<S extends string> = Record<S, S>;
// I can then define a map of machine names to their states records:
const myMachines1 = {
alan: {
a: "b",
b: "c",
c: "c",
},
turing: {
x: "y",
y: "x",
},
} satisfies Record<string, States<string>>;
// Problem: This is possible, though should be invalid:
const myMachines2 = {
foo: {
a: "x",
},
bar: {
x: "a",
}
} satisfies Record<string, States<string>>;
// Solution 1: Identity function with self-referential generic:
function defineMachines<T extends {
[name in keyof T]: States<keyof T[name] & string>;
}>(machines: T) {
return machines;
}
// Valid:
const myMachines3 = defineMachines({
alan: {
a: "b",
b: "c",
c: "c",
},
turing: {
x: "y",
y: "x",
},
});
// Errors:
const myMachines4 = defineMachines({
foo: {
a: "x",
},
bar: {
x: "a",
}
});
// Solution 2: The same behavior should be achievable using the new `satisfies` keyword:
const myMachines5 = {
foo: {
a: "x",
},
bar: {
x: "a",
}
} satisfies {
[name in keyof typeof myMachines5]: States<keyof typeof myMachines5[name] & string>;
};
// But errors: Block-scoped variable 'myMachines5' used before its declaration.
💻 Use Cases
As seen in the example above, this allows for a more precise control over (nested) type restrictions. It is already possible to self-reference type definitions, so why shouldn't this also be possible for variable declarations? The satisfies keyword does not modify the type itself to my knowledge.
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
動機となる satisfies の例から始め、拒否された自己参照と、制約付き恒等関数による解決策を比較します。変数宣言に対して再帰的な制約がどのように振る舞うべきかを判断し、そのうえで、有効なステートマシン・マップが受け入れられ、無効な相互参照がランタイム出力を変更せずに拒否されることを検証します。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- typescript
- 領域
- compilers
- issue の種類
- 機能追加
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 活発さ
- 停滞
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 30/100