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

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