TypeScript Interface Function Field Contravariance Type Error
Saya memiliki tipe berikut, yang menetapkan bahwa semua properti akan menjadi fungsi, yang tidak menerima argumen, atau satu argumen tipe Record<string, any>:
type FnTrait = Record<
string,
(input?: Record<string, any>) => any
>;
Saya mencoba memperluas tipe ini ke antarmuka lain (yang saya ingin memiliki batasan yang sama).
interface HasFn extends FnTrait {
someFn(): string; // no problem
anotherFn(o: {id: string}): number; // ts error 2411
}
Ini menghasilkan kesalahan pada anotherFn:Property 'anotherFn' of type '(o: { id: string; }) => number' is not assignable to string index type '(input?: Record<string | number | symbol, any> | undefined) => any'.
Mengapa tidak someFnmenghasilkan kesalahan, sementara anotherFnmenghasilkan kesalahan ts 2411? Tampaknya penyempitan ini harus dibiarkan.
Bantuan apa pun akan sangat dihargai. Terima kasih!
Jawaban
Ini adalah contoh tipe fungsi yang kontravarian dalam parameternya . Kata "contravariant" berarti "berbeda-beda". Jika Anda membuat parameter fungsi lebih spesifik (sempit), Anda membuat jenis fungsi itu sendiri lebih umum (lebar). Itu berarti alih-alih membuat HasFnsebuah subtipe dari FnTrait(yang adalah apa extendsartinya), Anda semacam membuatnya menjadi supertype . Ini tidak diperbolehkan, dan melanggar prinsip substitusi .
Secara khusus, anotherFn()berada dalam kesalahan karena mengharuskan argumennya memiliki properti string-valued id, sedangkan FnTrait["anotherFn"]tidak. Diharapkan bahwa Anda dapat memanggil properti apa pun dari a FnTraittanpa parameter, atau dengan parameter tunggal dari hampir semua tipe. Tetapi a HasFnmungkin meledak jika Anda memanggil anotherFn()metodenya tanpa parameter, atau dengan parameter yang kehilangan jenis idproperti yang tepat. Oleh karena itu, seperti yang didefinisikan, HasFntidak dapat dialihkan ke FnTrait, meskipun dideklarasikan untuk memperpanjangnya:
const hasFn: HasFn = {
someFn: () => "",
anotherFn: o => o.id.length
}
const fnTrait: FnTrait = hasFn;
fnTrait.anotherFn({ a: 123 }); // okay at compile time, explodes at runtime
Karena anotherFn()berarti Anda tidak dapat dengan aman mengganti FnTraitnilai di mana HasFnnilai diminta, FnTraitgagal ditetapkan HasFn, dan Anda mendapatkan kesalahan.
Alasan mengapa someFn()adalah tidak dalam kesalahan ini karena fungsi dari parameter yang lebih sedikit adalah dialihkan ke fungsi yang mengambil lebih parameter . Ini karena someFn()akan, dengan kebutuhan, mengabaikan parameter apa pun yang diteruskan ke dalamnya, jadi aman untuk memperlakukannya sebagai fungsi yang mungkin menerima parameter:
fnTrait.someFn({ a: 123 }); // okay at compile time and runtime
Ini bekerja untuk alasan yang sama anotherFn()gagal: substitusi.
Tautan taman bermain ke kode