typescript - 如何在 typescript 中制作通用类型参数?

标签 typescript generics

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

到目前为止,我找到的唯一方法是使用 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/

相关文章:

c# - 方法中的泛型,c#

java - 编译器如何推断无参数方法的类型?

javascript - 我们可以在没有 Node 的情况下使用 Angular 吗?

typescript - 是否可以在 Typescript 中声明动态字符串类型

typescript - 从通用访问原型(prototype)

c# - 将具体转换为基础通用抽象类的解决方法

c# - CaSTLe Windsor - 将泛型实现解析为基类型

javascript - 在 JavaScript/TypeScript 中将项目添加到数组末尾

typescript - 如何使用 ESLint TypeScript Airbnb 配置?

ios - 按索引移动数组中的元素