如何使通用模板类型参数成为必需的?
到目前为止,我找到的唯一方法是使用 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,我如何才能使 arg 成为必需的,而不是永远不使用,并且在调用 sendWorkRequest
时发生错误?
最佳答案
参见 this open suggestion .我知道的最好的方法是让 T
像你一样默认为 never
(假设 never
不是 的有效类型参数>T
) 并定义函数参数之一的类型,以便 (1) 如果 T
指定为非 never
,则参数具有您实际需要的类型,并且 (2) 如果允许 T
默认为 never
,则该参数具有某种虚拟类型,该类型将产生错误,因为它不t 匹配参数类型。
棘手的部分是,如果调用者将 T
设置为它自己的某个范围内类型变量 U
,我们希望允许调用,即使 TypeScript 无法规则U
可能是 never
。为了处理这种情况,我们使用辅助类型 IfDefinitelyNever
,它滥用索引访问类型的简化行为来区分明确的 never
和类型变量。需要特殊的 G
(“gate”)参数来防止 IfDefinitelyNever
的调用过早评估到函数本身签名中的错误分支。
type RequestType =
| 'foo'
| 'bar'
| 'baz'
interface SomeRequest {
id: string
type: RequestType
sessionId: string
bucket: string
params: Array<any>
}
type ResponseResult = string | number | boolean
const ERROR_INTERFACE_DUMMY = Symbol();
interface Type_parameter_T_is_required {
[ERROR_INTERFACE_DUMMY]: never;
}
interface Do_not_mess_with_this_type_parameter {
[ERROR_INTERFACE_DUMMY]: never;
}
type IfDefinitelyNever<X, A, B, G extends Do_not_mess_with_this_type_parameter> =
("good" | G) extends {[P in keyof X]: "good"}[keyof X] ? B : ([X] extends [never] ? A : B);
async function sendWorkRequest<T extends ResponseResult = never,
G extends Do_not_mess_with_this_type_parameter = never>(
type: RequestType & IfDefinitelyNever<T, Type_parameter_T_is_required, unknown, G>,
...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() {
// Error: Argument of type '"foo"' is not assignable to parameter of type
// '("foo" & Type_parameter_T_is_required) |
// ("bar" & Type_parameter_T_is_required) |
// ("baz" & Type_parameter_T_is_required)'.
const result = await sendWorkRequest('foo')
result.split('')
}
test1()
// WORKS
async function test2() {
const result = await sendWorkRequest<string>('foo')
result.split('')
}
test2()
// ALSO WORKS
async function test3<U extends ResponseResult>() {
const result = await sendWorkRequest<U>('foo')
}
test3()
关于typescript - 如何在 typescript 中制作通用类型参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53109837/