当您使用TypeScript编写库时,您通常不知道该库最终将如何使用。即使你警告潜在用户你只为TypeScript用户编写了库,你也可能在某个时候拥有JavaScript用户——要么是因为他们不顾你的警告使用库,要么是因为传递依赖这个库。这有一个非常重要的后果:您必须将库设计为可供任何语言的开发人员使用!它的主要部分是函数定义和函数体。如果您是为纯TypeScript读者编写的,您可以只定义函数类型并相信编译器会处理其余部分。如果您针对纯JavaScrpit阅读器编写,那么您需要记录这些类型,但在函数中使实际类型未知并检查调用者传递的内容。例如,给定以下代码interfacePerson{age:number;name?:string;}functiondescribe(person:Person):string{letname=person.name??'someone';return`${name}is${person.age}yearsold!`;}JS用户可以用任何东西调用describe函数。正确的方式:describe({name:"chris"})灾难性的错误方式:describe("potato");最常见的JS错误:describe(undefined);您库的JS用户并不是有意这样做的。恰恰相反,在任何足够大的系统中,很容易将错误的参数传递给系统中的函数。这通常是不可避免的错误,比如在一个点上做了一个修改,其他很多地方需要更新,却漏掉了一个点。故意的JS开发人员将错误数据发送到您设计精美的TSAPI中。如果您正在为纯TypeScript阅读器编写,那么您只需定义函数类型并相信编译器会处理其余部分。我故意不提TypeScript编译非常严格,从与JavaScript无法区分的级别到几乎任何人都能想到的任何严格级别。这意味着即使是TypeScript调用者也应该像JavaScript调用者一样对待:众所周知,他们会到处抛出任何东西,而忽略实际上可能为null或undefined的地方。返回上面的示例代码:interfacePerson{age:number;name?:string;}functiondescribe(person:Person):string{letname=person.name??'someone';return`${name}is${person.age}yearsold!`;}在没有启用严格标志的情况下,TypeScript用户可以按如下方式调用describe:functioncueTheSobbing(data:any){describe(data);}cueTheSobbing({breakfastOf:[“鸡蛋”,“华夫饼”]});或者这个:描述(空);或者这样:describe({age:null})也就是说:JS调用者大多会失败的方式,TS调用者也会在严格关闭的情况下失败。这意味着故意的TypeScript用户也会使用错误数据调用您的库。而且由于它们依赖于其他库,所以这很可能不是它们的错,因为这种问题可能发生在依赖图中的任何地方。所以如果问题是我们不能信任数据,我们应该怎么办?一种选择是使函数的所有参数实际上都是未知的,并使用JSDoc来指定它应该如何。但是,这会带走TS提供的很多功能。在与函数交互时,即使在内部,我们也永远不会出现完成或类型错误,更不用说我们库的用户了。但是正如我们刚刚看到的,我们也不能依赖类型定义来提供函数内部的安全性。然而,我们可以结合几种方法:指定类型定义和将传入数据视为有效未知。这确实会带来运行时开销——我们稍后会详细讨论这种权衡。现在,我们可以先看看如何检查类型。首先,我们将编写我们的代码,就好像我们真的要从调用者那里得到真正未知的数据,因为我们已经确定这是我们可能得到的。一旦我们完成了对未知数据的验证,我们就可以用Person替换它,一切都应该继续工作,但现在我们可以保证它可以处理任何扔给它的数据。functiondescribe(person:unknown):string{letname=person.name??'someone';return`${name}is${person.age}yearsold`;}这里有一个类型错误,因为这里的person类型可能是undefined或者“potato”或者任何其他类型。为了安全起见,我们可以使用TypeScript的类型缩小概念。然而,从未知缩小到特定的对象类型有点奇怪,因为如果你简单地检查typeofsomethingUnknown==='object',这会将类型缩小到{},这意味着它不会包含我们可能需要的任何东西类型。我们将首先定义一个isObject辅助函数,它会为我们提供正确的语义:我们还需要一种方法来检查此对象是否具有指定的属性。如果in运算符以这种方式工作就好了,但不幸的是它不会。我们也可以内联执行此操作,但每次都需要进行类型转换。我们可以称它为has,类似于Object.hasOwnProperty方法。由于这还需要检查isObject返回的类型集——在JS中索引对象的所有合法类型——我们将在此处将其提取到一个新的Key类型中。thishashelper的返回类型告诉类型系统,如果正文为真,则传入的项目具有其原始类型,并且包含我们要检查的属性。typeKey=string|number|symbol;functionhas
