對不起滿口的標題。我已經為這個問題搜索了很長一段時間,但不幸的是沒有找到。
這是代碼:
import { readdir } from "fs/promises"
export function tryGate<FUNC_RES, OUTPUT>(func: () => Promise<FUNC_RES>) {
return {
onPass(onPass: (res: FUNC_RES) => OUTPUT) {
return {
onThrow(onThrow: (catched: unknown) => OUTPUT) {
return {
async exec() {
try {
const res = await func()
return onPass(res)
} catch (e: unknown) {
return onThrow(e)
}
},
}
},
}
},
}
}
const test = tryGate(async () => await readdir("...", "utf8") )
.onPass((paths) => paths)
.onThrow(()=>[])
.exec()
的型別test應該是Promise<string[]>,因為:
await readdir("...", "utf8")回傳字串陣列,它應該是 generic 的型別FUNC_RES。onPass和函式都onThrow回傳string[],應該是泛型的型別OUTPUT。
但是型別test是Promise<unknown>.
顯然,我可以這樣顯式宣告型別:
const test = tryGate<string[], string[]>(async () => await readdir("...", "utf8") )
.onPass((paths) => paths)
.onThrow(()=>[])
.exec()
現在它按預期作業。但我認為必須有辦法以<string[], string[]>某種方式省略......
uj5u.com熱心網友回復:
在下文中,我將把你的引數FUNC_RES和OUTPUT型別引數分別重命名為F和O,以符合更常見的命名約定。
問題是您的函式上的第二個泛型型別引數沒有好的推理站點。tryGate<R, O>(...)它接受一個型別的函式,并且() => Promise<R>您希望編譯器能夠從中推斷出兩者。但根本沒有提及。ROO
唯一可能的方法是,如果編譯器推遲推斷型別引數,直到它看到您如何使用回傳的值。但這不是 TypeScript 的作業方式。泛型函式的型別引數必須在呼叫函式時立即指定(手動或推斷)。
這是有道理的,因為您希望在這里發生什么?
const x = tryGate(async () => await readdir("...", "utf8"));
型別引數應該O被推斷為什么?似乎您希望編譯器在這里等待,直到它看到您使用x. 但是當然你可以呼叫x.onPass()多次,像這樣:
x.onPass(paths => paths.length); // O should be number here
x.onPass(paths => paths); // O should be string[] here
然后似乎編譯器應該撕掉它的隱喻性聲音并尖叫著逃跑。形象地說。
一個人在這里沒有什么好做的O。編譯器需要推斷一些東西,但沒有好的推斷站點,所以推斷失敗并退回到unknown.
回顧x上面的內容,您希望O在呼叫后保持通用性tryGate(),以便使其正常作業。這表明您的O型別引數在錯誤的范圍內。如果您將它從tryGate()的通用呼叫簽名中移出并向下移動到回傳onPass方法的呼叫簽名中,從而使該方法也成為通用方法,那么事情就會開始表現良好:
function tryGate<R>(func: () => Promise<R>) { // only R here
return {
onPass<O>(onPass: (res: R) => O) { // O has been moved down here
return {
onThrow(onThrow: (catched: unknown) => O) {
return {
async exec() {
try {
const res = await func()
return onPass(res)
} catch (e: unknown) {
return onThrow(e)
}
},
}
},
}
},
}
現在當你打電話時tryGate(),你會得到一些O仍然是通用的東西:
const x = tryGate(async () => await readdir("...", "utf8"));
/* const x: {
onPass<O>(onPass: (res: string[]) => O): {
onThrow(onThrow: (catched: unknown) => O): {
exec(): Promise<O>;
};
};
} */
所以你可以呼叫它的onPass()方法兩次,得到兩個不同的泛型實體:
const y1 = x.onPass(paths => paths.length);
/* const y1: {
onThrow(onThrow: (catched: unknown) => number): {
exec(): Promise<number>;
};
} */
const y2 = x.onPass(paths => paths);
/* const y2: {
onThrow(onThrow: (catched: unknown) => string[]): {
exec(): Promise<string[]>;
};
} */
這意味著您的示例中的整個鏈條最終以Promise<string[]>而不是Promise<unknown>.
const test = tryGate(async () => await readdir("...", "utf8"))
.onPass((paths) => paths)
.onThrow(() => [])
.exec();
// const test: Promise<string[]>
Playground 代碼鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/515581.html
標籤:打字稿仿制药
上一篇:如果未指定,則默認的可選工廠引數
