タイプ2型のタプルから最後の項目型を取得する


(注意:著者はtypescript 3 . xを使用していると仮定します).
おいあなた!最後の項目の型をタプルから取得しますか?
もちろん、あなたはします.
type Stuff = [number, boolean, string]
type OtherStuff = [string, number, object, boolean]
ここでは2つのタプルタイプ、3つ、1つの4つのアイテムがあります.最後のタイプStuff is string , と最後のタイプOtherStuff is boolean .
どのように、我々はそれらの最後のアイテムタイプを得ますか?
よく、驚くべきことに、1つの方法ができます-書き込み時にタプルの長さを知っている場合は、数値リテラルを使用して位置で型を検索するインデックスです.私は、驚くべき知っている.以下のように:
type LastStuff = Stuff[2] // => string
type LastOtherStuff = OtherStuff[3] // => boolean
Kinda通常の配列のルックアップのように!
しかし、あなたがタプルの長さを知らないならば、どうですか?うーんどのように我々は長さを教えて、私たちはコンパイルの時点ですべての最後の項目を選択するためにその長さを使用するように入力スクリプトを取得しますか?
からの借入this amazing HKTs library タプルの長さを数値リテラルとして得ることができました.
type GetLength<original extends any[]> = original extends { length: infer L } ? L : never

type Stuff = [number, boolean, string]
type OtherStuff = [string, number, object, boolean]

type LengthStuff = GetLength<Stuff> // => 3
注意infer この部分のキーワードoriginal extends { length: infer L } - 私は、何についてもっと話しますinfer キーワードが入っているので、混乱しているなら、少し光を当てることを望みます.
しかし、覚えておいてください、リストがゼロインデックスされるので、我々が最後のアイテムが欲しいならば、我々はする方法を必要としますL - 1 . TS (まだ)の型でストレート演算を行うことはできません.type LastItem = Stuff[GetLength<Stuff> - 1] ( TSの構文エラーです).それで、我々は若干の種類の地図を必要とするつもりです.
私が最も感じたアプローチは、数値リテラルから前の数値リテラルへの型マッピングです.私はこのようなタイプのthis SimplyTyped library , これは本質的に私たちを与えているn - 1 0から63までの間.これを使用して、入力することができますL そして、そのリストの最後のインデックスを取得し、最後のタイプを調べるために使用します.以下のように:
// Borrowed from SimplyTyped:
type Prev<T extends number> = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62][T];

// Actual, legit sorcery
// Borrowed from pelotom/hkts:
type GetLength<original extends any[]> = original extends { length: infer L } ? L : nevertype GetLast<original extends any[]> = original[Prev<GetLength<original>>]

// Here are our test-subject tuples:
type Stuff = [number, boolean, string]
type OtherStuff = [string, number, object, boolean]

// How long is the `Stuff` tuple?
type LengthStuff = GetLength<Stuff> // => 3

// What is the last element of each tuple?
type LastStuff = GetLast<Stuff> // => string
type LastOtherStuff = GetLast<OtherStuff> // => boolean
ブーム!それは完璧ではないが、これは私は最新のtypescriptバージョンでそれを作ることができるように良いです.

思考の後
私は実際に多くの、この作品を作る多くの異なることを試みた.私が働いた最初のバージョンは、1980年代から「Tupletable」を使いましたHKTs library 最後の項目の推論された型に戻るには、タプルの長さに基づいてルックアップを使用します.それはこの仕事のために少しoverkillを感じて、わずか10項目に限られていました.私が制限を増やしたいならば、そのタプルテーブルははるかに大きくなければなりません.
代わりに、タイプレベルの算術演算を行う方法を探しました.任意のタプルの長さがn , で指定されたタプルの最後の項目はn - 1 . 我々は、我々がJSの配列の最後のアイテムを検索しなければならなかった無数の時代からこれを知っています.典型的なスクリプトでは、数値的リテラル型で算術演算をネイティブに行うことはできません.したがって、この道を下げれば、数値リテラル間のマッピングを必要とします5 , 我々は戻って4 ). それは有限の長さでもあります、しかし、我々が最大1(例えば)で増加するならば、うまくコードの複雑さは増加しません.
githubを捜した後、私は正確に私が必要としたものを見つけました.The Prev 種類from SimplyTyped 私たちは数(どこから0から63まで)で渡すことができます、それは私たちにそれの前に番号を与える.したがって、5 , あなたは戻って4 . 私たちがビルトイン「後継者/前任者」タイプをtypescriptで得るまで、私はこれがそれと同じくらい良いと思います.
私は実際に私はHaskellとPurescriptで使用されているものと比較して、タイプスクリプトのタイプシステムの力に非常に驚いています.比較にはまだ多くの制限があります、そして、多分、常にあります、しかし、1つのものは確かです:typescriptはタイプされた言語のこの切られたのど世界でそれ自身を持つことができます.
次回まで!