【问题标题】:TypeScript type narrowing not working when loopingTypeScript 类型缩小在循环时不起作用
【发布时间】:2021-08-23 05:00:03
【问题描述】:

我可能在这里遗漏了一些明显的东西,但我已经坚持了一段时间。

鉴于以下谓词(我知道它技术上不是谓词)

const hasProp = <K extends PropertyKey, T extends {}> (obj: T, prop: K): obj is T & Record<K, unknown> => {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}

我想做这样的事情:

const coreProps = ['id', 'module', 'name', 'auth'] as const;
for (const prop of coreProps) {
  if (!hasProp(rawObject, prop)) {
    throw new Error(`Missing property '${prop}'`);
  }
}

但是,这不允许我使用例如rawObject.id 之后。 如果我手动展开循环,它会按预期工作。

是我做错了什么,还是 TypeScript 编译器的限制?


我确实想出了这个替代方案:

const hasProps = <Ks extends PropertyKey[], T extends {}> (obj: T, ...props: Ks): obj is T & { [K in Ks[number]]: unknown } => {
  return props.every(prop => hasProp(obj, prop));
}

但我不喜欢这样,因为它不会返回任何关于缺少哪个属性的信息(如果有的话)。

【问题讨论】:

  • “但我不喜欢这个,因为它不会返回任何关于缺少哪个属性的信息......” 它不能像你的循环那样抛出吗?
  • 技术上是的,但我在实用程序文件中有hasProps,我几乎肯定会在其他地方使用它。理想情况下,我希望它与该实现细节隔离开来。
  • 对我来说这似乎不是一个实现细节,它似乎是函数所固有的。但是你总是可以在错误的情况下使用一个伴随函数来告诉你缺少什么。无论如何,希望你能找到你喜欢的解决方案。
  • 你说得对,这可能适合我的用例。我想现在我只是好奇为什么将循环从函数中取出会导致类型检查失败

标签: typescript


【解决方案1】:

我相信您应该为此目的使用assert 函数:

const hasProp = <K extends PropertyKey, T>(obj: T, prop: K): obj is T & Record<K, unknown> => {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}

const rawObject: any = {}

const coreProps = ['id', 'module', 'name', 'auth'] as const;

type Props = typeof coreProps;


function assert(value: unknown): asserts value is Record<Props[number], unknown> {
  for (const prop of coreProps) {
    if (!hasProp(rawObject, prop)) {
      throw new Error(`Missing property '${prop}'`);
    }
  }
}


assert(arg);// <---- assert function
arg.id // ok

Playground

Assertion function examples

TS official docs

因此,为了使其正常工作,您应该将循环包装到 assert function 中。这样,TS 就可以推断出你的对象的类型,否则由于对象的可变性而无法推断。

【讨论】:

    猜你喜欢
    • 2020-07-22
    • 2020-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 2018-07-03
    • 2013-02-23
    相关资源
    最近更新 更多