不关联两个或多个值的泛型类型参数



打字手册说泛型类型参数适用于"输入类型与输出类型相关"的情况,或者两个输入的类型以某种方式相关"的情况。它还说,不应该使用不关联两个值的泛型类型参数。这总是一个危险信号,因为这意味着想要指定类型参数的调用者必须无缘无故地手动指定额外的类型参数。">

今天我写了一个函数,它将在各种类的构造函数中被调用;它返回一个函数,该函数可以传递给该类的方法和这些方法接受的参数,并调用带有超时特性的方法,该功能将在经过一定时间后拒绝它们返回的承诺。它看起来像这样:

export function getTimer<ThisT>(thisArg: ThisT, ms: number) {
const time = <T>(fn: (...args: any[]) => Promise<T>, ...args: any[]): Promise<T> => {
return new Promise(async (resolve, reject) => {
const timer = setTimeout(() => reject(`Error: ${fn.name} timed out`), ms)
try {
resolve(await fn.bind(thisArg)(...args))
} catch(err) {
reject(err)
} finally {
clearTimeout(timer)
}
})
}
return time
}

在类的构造函数中,出现这一行:

this.time = getTimer(this, this.TIMEOUT)

然后各种方法看起来像这样:

async getTokenInfo(token: string): Promise<TokenInfo> {
return await this.time(this._getTokenInfo, token)
}

可以看到,getTimer函数接受一个泛型类型参数ThisT,它应该是创建计时器的类的类型。您还可以看到ThisT没有在其他任何地方使用,因此它没有"关联"两个值。我添加了这个类型参数,因为我不想让thisArg变成any;如果它是any,那么调用fn.bind(thisArg)会将函数绑定到any值,并且我将失去this的类型信息(我认为?)另外,据我所知,您应该尽可能避免使用any值。如果我明白发生了什么,创建ThisT泛型类型参数允许编译器根据我实际传递的thisArg来推断ThisT的类型,因此当fn被绑定到thisArg时,编译器理解我将它绑定到什么。

所以我的问题是,我做错了什么吗?我之所以问,是因为这似乎与TS文档中所说的泛型类型参数的用途相反。

你的思路肯定是对的。args的销毁和async/awaitpromise中的设置有一些问题。

你可以在这里找到代码。

function getTimer<T>(timeout: number) {
const time = (
fn: (...args: any[]) => Promise<T>,
args: any
) => {
const promise = new Promise(async (resolve: (value: Awaited<T>) => void, reject) => {
const timer = setTimeout(() => reject(`Error ${fn.name} timed out`), timeout)
try {
const data = await fn(...args)
resolve(data)
} catch(err) {
reject(err)
} finally {
clearTimeout(timer)
}
})
return promise
}
return time
}
type TokenInfo = {
token: string
info: string
}
class MyClass {
private TIMEOUT: number
private _time: <T>(fn: (...args: any[]) => Promise<T>, ...args: any[]) => Promise<T>
constructor(accepted_timeout: number) {
this.TIMEOUT = accepted_timeout
this._time = getTimer(this.TIMEOUT)
}
get time() { return this._time }
private _getTokenInfo(token: string) {
return new Promise((resolve: (value: TokenInfo) => void) => {
setTimeout(() => {
const tokenInfo: TokenInfo = { token, info: 'info_about_your_token' }
resolve(tokenInfo)
}, 500)
})
}
async getTokenInfo(token: string) {
return await this.time(this._getTokenInfo.bind(this), token)
}
}
new MyClass(300) // short timeout 
.getTokenInfo('my_secret_token')
.then(tokenInfo => {
console.log(tokenInfo) // ... no response timed out ...
})
.catch(error => {
console.error('ERROR', error) // "ERROR",  "Error _getTokenInfo timed out" 
})

new MyClass(1000) // long timeout
.getTokenInfo('my_secret_token')
.then(tokenInfo => {
console.log(tokenInfo) // { "token": "my_secret_token", "info": "info_about_your_token" } 
})

或者您可以在类函数中调用它,而不是在getTimer中调用该函数,这将返回promise。在getTimer中,您只需await数据并解析。不再需要破坏args,也不需要绑定到this。为了记录日志,您只需以字符串形式提供函数名。如果不重要,也可以省略该属性。

function getTimer<T>(timeout: number) {
const time = (
name: string,
promise: Promise<T>,
) => {
return new Promise(async (resolve: (value: Awaited<T>) => void, reject) => {
const timer = setTimeout(() => reject(`Error ${name} timed out`), timeout)
try {
const data = await promise
resolve(data)
} catch(err) {
reject(err)
} finally {
clearTimeout(timer)
}
})
}
return time
}
type TokenInfo = {
token: string
info: string
}
class MyClass {
private TIMEOUT: number
private _time: <T>(name: string, promise: Promise<T>) => Promise<Awaited<T>>
constructor(accepted_timeout: number) {
this.TIMEOUT = accepted_timeout
this._time = getTimer(this.TIMEOUT)
}
get time() { return this._time }
private _getTokenInfo(token: string) {
return new Promise((resolve: (value: TokenInfo) => void) => {
setTimeout(() => {
const tokenInfo: TokenInfo = { token, info: 'info_about_your_token' }
resolve(tokenInfo)
}, 500)
})
}
async getTokenInfo(token: string) {
return await this.time(this.getTokenInfo.name, this._getTokenInfo(token))
}
}
new MyClass(300) // short timeout 
.getTokenInfo('my_secret_token')
.then(tokenInfo => {
console.log(tokenInfo) // ... no response timed out ...
})
.catch(error => {
console.error('ERROR', error) // "ERROR",  "Error getTokenInfo timed out" 
})
new MyClass(1000) // long timeout
.getTokenInfo('my_secret_token')
.then(tokenInfo => {
console.log(tokenInfo) // { "token": "my_secret_token", "info": "info_about_your_token" } 
})

最新更新