ค้นหาเว็บไซต์

typescript - การกำหนดนามแฝงฟังก์ชันทั่วไปด้วยประเภทเฉพาะ


สมมติว่าฉันมีฟังก์ชันทั่วไป b ฉันอยากจะสามารถทำสิ่งต่อไปนี้ได้:

const a = b<X, Y, Z>;

ดังนั้น a(p, q, r) === b<X, Y, Z>(p, q, r).

ฉันจะทำให้สำเร็จได้อย่างไร?

วิธีหนึ่งที่ฉันรู้คือการทำอะไรบางอย่างเช่น const a=<X, Y, Z>(...props) => b<X, Y, Z>(...props) แต่จากนั้นฉันก็ต้องระบุประเภทของ props เป็น iterable เป็นต้น และฉันหวังว่าจะมีวิธีที่ดูเรียบร้อยกว่านี้


โดยเฉพาะอย่างยิ่ง ฉันกำลังมองหาที่จะทำสิ่งต่อไปนี้:

import { createAsyncThunk } from '@reduxjs/toolkit';

const createSpecificThunk = createAsyncThunk<ResponseType, PropsType>;

// Then, `... = createAsyncThunk<ResponseType, PropsType>(...)`
// should equal `... = createSpecificThunk(...)`

ฉันตระหนักว่าในกรณีของฉัน ฉันสามารถทำสิ่งต่อไปนี้ได้

const createSpecificThunk = (actionType: string, callback: (props: X) => Y) => (
  createAsyncThunk<X, Y>(actionType, callback)
);

เพราะว่าประเภทแรกแทนค่าประเภทผลลัพธ์ และประเภทที่สองแทนค่าประเภทพร็อพส์! แต่ฉันยังสงสัยว่าข้างต้นสามารถทำได้กับเจนเนอริกหรือไม่

คำตอบ #1

ก่อน TypeScript 4.7 คุณสามารถระบุพารามิเตอร์ประเภทของฟังก์ชันทั่วไปได้ก็ต่อเมื่อคุณเรียกใช้ฟังก์ชันนั้นเท่านั้น:

declare function foo<T>(x: T): T[]; // generic function

let strArr = foo<string>("hello"); // specify T when calling
// const strArr: string[]
let numArr = foo<number>(123); // specify T when calling
// const numArr: number[]

และในขณะที่คอมไพเลอร์สามารถตรวจสอบได้ว่าฟังก์ชันเจเนอริกสามารถกำหนดค่าให้กับเวอร์ชันที่ไม่ใช่เจเนอริกได้เสมอเมื่อพารามิเตอร์ชนิดได้รับการระบุแล้ว:

let bar: (x: string) => string[] = foo; // okay
let baz: (x: number) => number[] = foo; // okay

คุณยังคงต้องทำสิ่งนี้ ด้วยตัวเอง ไม่มีวิธีใดที่จะบอกว่า "bar เหมือนกับ foo เมื่อคุณสร้าง T ด้วย string" ดังนั้นคุณจะต้องใช้วิธีฟังก์ชัน wrapper หรือวิธีแก้ปัญหาอื่น ๆ ที่ยุ่งยาก:

bar = (x: string) => foo(x); // wrapper, okay

แต่ TypeScript 4.7 จะนำเสนอ นิพจน์การสร้างอินสแตนซ์ ตามที่ได้ดำเนินการใน microsoft/TypeScript#47607, "ซึ่งให้ความสามารถในการระบุอาร์กิวเมนต์ประเภทสำหรับฟังก์ชันเจนเนอริกหรือคอนสตรัคเตอร์เจนเนอริกโดยไม่ต้องเรียกใช้งานจริง"

bar = foo<string>; // okay in TS4.7+
baz = foo<number>; // okay in TS4.7+

ดังนั้นเมื่อถึงเวลานั้น คุณจะสามารถเขียนโค้ดของคุณได้ตามที่ต้องการ:

const createSpecificThunk = createAsyncThunk<ResponseType, PropsType>;  // okay TS4.7+

/* const createSpecificThunk: (
    typePrefix: string, 
    payloadCreator: AsyncThunkPayloadCreator<ResponseType, PropsType, {}>, 
    options?: AsyncThunkOptions<PropsType, {}> | undefined
) => AsyncThunk<ResponseType, PropsType, {}> */

ลิงก์ Playground ไปยังโค้ด