为什么函数在TypeScript中扩展Record?

时间:2018-06-28 16:46:03

标签: typescript static-typing

const fn = () => null
type Answer = typeof fn extends Record<string, any> ? true : false

上面的代码段产生的Answer类型为true,表明函数被视为Record的子类型。为什么?

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

2 个答案:

答案 0 :(得分: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进行检查的属性名称的合理选择是applycallbindarguments和{{ 1}}。


如果您不想放弃其中一个名称,则可以选择使用global augmentationcaller界面添加幻像属性:

Function

这使得属性名称冲突的可能性较小,但会污染全局名称空间,并使此解决方法变得相当简单。


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

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

function noFunctionsPlease<T extends object>(t: T & (T extends Function ? never : T)) { // do something with t } noFunctionsPlease({ a: 4 }); // okay noFunctionsPlease(() => 2); // error 的类型或多或少准确地说是“不是函数的对象”,但只能表示为函数参数。


希望其中之一对您有所帮助或为您提供有关如何(或是否)进行的想法。祝你好运!

答案 1 :(得分:3)

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