如何使通用模板类型参数成为必需?
到目前为止,我发现做到这一点的唯一方法是使用never
,但这会导致在泛型的调用站点以外的其他位置发生错误。
粘贴在此处的TypeScript Playground example:
type RequestType =
| 'foo'
| 'bar'
| 'baz'
interface SomeRequest {
id: string
type: RequestType
sessionId: string
bucket: string
params: Array<any>
}
type ResponseResult = string | number | boolean
async function sendWorkRequest<T extends ResponseResult = never>(
type: RequestType,
...params
): Promise<T> {
await this.readyDeferred.promise
const request: SomeRequest = {
id: 'abc',
bucket: 'bucket',
type,
sessionId: 'some session id',
params: [1,'two',3],
}
const p = new Promise<T>(() => {})
this.requests[request.id] = p
this.worker.postMessage(request)
return p
}
// DOESN'T WORK
async function test1() {
const result = await sendWorkRequest('foo')
result.split('')
}
test1()
// WORKS
async function test2() {
const result = await sendWorkRequest<string>('foo')
result.split('')
}
test2()
正如在对test1()
的调用中所看到的,由于result.split('')
没有never
方法,错误发生在.split()
。
在test2
中,当我提供通用参数时,效果很好。
如何使arg为必需参数,而不使用从不使用,如果没有给出通用arg,则在调用sendWorkRequest
时发生错误?
答案 0 :(得分:3)
请参见this open suggestion。我所知道的最好方法是像您一样将T
默认设置为never
(假设never
对于T
不是有效的类型参数)并定义函数的参数之一,以便(1)如果将T
指定为非never
,则参数具有您实际想要的类型,而(2)如果T
为允许默认设置为never
,则该参数具有某种虚拟类型,因为它与参数类型不匹配,将产生错误。
棘手的部分是,如果调用者将T
设置为自己的某个作用域内类型变量U
,即使TypeScript无法排除{{1} }可以为U
。为了处理这种情况,我们使用了辅助类型never
,该辅助类型滥用索引访问类型的简化行为来将确定的IfDefinitelyNever
与类型变量区分开。需要使用特殊的never
(“门”)参数来防止来自函数G
的调用在函数本身的签名中过早评估为其假分支。
IfDefinitelyNever
答案 1 :(得分:1)
有一种更简单的方法可以达到上述目的,其中:
unknown
的值async function sendWorkRequest<ReqT = never, ResT = unknown, InferredReqT extends ReqT = ReqT>(
request: InferredReqT,
): Promise<ResT> {
return {} as ResT;
}
// Call does not succeed without an explicit request parameter.
async function test1() {
const result = await sendWorkRequest('foo');
// ~~~~~
// ERROR: Argument of type '"foo"' is not assignable to parameter of type 'never'
}
// Call succeeds, but response is 'unknown'.
async function test2() {
const result: number = await sendWorkRequest<string>('foo');
// ~~~~~~
// ERROR: Type 'unknown' is not assignable to type 'number'.
result.valueOf();
}
// Call succeeds and returns expected response.
async function test3() {
const result = await sendWorkRequest<string, number>('foo');
result.valueOf();
}
请参见this TypeScript playground。
这是通过让TypeScript仅推断最后一个类型参数,而将never
设置为非推断的主类型参数的默认值来实现的。如果未传递显式类型参数,则会发生错误,因为传递的值不可分配给默认的never
。至于返回类型,它是unknown
的很好的用法,因为除非明确地进行参数化,否则它不会被推断为其他任何类型。