microsoft / microsoft/TypeScript

Manually widen a type for conditional/mapped type

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

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

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

説明

Search Terms

manual widen

Suggestion

I would like some keyword or other mechanism to refer to the widened version of a type, e.g. widened 42 would be number and widened 'foo' would be string. Importantly, widened 42 & { foo: 'bar' } would be number & { foo: 'bar' }.

Use Cases

Our project uses a ton of branded primitives, which are accomplished by casting a number or string to number & Branding or string & Branding. We use this for all sorts of things: we have number & Pixels to indicate that a number is in pixels (and to force numbers passed to a function expecting pixels to use a number branded as pixels), we use string & Url to indicate a string is a URL, and so on.

We also have functions that operate on these types in a generic fashion, where we want to retain the branding. For example,

declare function plus<N extends number>(a: N, b: N): N;

This plus function is supposed to make sure we are adding pixels to pixels (or whatever unit), and retain the fact that their sum is also a number of pixels.

The problem comes in when we also have const offset = 5 as 5 & Pixels;. We define it as having that literal value for convenience (mostly, it shows up in Intellisense), and we have quite a few of these. More relevantly, the implementations of times and dividedBy (which I’m avoiding putting here as they are vastly more complex to cover canceling out units) can and should be able to take just plain numbers, but when I call times(offset, 2) it is inferred as times<Pixels, 2> and the return value retains the 2 even though obviously the runtime value is almost-certainly not going to be 2.

I asked for a solution to this on Stack Overflow, and was informed the only solution was to manually recreate the type by covering all of the potential brandings—which I basically have done, except that in the end there needs to be a generic overload, because we have a lot of situations where these functions are called by functions/classes that are also generic, and so only have N extends number, so the only overload available is the final one—which just retains whatever N was, even if N was a literal. I want to use widen N instead.

Examples

const c = 5 as const;
const w: widened typeof c = 3;

function plus<N extends number>(a: N, b: N): widened N {
    return a + b;
}
class Pixels { private '__ in': 'pixel'; }
const offset = 5 as 5 & Pixels;
declare const width: Pixels;

const offsetWidth = plus(width, offset); // Pixels, not 5 & Pixels

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, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

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

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

はじめの一歩

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

調査の方向性

ファイル、テスト、エントリポイントは指定されていません。まず、要求された widened の例と、既存の条件型およびマップ型の動作を調査し、その後、提案で交差型とブランディングをどのように保持すべきかを判断してください。例が widened の結果で型チェックを通り、出力される JavaScript を変更しないことが完了の条件です。

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

評価

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

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

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