Language-tools: Mengetik Alat Peraga/Acara/Slot Komponen Langsing

Dibuat pada 11 Agu 2020  ·  24Komentar  ·  Sumber: sveltejs/language-tools

Sudah ada beberapa masalah tentang ini (#424, #304, #273, #263), tetapi saya ingin membuat yang terkonsolidasi untuk berdiskusi tentang pendekatan yang dapat kita ambil.
PR terkait: #437

Perhatikan bahwa masalah ini BUKAN tentang mengetik file d.ts , ini akan muncul setelahnya sebagai masalah terpisah.

Apakah permintaan fitur Anda terkait dengan masalah?
Saat ini input/output komponen tidak dapat diketik dalam kondisi tertentu:

  • Tidak mungkin untuk mendefinisikan hubungan umum antara alat peraga dan acara/slot (generik)
  • Tidak mungkin untuk mendefinisikan peristiwa dan jenisnya
  • Tidak mungkin untuk menentukan slot dan jenisnya dengan cara tertentu

Jelaskan solusi yang Anda inginkan
Cara mengetik props/events/slots secara eksplisit.

Proposal: Antarmuka baru yang dicadangkan ComponentDef yang, ketika didefinisikan, digunakan sebagai API publik komponen alih-alih menyimpulkan hal-hal dari kode.

Contoh (dengan komentar tentang apa yang mungkin tidak mungkin):

<script lang="ts"
   interface ComponentDef<T> { // <-- note that we can use generics as long as they are "defined" through props
      props: {  items: T[]  }
      events: {  itemClick: CustomEvent<T>  }
      slots: { default: { item: T } }
  }

   // generic type T is not usable here. Is that a good solution? Should it be possible?
   // Also: How do we make sure the types match the component definition?
  export items: any[];

   // ...

   // We cannot make sure that the dispatched event and its type are correct. Is that a problem?
   // Maybe enhance the type definitions in the core repo so createEventDispatcher accepts a record of eventname->type?
   dispatch('itemClick', item);
</script>
<!-- ... -->
<slot item={item}> <!-- again, we cannot make sure this actually matches the type definition -->

Ketika seseorang sekarang menggunakan komponen, dia akan mendapatkan tipe yang benar dari props/events/slots dan juga diagnosa kesalahan ketika ada sesuatu yang salah:

<Child
     items="{['a', 'string']}"
     on:itemClick="{event => event.detail === 1 /* ERROR, number not comparable to type string */}"
/>

Jika ini berhasil dan diuji sedikit, kami dapat meningkatkannya dengan antarmuka lain yang dicadangkan seperti ComponentEvents untuk hanya mengetikkan salah satu saja.

Saya ingin mendapatkan umpan balik sebanyak mungkin tentang ini karena ini kemungkinan merupakan perubahan besar yang dapat digunakan secara luas. Juga, saya ingin beberapa anggota tim inti untuk melihat ini jika ini terlihat bagus untuk mereka atau memperkenalkan sesuatu yang tidak mereka setujui.

@jasonlyu123 @orta @Conduitry @antony @pngwn

enhancement

Komentar yang paling membantu

Mungkin saya menggunakan istilah yang salah di sini... atau semoga saja saya melakukan kesalahan.

Contoh apa yang saya harap bisa saya lakukan:

```html


{option.myLabelProp}

{#setiap opsi sebagai opsi} {/setiap}

Semua 24 komentar

Tampaknya masuk akal, sebenarnya ada masalah Svelte tentang kemampuan untuk menyuntikkan slot saat runtime. https://github.com/sveltejs/svelte/issues/2588 dan PR yang mengimplementasikannya https://github.com/sveltejs/svelte/pull/4296 , terasa seperti mungkin ada tumpang tindih, atau setidaknya beberapa peluang untuk sejajarkan antarmuka (jika ada konsensus, masih ada beberapa pertanyaan yang belum terselesaikan dengan PR di atas).

Terima kasih untuk tautan PR, sepertinya itu hanya terkait dengan cara kami bahwa kami harus mengetikkan konstruktor sedikit berbeda, tapi saya pikir ini adalah indenden dari jenis yang memeriksa pada tingkat templat.

menarik.
Aku ingin tahu apakah kita bisa melakukan sesuatu seperti

<script lang="ts" generic="T"> 
    type T = unknown
    export let items: T[]
    let item:T = items[0]
</script>
<slot b={item}></slot>

yang akan menghapus type T = unknown selama typecheck dan sebagai gantinya menambahkannya sebagai argumen umum ke komponen.

//...
render<T>() {
    export let items: T[]
    let item:T = items[0]
}

Ide bagus tentang menambahkannya ke fungsi render !

Saya pikir kita bisa mendapatkan hasil yang sama dengan

<script lang="ts">
    interface ComponentDef<T> {
       ...
    } 
    type T = unknown
    export let items: T[]
    let item:T = items[0]
</script>
<slot b={item}></slot>

dengan mengekstrak T dari definisi antarmuka.

Meskipun dengan solusi Anda, Anda harus mengetik lebih sedikit dalam kasus "Saya hanya ingin hubungan umum antara alat peraga dan slot saya", yang bagus. Di satu sisi saya berpikir "ya kita bisa menambahkan keduanya", di sisi lain rasanya seperti terlalu banyak memperluas permukaan API (Anda dapat melakukan hal yang sama dengan cara yang berbeda) - tidak yakin.

Saya benar-benar tidak ingin harus menulis interface ComponentDef<T>{ props: {} } dan menyelaraskannya dengan masing-masing ekspor saya. Svelte melakukannya dengan sangat baik dalam mengurangi karakter yang diketik dibandingkan dengan React, dan ini terasa seperti langkah mundur. Secara khusus, ini membutuhkan penggandaan jenis setiap ekspor ke dalam alat peraga, yang tidak menyenangkan (dan pasti akan sering menimbulkan masalah).

Saya suka cara berpikir @halfnelson . Ekspor harus dideteksi sebagai alat peraga. Saya tidak tahu seperti apa modul itu sekali

Satu lagi cepat, ketika saya membacanya di beberapa masalah terkait: Saya tidak pernah mengalami kesulitan menggunakan komentar JSDoc untuk mengetik sesuatu, termasuk opsi @template .

Meskipun kita harus tetap berpikiran terbuka terhadap JSDoc (bahkan menggunakan JSDoc dalam HTML), saya harus memperingatkan bahwa, baik digunakan melalui WebStorm atau VS Code, itu sama sekali tidak ekspresif sebagai TypeScript. misalnya tidak mengimplementasikan tipe true ; Saya yakin itu tidak melakukan tipe indeks; dan jika saya ingat, Anda juga tidak dapat memiliki Record<keyof X, any> . Saya terus berlari ke dinding dengan itu. @template berhasil, tetapi juga sangat terbatas. Dan saya pikir itu bekerja dengan baik secara berbeda tergantung pada IDE juga.

Melewati tipe generik elemen (jsx) adalah hal yang tidak boleh dilakukan bagi saya karena itu bukan sintaks template langsing yang valid. Seharusnya juga tidak diperlukan karena obat generik didorong oleh properti, saya tidak bisa memikirkan cara untuk memperkenalkan ketergantungan generik hanya untuk slot/acara. Jika mereka didorong oleh properti input yang meneruskan generik ke elemen jsx tidak diperlukan karena kami dapat menghasilkan kode svelte2tsx sedemikian rupa sehingga TS akan menyimpulkannya untuk kami.

Tentang overhead pengetikan: Ini benar tetapi hanya akan diperlukan dalam situasi di mana Anda ingin menggunakan generik dan/atau mengetik acara/slot secara eksplisit. Secara teori, kami dapat menyimpulkan semua ini sendiri, tetapi ini terasa sangat sulit untuk diterapkan. Contoh masalah yang sulit diterapkan adalah dukungan untuk skenario slot lanjutan seperti pada #263, dan untuk mengumpulkan semua kemungkinan kejadian komponen (yang mudah jika pengguna hanya akan menggunakan createEventDispatcher , tetapi dia juga dapat mengimpor fungsi yang membungkus barang createEventDispatcher ).

Secara umum saya merasa bahwa ada banyak situasi di mana orang hanya ingin mendefinisikan salah satu dari hal-hal itu, tetapi tidak yang lain, jadi mungkin memang perlu untuk menyediakan semua opsi yang berbeda untuk menjaga semangat "mengetik lebih sedikit" dari Svelte.

Ini berarti:

  • ComponentDef adalah melakukan-semua-dalam-satu-jika-Anda-membutuhkannya
  • ComponentEvents hanya untuk mengetik acara
  • ComponentSlots hanya untuk slot mengetik
  • konstruksi seperti <script generic="T"> jika Anda hanya membutuhkan obat generik
  • kombinasi ComponentEvents/ComponentSlots/generic="T"

@shirakaba Dukungan tipe JSDoc tidak harus kaya fitur seperti TypeScript. Saya ragu banyak orang akan mengetik komponen generik dengannya. Juga karena kami menggunakan layanan bahasa TypeScript, banyak jenis lanjutan dapat dengan mudah diimpor dari file TypeScript.

Tentang pemeriksaan jenis alat peraga slot, saya memiliki beberapa peretasan tetapi tidak tahu apakah ini akan menghasilkan pengalaman pengembang yang baik. Jika pengguna mengetik komponen seperti ini:

interface ComponentDef {
      slots: { default: { item: string } }
  }

kita dapat menghasilkan kelas

class DefaultSlot extends Svelte2TsxComponent<ComponentDef['slots']['default']>{ }

dan ubah slot default menjadi

<DeafaultSlot />

(hanya menimpali sebagai pengguna) Bagi saya pengetikan ekstra tidak menjadi masalah karena saya harus melakukan banyak hal saat bekerja dengan TypeScript.

Adapun antarmuka vs prop generic , karena ada kasus di mana variabel hanya diekspos ke konsumen, akan lebih baik DX untuk mendukung prop. Tetapi pada catatan itu, apakah mungkin untuk mendukung export type T daripada generic="T" ?

Ini adalah sintaks TS yang tidak valid yang mungkin membingungkan orang. Selain itu, kita harus melakukan langkah transformasi tambahan, yang tidak akan dilakukan jika komponen Svelte berada dalam kondisi yang tidak dapat dikompilasi. Dalam hal ini alat bahasa kembali ke apa yang ada di skrip, yang kemudian akan memiliki sintaks yang tidak valid ini. Jadi saya menentang itu.

Saya mengerti. Tidak berlama-lama, tetapi apakah itu sintaks yang tidak valid atau lebih dari kesalahan "tipe tidak terdefinisi/tidak dikenal"? Saya bertanya karena saya tidak tahu bagaimana kompiler TypeScript menangani dua kasus. Saya hanya keberatan untuk menambahkan atribut khusus ke tag script , terutama dengan nama yang generik seperti "generik" :)

Secara keseluruhan, bagi saya, ini bagus untuk dimiliki dan mungkin memaksa pengguna untuk mengetik variabel yang diekspor saat menggunakan komponen adalah hal yang baik (kode yang lebih mudah dibaca).

Saat Anda mengetik export type T; , TS akan memunculkan kesalahan sintaks. Juga, bagaimana merumuskan kendala? export type T extends string; akan menimbulkan lebih banyak kesalahan sintaks pada Anda. Saya benar-benar memahami reservasi Anda terhadap atribut khusus, tetapi saya pikir itu cara yang paling tidak mengganggu. Cara lain adalah dengan memiliki nama tipe yang dicadangkan seperti T1 atau T2 tetapi itu tidak cukup fleksibel (bagaimana cara menambahkan batasan?).

Alternatifnya adalah mengetikkan seluruh komponen melalui ComponentDef , di mana Anda dapat dengan bebas menambahkan obat generik, seperti pada contoh di posting awal. Tapi ini datang dengan biaya lebih banyak mengetik.

Bagi saya menjadi jelas dari diskusi bahwa orang memiliki pendapat yang sangat berbeda tentang ini dan memiliki lebih banyak opsi yang ditata ( ComponentDef , alat peraga generik sebagai atribut, hanya mengetik ComponentEvents ) adalah yang paling fleksibel untuk membuat sebagian besar dari mereka bahagia, meskipun mereka memperkenalkan cara yang berbeda untuk melakukan hal yang sama.

Saya mungkin melewatkan ini tetapi apakah membaginya menjadi pilihan?

<script>
  import type { Foo } from './foo';
  export let items: Foo[];
  interface ComponentSlots<T> {}
  interface ComponentEvents<T> {}
</script>

Apakah itu akan mengurangi biaya pengetikan untuk sebagian besar kasus?

Ya ini akan mungkin.

@dummdidumm apakah mungkin untuk bekerja mendukung interface ComponentSlots {} (dengan atau tanpa dukungan untuk obat generik) untuk sementara? Atau akankah memperkenalkan itu bertentangan dengan hal-hal yang dibahas di sini?

Tidak, tetapi sebelum melanjutkan implementasi, pertama-tama saya ingin menulis RFC untuk mendapatkan lebih banyak umpan balik dari komunitas dan pengelola lain tentang solusi yang diusulkan. Setelah ada kesepakatan, kami akan mulai menerapkan hal-hal itu.

@joelmukuthu btw mengapa Anda ingin memiliki dukungan antarmuka? Kami meningkatkan inferensi tipe untuk slot, apakah ada kasus di mana itu masih tidak berhasil untuk Anda? Jika demikian, saya ingin tahu kasus Anda.

Saya sekarang membuat RFC tentang topik ini: https://github.com/sveltejs/rfcs/pull/38
Diskusi tentang API harus berlanjut di sana.

@dummdidumm maaf atas respon yang lambat. Saya baru menyadari bahwa untuk kasus penggunaan saya, saya benar-benar membutuhkan dukungan untuk obat generik. Ketik inferensi untuk slot bekerja dengan cukup baik!

Ini akan sangat besar! Saat ini saya sedih karena menggunakan properti slot selalu ada.

Itu aneh, slot yang diketik dapat disimpulkan dengan cukup baik pada saat ini, jika komponen yang Anda gunakan ada di dalam proyek Anda dan menggunakan TS juga.

Mungkin saya menggunakan istilah yang salah di sini... atau semoga saja saya melakukan kesalahan.

Contoh apa yang saya harap bisa saya lakukan:

```html


{option.myLabelProp}

{#setiap opsi sebagai opsi} {/setiap}

Ok saya mengerti, ya sekarang ini tidak mungkin, jadi Anda harus mundur ke any[] . Jika ini hanya memungkinkan string[] , slot Anda akan diketik sebagai string , yang saya maksud dengan "jenis dapat disimpulkan".

Solusi sementara yang bagus untuk mengetik komponen pengecekan yang saya temui di "Svelte and Sapper in Action" oleh Mark Volkmann adalah penggunaan pustaka tipe prop React bersama $$ props.

Ini adalah kode untuk item Todo misalnya:

import PropTypes from "prop-types/prop-types";

const propTypes = {
    text: PropTypes.string.isRequired,
    checked: PropTypes.bool,
};

// Interface for Todo items
export interface TodoInterface {
    id: number;
    text: string;
    checked: boolean;
}

PropTypes.checkPropTypes(propTypes, $$props, "prop", "Todo");

Sementara saya memvalidasi alat peraga Todo dan mendapatkan kesalahan jika ada yang tidak beres, saya juga ingin memiliki antarmuka untuk item Todo, sehingga saya dapat memiliki pemeriksaan tipe statis di VSCode serta pelengkapan otomatis. Ini dicapai dengan menggunakan antarmuka yang saya definisikan di atas. Kelemahan yang jelas dan signifikan dari ini adalah saya harus memiliki kode duplikat di sini. Saya tidak dapat mendefinisikan antarmuka dari objek propTypes, atau sebaliknya.

Saya masih memulai dalam javascript jadi tolong perbaiki saya jika ada yang salah.

Saya pikir pemeriksaan tipe harus dimiliki untuk basis kode apa pun yang produktif, baik pemeriksaan statis maupun pemeriksaan tipe alat peraga saat runtime.

(Perhatikan bahwa antarmuka akan didefinisikan dalam konteks = 'modul' sehingga dapat diekspor di samping ekspor default komponen, tetapi hilangkan bagian itu untuk singkatnya)

Sunting: Belum mencoba ini untuk apa pun selain validasi alat peraga, beri tahu saya jika itu juga berfungsi untuk slot/acara!

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

johanbissemattsson picture johanbissemattsson  ·  4Komentar

Kingwl picture Kingwl  ·  6Komentar

NickKaramoff picture NickKaramoff  ·  4Komentar

arxpoetica picture arxpoetica  ·  3Komentar

baileyherbert picture baileyherbert  ·  3Komentar