Skrip ketikan mendapatkan tipe gabungan dari nilai tuple / array

94

Katakanlah saya punya daftar const list = ['a', 'b', 'c']

Apakah mungkin untuk diturunkan dari tipe serikat nilai ini 'a' | 'b' | 'c'?

Saya menginginkan ini karena saya ingin menentukan tipe yang hanya mengizinkan nilai dari array statis, dan juga perlu menghitung nilai-nilai ini saat runtime, jadi saya menggunakan array.

Contoh bagaimana itu dapat diimplementasikan dengan objek yang diindeks:

const indexed = {a: null, b: null, c: null}
const list = Object.keys(index)
type NeededUnionType = keyof typeof indexed

Saya ingin tahu apakah mungkin melakukannya tanpa menggunakan peta yang diindeks.

WARNA PUTIH
sumber
Jadi intinya, Anda ingin membuat tipe dengan cepat?
SwiftsNamesake
Jenis hanya ada saat mengompilasi, jadi Anda tidak dapat membuatnya secara dinamis saat runtime.
jonrsharpe
Ini pertanyaan yang menarik. Apa kasus penggunaan Anda?
unional

Jawaban:

199

UPDATE Feb 2019

Dalam TypeScript 3.4, yang akan dirilis pada Maret 2019 , dimungkinkan untuk memberi tahu kompiler untuk menyimpulkan jenis tupel literal sebagai tupel literal , alih-alih sebagai, katakanlah string[], dengan menggunakan as constsintaks . Jenis pernyataan ini menyebabkan compiler menyimpulkan jenis sesempit mungkin untuk suatu nilai, termasuk membuat semuanya readonly. Ini akan terlihat seperti ini:

const list = ['a', 'b', 'c'] as const; // TS3.4 syntax
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c';

Ini akan meniadakan kebutuhan akan fungsi pembantu apapun. Semoga berhasil lagi untuk semua!


UPDATE Juli 2018

Sepertinya, dimulai dengan TypeScript 3.0, TypeScript dapat menyimpulkan jenis tupel secara otomatis . Setelah dirilis, tuple()fungsi yang Anda butuhkan dapat ditulis secara ringkas sebagai:

export type Lit = string | number | boolean | undefined | null | void | {};
export const tuple = <T extends Lit[]>(...args: T) => args;

Dan kemudian Anda bisa menggunakannya seperti ini:

const list = tuple('a','b','c');  // type is ['a','b','c']
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c'

Harapan yang berhasil untuk orang-orang!


UPDATE Desember 2017

Sejak saya memposting jawaban ini, saya menemukan cara untuk menyimpulkan jenis tupel jika Anda ingin menambahkan fungsi ke perpustakaan Anda. Lihat fungsinya tuple()di tuple.ts . Dengan menggunakannya, Anda dapat menulis yang berikut dan tidak mengulangi diri Anda sendiri:

const list = tuple('a','b','c');  // type is ['a','b','c']
type NeededUnionType = typeof list[number]; // 'a'|'b'|'c'

Semoga berhasil!


ASLI Juli 2017

Satu masalah adalah literal ['a','b','c']akan disimpulkan sebagai tipe string[], sehingga sistem tipe akan melupakan nilai spesifiknya. Anda dapat memaksa sistem tipe untuk mengingat setiap nilai sebagai string literal:

const list = ['a' as 'a','b' as 'b','c' as 'c']; // infers as ('a'|'b'|'c')[]

Atau, mungkin lebih baik, tafsirkan daftar tersebut sebagai jenis tupel:

const list: ['a','b','c'] = ['a','b','c']; // tuple

Ini adalah pengulangan yang mengganggu, tetapi setidaknya itu tidak memperkenalkan objek asing pada waktu proses.

Sekarang Anda bisa mendapatkan penyatuan Anda seperti ini:

type NeededUnionType = typeof list[number];  // 'a'|'b'|'c'.

Semoga membantu.

jcalz.dll
sumber
Ini adalah solusi yang sangat baik untuk masalah yang sering saya temui, ketika saya membutuhkan pemeriksaan runtime (menggunakan tuple) dan mengkompilasi pemeriksaan waktu menggunakan tipe union. Adakah yang tahu jika ada upaya untuk menambahkan dukungan untuk pengetikan tupel implisit ke bahasa tersebut?
Jørgen Tvedt
Mencoba solusi Anda tetapi TIDAK berhasil dengancont xs = ['a','b','c']; const list = tuple(...xs);
Miguel Carvajal
1
Ini tidak seharusnya bekerja dengan itu, karena pada saat Anda melakukannya const xs = ['a','b','c'], kompilator telah melebar xske string[]dan sepenuhnya melupakan nilai-nilai spesifik. Saya tidak dapat membantu perilaku itu setidaknya pada TS3.2 (mungkin ada as constnotasi masa depan yang berfungsi). Pokoknya saya pikir jawabannya masih benar karena saya bisa (saya sebutkan di sana yang ['a','b','c']disimpulkan string[]) jadi saya tidak yakin apa lagi yang Anda butuhkan.
jcalz
3
dapatkah seseorang menjelaskan apa [number]artinya list[number]?
Orelus
3
Ini diuraikan sebagai (typeof list)[number]... tidak typeof (list[number]). Tipe T[K]adalah tipe pencarian yang mendapatkan tipe properti Tyang kuncinya adalah K. Di (typeof list)[number], Anda mendapatkan tipe properti dari (typeof list)siapa kuncinya number. Array seperti typeof listmemiliki tanda tangan indeks numerik , sehingga numberkuncinya menghasilkan gabungan dari semua properti yang diindeks secara numerik.
jcalz
15

Pembaruan untuk TypeScript 3.4:

Sintaks baru yang disebut "konteks const" yang akan tiba di TypeScript 3.4 akan memungkinkan solusi yang lebih sederhana yang tidak memerlukan panggilan fungsi seperti yang ditunjukkan. Fitur ini sedang ditinjau seperti yang terlihat di PR ini .

Singkatnya, sintaksis ini memungkinkan pembuatan array yang tidak dapat diubah yang memiliki tipe sempit (yaitu tipe, ['a', 'b', 'c']bukan ('a' | 'b' | 'c')[]atau string[]). Dengan cara ini, kita dapat membuat tipe gabungan dari literal semudah yang ditunjukkan di bawah ini:

const MY_VALUES = <const> ['a', 'b', 'c']
type MyType = typeof MY_VALUES[number]

Dalam sintaks alternatif:

const MY_VALUES = ['a', 'b', 'c'] as const
type MyType = typeof MY_VALUES[number]
ggradnig.dll
sumber
Hai, baru menemukan jawaban Anda setelah memposting pertanyaan ini. Saya akan membiarkan Anda mendapatkan karma jika Anda ingin menjawab;) stackoverflow.com/questions/56113411/…
Sebastien Lorber
2

Ini tidak mungkin dilakukan dengan Array.

Alasannya adalah, meskipun Anda mendeklarasikan variabel sebagai const, konten array masih bisa berubah, sehingga @jonrsharpe menyebutkan ini adalah runtime.

Mengingat apa yang Anda inginkan, mungkin lebih baik digunakan interfacedengan keyof:

interface X {
    a: string,
    b: string
}

type Y = keyof X  // Y: 'a' | 'b'

Atau enum:

enum X { a, b, c }
unional
sumber
Apakah ada cara untuk mengecualikan bidang tertentu? Misalnya, jika saya hanya menginginkan alapangan ..
neomib
Anda bisa menggunakannya Pick<T, U>untuk itu.
unional