繁体   English   中英

如何制作 typescript 中所需的泛型类型参数?

[英]How to make a generic type argument required in typescript?

如何使通用模板类型参数成为必需的?

到目前为止,我发现唯一的方法是使用never但它会导致错误发生在泛型调用站点以外的其他地方。

此处粘贴的TypeScript 游乐场示例

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时发生错误?

请参阅此公开建议 我所知道的最好方法是让T默认为never不像您默认的那样(假设Never never不是T的有效类型参数)并定义函数参数之一的类型,以便(1)如果指定了T作为non- never ,则参数具有您实际想要的类型,并且(2)如果允许T默认为never ,则参数具有某种虚拟类型,因为它与参数类型不匹配,将产生错误。

棘手的部分是,如果调用者将T设置为其自己的某个范围内类型变量U ,则即使TypeScript不能排除U neverT ,我们也希望允许调用。 为了处理这种情况,我们使用了一个辅助类型IfDefinitelyNever ,它滥用索引访问类型的简化行为来将一个确定的never与类型变量区分开。 需要特殊的G (“门”)参数,以防止来自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()

有一种更简单的方法可以实现上述目的,其中:

  1. 必须提供一个显式类型参数以无误地传递参数,并且
  2. 必须提供第二个显式类型参数以获取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();
}

看到这个TypeScript游乐场

这是通过让TypeScript仅推断出最后一个类型参数,而never将其设置为非推断出的主类型参数的默认值而起作用的。 如果未传递显式类型参数,则会发生错误,因为传递的值不可分配给默认的never 至于返回类型,它极大地使用了unknown ,因为除非明确地对其进行参数化,否则它不会被推断为其他任何类型。

这就是我如何使我的第一个通用强制性

const fn = <T = never, AT extends T = T>(arg: AT): any => {}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM