TypeScript 3.x:访问未知类型的属性

Posted

技术标签:

【中文标题】TypeScript 3.x:访问未知类型的属性【英文标题】:TypeScript 3.x: Access properties of type unknown 【发布时间】:2020-05-07 09:07:46 【问题描述】:

我从 TypeScript 文档中了解到,您无法访问未知类型的属性:

// No property accesses, element accesses, or function calls

function f11(x: unknown) 
    x.foo;  // Error
    x[5];  // Error
    x();  // Error
    new x();  // Error


但我不明白为什么?如果我可以分配包括对象在内的每个值,为什么我不能访问属性?

我正在应对以下情况:我有一个 any 类型的对象,我将它传递给 Object.entries 方法,然后调用 forEach

因为它是any 类型,所以forEach 调用参数将是一个数组,其中第一个元素为字符串类型,第二个元素为未知类型。

在我的例子中,第二个元素是一个对象,但如果不转换它的类型,我就无法访问它的属性,这似乎是错误的做法。

这是一个示例,它会引发 TS 错误(只是一个抽象,我知道在这种情况下将其声明为 any 没有意义):

const obj: any = 
  val1: "someval",
  val2: 
    some: "some",
    object: "object",
  ,
;
Object.entries(obj).forEach(el => 
  if (el[1].some) 
    console.log(el);
  
);

我猜这也可能是 Object.entries 方法的错误类型,但我仍然想解释一下,为什么我不能访问类型为 unknown 的属性。

总结一下,我的问题是:

    为什么即使未知类型可以是对象,我也无法访问未知类型的属性? 我想上述问题是有原因的,但Object.entries 不应该返回一个带有元素 nr 的数组。 0 类型的字符串和元素 nr。任何类型的 1 个?

【问题讨论】:

【参考方案1】:

我认为,为了解决您的问题,重要的是要提供一些关于 anyunknown 的背景信息。虽然您可以在 official TypeScript documentation 中找到详尽的比较列表,但我想我可以冒昧地将文章缩短为几句话:any 基本上是一种万能的,因此 不是类型安全。类型安全是指您可以访问不存在的 any 的运行时属性。

unknown 不同。在这方面unknownany 正好相反。它表示any 的类型安全版本,声明“我不会假装我适合所有东西,因为我不适合”。所以unknown 需要额外转换为所需的类型才能工作(因为它本身不具备任何属性)。

现在,进入实际问题。为什么Object.entries 使用unknown 而不是any?因为说“在使用前将这个未知值转换为你需要的任何值,我不会假设我对这种类型一无所知”比“我不知道它是哪种类型”更安全是,但我会假设它具有所有可能类型的所有可能属性”。

【讨论】:

哦,所以unknown 类型实际上是要“重新分配”给具有可访问属性的类型?所以提醒开发者,在做诸如访问属性之类的事情时,他必须确保它存在,这样他就不会犯可能导致运行时错误的错误。虽然使用 any 类型,但他可以这样做,这可能是错误的,因此不是类型安全的。【参考方案2】:

我也有同样的问题 但我尝试这种技术来解决它

// choice is a custom data type 
export interface Choice 
  id: string; 
  isSelected: boolean; 
  score: number;
  status: string; 

for (const [key, value] of Object.entries(this.tableForm.value)) 
      if (value) 
        let choiceItem  = value as Choice;
        let choiceIds = [String(choiceItem.id)];
         
      
    

【讨论】:

【参考方案3】:

Object.entries(...)(取自GitHub)的定义是:

entries<T>(o:  [s: string]: T  | ArrayLike<T>): [string, T][];

由于您将obj 明确定义为any,因此对Object.entries 的调用返回

[string, unknown][]

您可以在声明 obj 时简单地删除 : any,TypeScript 将根据定义推断其类型。

在这种情况下,对Object.entries 的调用将返回:

[string, string |  some: string; object string; ][]

回答您的问题:

为什么我不能访问未知类型的属性?

编译器试图通过在编译时检查来避免运行时错误。如果您真的知道自己在做什么,您可以随时将unknown 转换为any。例如:

const value:any = el[1];

我想上述问题是有原因的,但 Object.entries 不应该返回一个带有元素 nr 的数组。 0 类型的字符串和元素 nr。任何类型的 1 个?

它实际上返回[string, unknown][]

【讨论】:

我明白,在我的示例中,将其声明为 any 没有任何意义。这只是一个抽象。即当对象来自获取时,它将是任何类型... 我为我的问题添加了摘要。 (我确实知道我的问题有很多解决方法我只是不知道它为什么存在) 感谢您更新的答案,但他们都只是说“是这样”和“它发生了”,但我实际上问为什么是这样,为什么返回类型未知,即使从我的角度来看,any 会更精确

以上是关于TypeScript 3.x:访问未知类型的属性的主要内容,如果未能解决你的问题,请参考以下文章

Typescript 推断的泛型类型在条件类型中是未知的

TypeScript:在 keyof T 处获取属性的类型

Typescript:至少有一个T类型属性的对象。

如何通过 TypeScript 中的映射类型删除属性

在 TypeScript 中,当类型是函数的参数时,有没有办法限制 Partial<T> 类型的额外/多余属性?

如何在 TypeScript 中通过索引访问通用对象的属性?