我有这个TS代码:

type MyFunctions = {
  play(): void
  pause(args: { reset: boolean }): void
  stop(args: { restart: boolean }): void
}

type AuthStruct = {
  [K in keyof MyFunctions]: {
    requiresAuth: boolean
  }
}

const auth = {
  play: {
    requiresAuth: true
  },
  pause: {
    requiresAuth: true
  },
  stop: {
    requiresAuth: false
  }
} as const satisfies AuthStruct

type Auth = typeof auth

type FunctionParameters<K extends keyof MyFunctions> = Parameters<
  MyFunctions[K]
>[0] extends undefined
  ? never
  : Parameters<MyFunctions[K]>[0]

type Data<K extends keyof MyFunctions> = (FunctionParameters<K> extends never
  ? {}
  : FunctionParameters<K>
) & (
    Auth[K] extends { requiresAuth: true }
    ? { token: string }
    : {}
  )

function runFunction<F extends keyof MyFunctions>(
  func: F,
  data: Data<F>
) {
  if(auth[func].requiresAuth) {
    data.token // Should work
  }
}

我假设在运行条件判断if(auth[func].requiresAuth)之后,编译器会理解data有一个requiresAuth属性.但不幸的是它不起作用.此外,我甚至不能强迫编译器接受data具有token属性,比如:data.token!. 如何解决这个问题?

Playground

推荐答案

我能够使用typegard和一些黑魔法(不使用Any)让它与这段代码一起工作:

type HasToken = {token: string}

const isAuth = <F extends keyof MyFunctions> (func: AuthStruct[F], _: Data<F> | HasToken): _ is HasToken => {
  return func.requiresAuth;
}

function runFunction<F extends keyof MyFunctions>(
  func: F,
  data: Data<F>
) {
  if(isAuth(auth[func], data)) {
    data.token // Should work
  }
}

也许它可以改进,这样你就不需要传递func和使用一个有点难看的未使用变量了,但嘿,它工作.

编辑:在看到另一个答案后,我认为这可能更好:

const isAuth = <F extends keyof MyFunctions> (data: Data<F> | HasToken): data is HasToken => {
  return 'token' in data;
}

// inside of function
if(auth[func].requiresAuth && isAuth(data)) {
    data.token // Should work
  }
}

或者甚至像这样,最大限度地保留你原来的逻辑

const isAuth = <F extends keyof MyFunctions> (func: AuthStruct[F], data: Data<F> | HasToken): data is HasToken => {
  return func.requiresAuth && 'token' in data;
}

// inside of function
  if(isAuth(auth[func], data)) {
    data.token // Should work
  }

Typescript相关问答推荐

您可以创建一个类型来表示打字员吗

`((PrevState:NULL)=>;NULL)|null`是什么意思?

TypeScript:在作为参数传递给另一个函数的记录中对函数的参数实现类型约束

在映射类型中用作索引时,TypeScrip泛型类型参数无法正常工作

编剧错误:正在等待Expect(Locator).toBeVisible()

PrimeNG日历需要找到覆盖默认Enter键行为的方法

是否有一个版本的联合类型递归地使属性只出现在一个可选选项中?

如何实现异构数组内函数的类型缩小

寻址对象中路径的泛型类型

为什么在leetcode问题的测试用例中,即使我确实得到了比预期更好的解决方案,这段代码也失败了?

在类型脚本中将泛型字符串数组转换为字符串字母并集

类型脚本-在调用期间解析函数参数类型

当数组类型被扩展并提供标准数组时,TypeScrip不会抱怨

如何在try 运行独立的文字脚本Angular 项目时修复Visual Studio中的MODULE_NOT_FOUND

使用Cypress测试从Reaction受控列表中删除项目

将类型脚本函数返回类型提取到VSCode中的接口

类型';只读<;部分<;字符串|记录<;字符串、字符串|未定义&>';上不存在TS 2339错误属性.属性&q;x&q;不存在字符串

React-跨组件共享功能的最佳实践

当受其他参数限制时,通用参数类型不会缩小

const 使用条件类型时,通用类型参数不会推断为 const