TypeScript 接口中具有不可预测字段的对象

Object with Unpredictable Fields in TypeScript Interface

我正在 Angular 中开发 Web 应用程序,并且正在对服务进行以下调用 return this.http.get<SiteContent>(this.apiUrl + this.endpoint + id) 问题是映射到 SiteContent 接口的 JSON 在其中一个内容不可预测的字段中包含一个对象。我如何在我的代码中处理这个问题才能访问 siteContent 字段中的那些字段?理想情况下,我希望能够做这样的事情

return this.http.get<SiteContent>(this.apiUrl + this.endpoint + id).subscribe((obj) => {
    console.log(obj.siteContent.get("randomKey"));
})

当我尝试按键执行 get() 时,上述示例导致以下错误消息。

ERROR TypeError: obj.siteContent.get is not a function

SiteContent 界面

export interface SiteContent {
    id: string;
    name: string;
    siteContent: Map<string,string>
}

样本JSON

{"id":"abc123","name":"name name","siteContent":{"random1":"hi","random2":"hey"}}

示例:https://www.typescriptlang.org/play?target=7&ts=4.6.2#code/JYOwLgpgTgZghgYwgAgMrEgYQPbguZAbwFgAoZC5YAEwC5kBnMKUAcwG4zLkQ4BbCPSYsQHLpQYYIOPOHoBZOAAcAPMLYAadaIB8ZAL5kyAGwhhkAKwa5kAXmQApVAHkAcgDolcKAwgAKcQoAckIAIhpQ2lC4ACMEAEYAJgBmUI1Q3gFIjP4UTIg00MksXEhwSLCoOBBqbD547IALYEKqmrrEpogAT1D9fSDA5ABKZDgGNCkZMrAyBFxrU3djbFY-K1x3YulS-DB3VjM-ULba+tDh4fYgA

JavaScript 背景,不特定于 TypeScript:

JSON is a data format based on JavaScript object literal notation. When you use the JSON.parse() method 将 JSON 字符串反序列化为 JavaScript 值,您将获得与使用 JavaScript 对象字面量相同的对象类型:

let foo = { "a": 1, "b": "two", "c": false };
console.log(foo) // { "a": 1, "b": "two", "c": false }

let bar = JSON.parse('{ "a": 1, "b": "two", "c": false }');
console.log(bar) // { "a": 1, "b": "two", "c": false }

JavaScript 中的对象是 key-value 对,其中键(通常)是字符串,但值可以是任何类型。就 JavaScript 而言,这些密钥不需要提前声明。 All objects in JavaScript are "expando" objects.

您使用点符号访问对象的 属性(如果键名是已知的有效标识符):

foo.a;

或使用方括号表示法(通常仅当键名不是有效标识符或它是某个表达式的结果时):

foo["a"];

const k = "a";
foo[k]; 

您不使用 get() 访问此类对象的属性。还有a JS Map class which stores key-value pairs and has a get() method to retrieve such pairs. But a Map is not the same as a plain JS object。使用Map是有原因的,但是处理反序列化JSON一般不在其中。

因此在运行时你应该有这样的代码:

let json = JSON.parse(
    '{"id":"abc123","name":"name name","siteContent":{"random1":"hi","random2":"hey"}}'
);

console.log(json.siteContent.random1); // hi

TypeScript 添加了一个静态类型系统来描述 JavaScript。它通常不会添加任何功能或改变 JavaScript 的工作方式。 An interface type 类似于 SiteContent 是一种描述您希望在运行时与之交互的某些 JavaScript 对象形状的方式。

TypeScript 中的对象类型可以具有具有特定字符串文字键的已知属性,例如 idname。但是,将 JavaScript 对象用作 expando 对象也很常见,其中属性具有已知的值类型,但无法提前知道键。所以 TypeScript 允许你给对象类型一个 index signature。对象类型 {[k: string]: number} 表示“您可以使用 string 类型的任何键 k 对该对象进行索引,如果那里有 属性,它将是 number。在你的例子中,你希望 siteContent 属性 是一个对象类型,它的键是任意的,但它的值是 strings:

interface SiteContent {
    id: string;
    name: string;
    siteContent: { [k: string]: string }
}

有了索引签名,您的代码现在可以在 TypeScript 中编译而不会出现错误:

let json = JSON.parse(
    '{"id":"abc123","name":"name name","siteContent":{"random1":"hi","random2":"hey"}}'
) as SiteContent;

console.log(json.siteContent.random1); // hi

Playground link to code