วิธีส่งอาร์กิวเมนต์ประเภทไปยังประเภทที่มีดัชนีใน Typescript อย่างไร?
ฉันกำลังพยายามหาวิธีที่จะได้ประเภทผลลัพธ์ของฟังก์ชันทั่วไปในคลาส
ตัวอย่างเช่น,
declare class Test {
open<T, R>(t1: T, t2: R): T | R;
}
type TestType<T, R> = (Test['open'])<T, R>;
type TestTypeReturn = ReturnType<TestType<number, number>>;
อย่างไรก็ตาม ฉันได้รับข้อผิดพลาดที่บรรทัดที่ 4 ที่ไม่ว่างประมาณที่ ...])<T, R> บอกว่าคาดว่าจะมีเครื่องหมายอัฒภาค ฉันไม่แน่ใจว่าสิ่งที่ฉันต้องการทำเป็นไปได้หรือไม่ มีใครรู้ไหม?
คำตอบ #1
สมมติว่าสำหรับตัวอย่างเฉพาะที่แสดง คุณสามารถเขียนประเภทที่ต้องการด้วยตนเองได้ เช่น:
type TestTypeManual<T, R> = (t1: T, t2: R) => T | R;
type TestTypeReturnManual = ReturnType<TestTypeManual<number, number>>; // number
แต่คุณต้องการให้ตัวคอมไพเลอร์ทำสิ่งนี้ให้คุณ
ระบบชนิดของ TypeScript ขาดความสามารถในการแสดงความสัมพันธ์ระหว่างอาลิอัสชนิดคอนกรีตที่อ้างถึงฟังก์ชันเจเนอริก เช่น type GenFunc=<T>(x: T)=>T กับอาลิอัสชนิดเจเนอริกที่อ้างถึงฟังก์ชันคอนกรีต เช่น type GenAlias<T>=(x:T)=>T คุณไม่สามารถระบุพารามิเตอร์ชนิดของฟังก์ชันเจเนอริกโดยไม่เรียกใช้ฟังก์ชัน ดังนั้นจากตัวอย่างข้างต้น แม้ว่าคุณจะสามารถ เรียก ฟังก์ชันที่มีชนิดเป็น GenFunc เช่น genFunc<string>("hello") แต่ก็ไม่มี ชนิด ใด ๆ ที่ชื่อว่า GenFunc<string> มีข้อเสนอเปิด (microsoft/TypeScript#17574) ซึ่งอาจทำให้สามารถจัดการกับชนิดขั้นสูงเช่นนี้ได้ แต่ฉันไม่รู้ว่าเราสามารถคาดหวังความคืบหน้าใด ๆ ที่นั่นได้หรือไม่
ในตอนนี้ ดูเหมือนว่าจะไม่มีวิธีในการทำสิ่งนี้ เพียงแค่ระดับประเภท
ถ้าคุณไม่รังเกียจบางส่วนของการแฮ็กที่อาจมีผลต่อ JavaScript ที่ถูกสร้างออกมา คุณสามารถใช้ประโยชน์จากการสนับสนุนบางอย่างสำหรับการสรุปฟังก์ชันทั่วไปที่เพิ่มเข้ามาใน TypeScript 3.4 นี่คือวิธีที่ฉันอาจทำ ก่อนอื่น ให้แนะนำการประกาศฟังก์ชันบางแห่งเช่นนี้:
// you could just declare this instead of implementing it, but whatever:
function magicThunk<A extends any[], R>(f: (...args: A) => R): () => (...args: A) => R {
return () => f;
}
ฟังก์ชัน magicThunk() น่าจะเป็นแค่การรับฟังก์ชัน callback f และคืนค่าฟังก์ชันใหม่ที่ไม่มีอาร์กิวเมนต์ซึ่งคืนค่า f ซึ่งดูเหมือนไม่ได้มีความมหัศจรรย์มากนัก (แต่ก็นับว่าเป็น thunk อย่างน้อย) แต่ถ้าคุณเรียก magicThunk() ด้วยฟังก์ชันทั่วไป จะเกิดสิ่งที่น่าสนใจตั้งแต่ TS3.4 เป็นต้นไป:
const genericIdentity = <T>(x: T) => x; // <T>(x: T) => T
const thunkIdentity = magicThunk(genericIdentity); // <T>() => (x: T) => T
ฟังก์ชันที่ส่งคืนกลับมานั้น ก็ เป็นฟังก์ชันประเภททั่วไปเช่นกัน แต่พารามิเตอร์ประเภททั่วไปได้ถูกย้าย ออก หนึ่งระดับ ตอนนี้คุณสามารถเรียกฟังก์ชันทั่วไปแบบ thunked และระบุพารามิเตอร์ประเภทของมันได้ ทำให้ได้ฟังก์ชันที่ไม่ใช่ประเภททั่วไป:
const stringIdentity = thunkIdentity<string>(); // (x: string) => string;
ดังนั้นนี่จึงเป็นการทำงานของ "การระบุพารามิเตอร์ประเภทของฟังก์ชันเจนเนอริกโดยไม่ต้องเรียกใช้งาน" ตามที่เราต้องการ
อย่างไรก็ตาม เราเกือบจะถึงแล้ว... ขั้นตอนสุดท้ายคือการสร้าง generic class ใหม่ ซึ่งจะช่วยให้เรานำพารามิเตอร์ชนิดมาสู่ขอบเขตได้โดยไม่ต้องใส่ตรงไปยังฟังก์ชันอื่น ลองดูวิธีที่ฉันจะทำสำหรับชนิด Test["open"] ของคุณ:
class TransformTest<T, R> {
open = magicThunk(new Test().open)<T, R>()
}
และตอนนี้ชนิดของ TransformTest<T, R>.open ควรเป็นชนิดที่คุณกำลังมองหา:
type TestType<T, R> = TransformTest<T, R>["open"]; // (t1: T, t2: R) => T | R
type TestTypeReturn = ReturnType<TestType<number, number>>; // number
ดังนั้นทั้งหมดจึงทำงานได้ สังเกตว่าคุณไม่จำเป็นต้องเรียกใช้ magicThunk() จริง ๆ หรือสร้างอินสแตนซ์ของ TransformTest ในโค้ดของคุณ (นั่นเป็นเรื่องดีเพราะฉันไม่คิดว่า open จะถูกผูกไว้อย่างถูกต้อง); คอมไพเลอร์กำลังทำงานทั้งหมดที่คุณต้องการในเวลาการออกแบบ; เพียงแค่ต้องให้มันทำตามกระบวนการของการประเมินชนิดของโค้ดที่จะถูกสร้างขึ้น
โอเค หวังว่านี่จะช่วยได้ โชคดีนะ!
ลิงก์ไปยังโค้ด
