【问题标题】:Why does Function extend Record in TypeScript?为什么函数在 TypeScript 中扩展 Record?
【发布时间】:2018-06-28 16:46:03
【问题描述】:
const fn = () => null
type Answer = typeof fn extends Record<string, any> ? true : false

上面的 sn-p 生成 Answertrue 类型,表明函数被视为 Record 的子类型。为什么?

如何使我的类型更严格,使函数不是它的子类型?

【问题讨论】:

  • “我怎样才能使我的类型更严格”......究竟是哪些类型?可能你拥有的任何东西都可以比Record&lt;string, any&gt; 更严格
  • 我只想要对象,但不可调用。这是一个非常全局的验证器功能,并检查某物是否是“对象”(记录)以递归遍历验证器树。我想问题是“如何键入不是函数的记录?”。我试过Exclude&lt;Record&lt;string, any&gt;, Function&gt;,但这仍然允许一个函数。

标签: typescript static-typing


【解决方案1】:

因此,根据您的评论,您想要“不是函数的对象”之类的东西。 TypeScript 目前没有negated types,因此没有简洁的方式来表达“不是函数”。根据您的用例,您可能只想耸耸肩继续前进。

或者,您可能会尝试通过变通方法强制 TypeScript 屈服于您的意愿。


最简单的解决方法是找到一些函数始终具有的属性,并声明您的类型具有不兼容的同名可选属性。例如:

type NoCall = { call?: number | string | boolean | null | undefined, [k: string]: any };
const okay: NoCall = { a: 4 }; // okay
const err: NoCall = () => 2; // error

缺点是您将无法验证某些合法的非函数(在上述情况下,{call: ()=&gt;0} 不是函数,但无法验证为 NoCall)。但是,对于您的用例来说,可能有些误报是可以接受的?

无论如何,根据标准库lib.es5.d.ts 来检查属性名称的合理选择是applycallbindargumentscaller


如果您不喜欢放弃其中一个名称,您可以选择使用global augmentation 将幻像属性添加到Function 接口:

declare global {
  interface Function {
    '*FunctionsHaveThis*': true
  }
}
type ObjNotFunction = { '*FunctionsHaveThis*'?: never, [k: string]: any };
const okay: ObjNotFunction = { a: 4 }; // okay
const err: ObjNotFunction = () => 2; // error

这会降低属性名称冲突的可能性,但会污染全局命名空间,并使这种解决方法变得不那么简单。


另一种解决方法是使用 trickconditional types 来防止函数作为参数传递给另一个函数:

function noFunctionsPlease<T extends object>(t: T & (T extends Function ? never : T)) {
  // do something with t
}
noFunctionsPlease({ a: 4 }); // okay
noFunctionsPlease(() => 2); // error

t 的类型或多或少准确地说是“不是函数的对象”,但它只能表示为函数参数。


希望其中一个对您有所帮助或让您了解如何(或是否)继续进行。祝你好运!

【讨论】:

  • 哇,非常感谢!我喜欢call 之一。已经通过切换条件做了一个解决方法,所以我要求除对象之外的所有内容,然后简单地将其保留为“其他”情况......是时候再次重构它了 \m/
【解决方案2】:

Record&lt;string, any&gt; 表示具有任何字符串键和任何值的任何对象。所有函数都是对象,它们有一些属性(我们甚至可以索引到对象fn['call'])。通过这些定义,并且由于 Typescript 中的类型关系基于结构,typeof fn 确实扩展了 Record&lt;string, any&gt;

【讨论】:

    猜你喜欢
    • 2022-10-07
    • 2022-09-23
    • 2020-08-20
    • 2016-09-16
    • 2017-04-18
    • 1970-01-01
    • 2019-05-20
    • 2016-08-16
    • 1970-01-01
    相关资源
    最近更新 更多