为什么Function在TypeScript中扩展Record?

问题描述 投票:2回答:2
const fn = () => null
type Answer = typeof fn extends Record<string, any> ? true : false

上面的片段生成Answertrue类型,表明函数被认为是Record的子类型。为什么?

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

typescript static-typing
2个回答
4
投票

所以,根据你的评论,你想要的是“一个不是一个功能的对象”。 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: ()=>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的类型或多或少准确地说“不是函数的对象”,但它只能表示为函数参数。


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


3
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.