Memetakan tipe tupel dalam tipe tupel

Dokumen untuk jenis tupel yang dipetakan menawarkan contoh ini:

type MapToPromise<T> = { [K in keyof T]: Promise<T[K]> };

type Coordinate = [number, number]

type PromiseCoordinate = MapToPromise<Coordinate>; // [Promise<number>, Promise<number>]

Namun bagaimana jika tupel asli saya berisi tupel lain, dan sebagai bagian dari pemetaan saya ingin mengambil nilai menarik dari tupel bagian dalam tersebut?

e.g.

// I just want the numbers, but there is other stuff in the type
type MessyCoordinate = [
    [string, number],
    [string, number]
]

Saya berharap saya bisa melakukan ini:

type Clean<T extends [string, number]> = T[1]

// Why doesn't this work?
type MapToClean<T> = { [K in keyof T]: Clean<T[K]> }

type CleanCoordinate = MapToClean<MessyCoordinate>; // [number, number]

Kompiler memberi saya kesalahan ini:

Type 'T[K]' does not satisfy the constraint '[string, number]'.
  Type 'T[keyof T]' is not assignable to type '[string, number]'.
    Type 'T[string] | T[number] | T[symbol]' is not assignable to type '[string, number]'.
      Type 'T[string]' is not assignable to type '[string, number]'.(2344)

Saya dapat mencoba menambahkan lebih banyak batasan pada MapToClean tetapi sepertinya tidak membantu:

type MapToClean2<T extends [string, number][]> = { [K in keyof T]: Clean<T[K]> }

Saya juga dapat menghilangkan tipe utilitas Clean dan hanya melakukan ini, tetapi tidak berhasil juga:

// Why doesn't this work?
type MapToClean<T> = { [K in keyof T]: T[K][1] }
// Type '1' cannot be used to index type 'T[K]'.

type CleanCoordinate = MapToClean<MessyCoordinate>; // [number, number]

Apakah harapan mendasar saya bahwa saya harus dapat membuka tupel di dalam pemetaan itu salah? Atau apakah saya hanya melewatkan sedikit sintaks ajaib agar T[K] terlihat seperti [string, number]?

Tautan taman bermain


person Matt Wynne    schedule 05.06.2020    source sumber


Jawaban (2)


Masalah yang jelas di sini adalah T di MapToClean<T> tidak dibatasi ke tipe yang propertinya [string, number]. Anda dapat menambahkan batasan yang sesuai dan kemudian akan berhasil:

type MapToClean<T extends { [K in keyof T]: [string, number] }> =
    { [K in keyof T]: Clean<T[K]> }

Masalah yang kurang jelas adalah mengapa batasan yang secara khusus memerlukan T sebagai tipe array masih akan gagal:

type MapToCleanOops<T extends [string, number][]> =
    { [K in keyof T]: Clean<T[K]> } // same error

Masalah Itu adalah bug yang luar biasa di TypeScript, microsoft/TypeScript#27995. Saat Anda memetakan tupel dengan tipe yang dipetakan, Anda akan mendapatkan tupel. Namun kompiler tidak menyadari hal itu di dalam definisi tipe yang dipetakan: lihat ini komentar. Untuk saat ini, solusinya adalah dengan melakukan sesuatu untuk memberi tahu kompiler bahwa properti tersebut akan menjadi tipe yang diharapkan; biasanya dengan Extract jenis utilitas:

type MapToCleanOkayAgain<T extends [string, number][]> =
    { [K in keyof T]: Clean<Extract<T[K], [string, number]>> } // okay again

type CleanCoordinate2 = MapToCleanOkayAgain<MessyCoordinate>; // [number, number]

Anda dapat menggunakan Extract untuk memperbaiki MapToClean asli Anda tanpa membatasi T:

type MapToCleanOkayUnconstrained<T> =
    { [K in keyof T]: Clean<Extract<T[K], [string, number]>> } // still okay

type CleanCoordinate3 = MapToCleanOkayUnconstrained<MessyCoordinate>; // [number, number]

Namun Anda mungkin menginginkan batasan itu atau batasan itu akan memungkinkan Anda meneruskan hal-hal aneh di:

type BeCareful = MapToCleanOkayUnconstrained<{ a: 1, b: ["", 4], c: false }>;
/* type BeCareful = { a: never; b: 4; c: never; } */

Terserah kamu.


Oke, semoga membantu; semoga beruntung!

Tautan taman bermain ke kode

person jcalz    schedule 06.06.2020
comment
Terima kasih, inilah yang saya butuhkan. - person Matt Wynne; 07.06.2020
comment
Saya punya pertanyaan lanjutan, tapi mungkin saya harus menanyakannya secara terpisah? Saya terkejut menemukan bahwa nilai tipe yang dipetakan ini tidak dapat diubah (harus memiliki metode '[Simbol.iterator]()' yang mengembalikan iterator.). Ini berfungsi di Playground, tetapi tidak di mesin lokal saya (menjalankan tsc 3.9.3). - person Matt Wynne; 07.06.2020

Jika Anda mengetahui sebelumnya bahwa Anda akan memetakan MessyCoordinate untuk mendapatkan number lalu mengapa Anda mempersulit tugas Anda?

type MessyCoordinate = [
    [string, number],
    [string, number]
]
type MapToClean<T> = { [K in keyof T]: number }
type CleanCoordinate = MapToClean<MessyCoordinate>; // [number, number]
person Rachid Oussanaa    schedule 06.06.2020
comment
Mungkin MapToClean<[["",1],["",2],["",3]]> seharusnya menghasilkan [1,2,3] dan bukan [number, number, number]? - person jcalz; 06.06.2020
comment
@jcalz tergantung apa yang OP inginkan, bukan itu yang saya pahami dari pertanyaannya - person Rachid Oussanaa; 06.06.2020
comment
jawaban saya menjawab kebutuhan spesifiknya, ini bukan jawaban umum untuk masalah yang dia ajukan - person Rachid Oussanaa; 06.06.2020
comment
Saya telah menyederhanakan masalahnya agar lebih mudah diikuti, maaf. Saya punya pertanyaan umum tentang kemampuan menarik item keluar dari tupel bagian dalam. Dalam kasus penggunaan saya, saya tidak akan mengetahui sebelumnya apa saja tipenya, dan tipenya akan berbeda satu sama lain. - person Matt Wynne; 06.06.2020