javascript - ฟังก์ชัน tapAsync ของ Typescript ไม่สามารถตั้งค่าไทป์ได้ถูกต้อง
ฉันต้องการที่จะสามารถเชื่อมต่อฟังก์ชัน async ของ ES6 หลายอันเข้าด้วยกันโดยใช้ TypeScript ฟังก์ชัน tap ควรส่งคืนอาร์กิวเมนต์ถ้าไม่มีอะไรถูกส่งคืนจากฟังก์ชันที่ถูก tap แต่ถ้ามีค่าส่งคืนจากฟังก์ชันที่ถูก tap มันก็จะ tap ค่าที่ส่งคืนนั้น
ตัวอย่าง playground ของ TypeScript
ฉันสามารถทำให้มันทำงานได้โดยไม่ต้องพิมพ์ แต่มีปัญหาในการตั้งค่าประเภท โปรดดูตัวอย่างโค้ดที่ทำงานใน Javascript
ฟังก์ชันที่ถูกเรียกใช้เพียงแค่เรียกด้วยค่าของ x x => fn(x) จากนั้นเชื่อมต่อเพื่อคืนค่าผลลัพธ์ y หรือค่าที่ถูกเรียกใช้ x x => fn(x).then(y => y || x)
เวอร์ชันแรกนี้ที่ใช้ประเภท any ใช้งานได้ แต่ฉันได้รับข้อผิดพลาดเมื่อระบุรายละเอียดเกี่ยวกับการจัดการประเภทในฟังก์ชันที่ถูกแตะ
const tapAsync = (fn: (x: any) => Promise<any>) => (
x: any
): Promise<any> => fn(x).then((y: any) => y || x)
เพื่อให้ชัดเจนมากขึ้น ผมกำลังใช้ Generic สองตัว คือ X สำหรับอาร์กิวเมนต์เริ่มต้น และ Y สำหรับค่าที่ส่งกลับของฟังก์ชันที่ถูกแตะ
const tapAsync = (fn: <X>(x: X) => Promise<X>) => (
x: X
): Promise<Y|X> => fn(x).then(<Y>(y: Y) => y || x)
เมื่อฉันเรียกฟังก์ชันโดยใช้ tapAsync ฉันได้รับข้อผิดพลาดดังต่อไปนี้
src/index.ts:45:18 - error TS2345: Argument of type '({ foo }: { foo: any; }) => Promise<void>' is not assignable to parameter of type '<X>(x: X) => Promise<X>'.
Types of parameters '__0' and 'x' are incompatible.
Type 'X' is not assignable to type '{ foo: any; }'.
45 .then(tapAsync(one))
~~~
src/index.ts:46:18 - error TS2345: Argument of type '({ foo }: { foo: any; }) => Promise<{ foo: any; bar: string; }>' is not assignable to parameter of type '<X>(x: X) => Promise<X>'.
Types of parameters '__0' and 'x' are incompatible.
Type 'X' is not assignable to type '{ foo: any; }'.
46 .then(tapAsync(two))
~~~
src/index.ts:47:18 - error TS2345: Argument of type '({ foo, bar }: { foo: any; bar: any; }) => Promise<void>' is not assignable to parameter of type '<X>(x: X) => Promise<X>'.
Types of parameters '__0' and 'x' are incompatible.
Type 'X' is not assignable to type '{ foo: any; bar: any; }'.
47 .then(tapAsync(three))
ฉันไม่ได้กำหนดประเภทใด ๆ ใน TypeScript สำหรับฟังก์ชันที่ถูกแตะ แต่ฉันได้ลองใช้ประเภททั่วไปกับฟังก์ชันที่สอง สอง แต่ก็ยังใช้ไม่ได้
async function two<X>({ foo }): Promise<X> {
console.log('two', foo)
return {
foo,
bar: 'bar'
}
}
async function one({ foo }) {
console.log('one', foo)
}
async function two({ foo }) {
console.log('two', foo)
return {
foo,
bar: 'bar'
}
}
async function three({ foo, bar }) {
console.log('three', foo, bar)
}
const tapAsync = fn => x => fn(x).then(y => y || x)
Promise.resolve({ foo: 'foo' })
.then(tapAsync(one))
.then(tapAsync(two))
.then(tapAsync(three))ขอบคุณสำหรับความช่วยเหลือทุกอย่าง!
============== แก้ไข 2020-09-01 ====================
ฉันได้ลองเล่นกับโค้ดและได้ขยายประเภทออกมาอีกเล็กน้อย แต่ตอนนี้ฉันได้รับข้อผิดพลาดในฟังก์ชัน สอง เมื่อมันส่งกลับวัตถุใหม่แม้ว่ามันจะมีรูปร่างเหมือนเดิม
ตัวอย่าง playground ของ TypeScript ใหม่
const tapAsync = <X, Y>(fn: (x: X) => Promise<Y|void>) =>
(x: X): Promise<X|Y> =>
fn(x).then((y: Y|void) => y || x)
คำตอบ #1
ฉันคิดว่าด้วยการโอเวอร์โหลดมันสามารถทำงานได้ และการอนุมานประเภทดูถูกต้อง โดยพื้นฐานแล้วสิ่งนี้ทำให้คอมไพเลอร์ ts สามารถอนุมานได้ว่าจะไม่มี (x:X)=>Promise<void | Y> เป็นค่าที่ส่งกลับ ซึ่งเกิดขึ้นหลังจากการเรียกครั้งแรกของฟังก์ชัน async ที่ไม่ส่งค่ากลับ function :Promise<void> Y ถูกอนุมานให้เป็นประเภทนั้น ดังนั้น Promise.then(... จะพยายามส่งค่า X เป็น void | Args ในการเรียกครั้งถัดไป ส่งผลให้เกิด (x: void | Args)=>.... ซึ่งไม่เข้ากันกับ (x: Args)=>... ที่คาดหวัง
ตรวจสอบ playground นี้ ตัวอย่างของคุณพร้อมกับการโอเวอร์โหลด
function tapAsync <X,Y>(fn: (x:X)=>Promise<void>): (x:X)=>Promise<X>;
function tapAsync <X,Y>(fn: (x:X)=>Promise<Y>): (x:X)=>Promise<Y>;
function tapAsync <X, Y>(fn: (x: X) => Promise<Y|void>) {
return (x: X) => fn(x).then(y => y||x )
}
แก้ไข: เมื่ออ่านคำตอบของฉันอีกครั้ง ฉันลืมกล่าวถึงว่า ตัวต้นเหตุคือ (y: void| Y )=> Y||X ไม่สามารถสรุปได้ว่ามันไม่สามารถคืนค่า void ได้ถ้า X เริ่มต้นไม่ใช่ void
