【问题标题】:TypeScript: Strange behaviour when switching between PascalCase and camelCaseTypeScript:在 PascalCase 和 camelCase 之间切换时的奇怪行为
【发布时间】:2019-04-03 22:25:59
【问题描述】:

我正在将 C# 桌面应用程序重构为 Angular/TypeScript Web 应用程序。

C# 应用程序中的所有类属性都使用 PascalCase,所以我想,最好保留它。

这里有 2 个基本相同的 TypeScript 类的示例。第 1 个使用 PascalCase,第 2 个使用 camelCase:

//PascalCase
export class Info 
{
    public ManagedType:string;

    public ApiTemplate:string;
}

//camelCase
export class Info 
{
    public managedType:string;

    public apiTemplate:string;
}

这是奇怪的行为:

  1. 我从网络服务器加载 JSON 数据并创建上述 Info 类的数组。 TypeScript 类使用 PascalCase 还是 camelCase 似乎无关紧要。到目前为止,一切顺利。

    this.Infos = await this.HttpClient.get<Info[]>(this.Url).toPromise<Info[]>();
    
  2. 当我将数组记录到控制台时,我可以看到,无论 Info 类是使用 PascalCase 还是 camelCase,输出都使用 camelCase 作为属性。有点奇怪,但到目前为止还不错。

  3. 现在我变得奇怪了:当我使用 PascalCase 并过滤数组以获取 Info 类的一个特定实例时,结果始终为 undefined/null。 p>

  4. 当我使用 camelCase 过滤数组时,为了获取 Info 类的一个特定实例,结果被找到并正确。

    //This doesn't work: Info is always undefinded, although the Array exists.
    let Info = Infos.filter(i => i.ManagedType == "Something" && i.ApiTemplate == "Something else")[0];
    
    //This works: Info is found 
    let Info = Infos.filter(i => i.managedType == "Something" && i.apiTemplate == "Something else")[0];
    

我的问题是:

为什么会这样?这是 TypeScript 问题还是 Angular 问题?

是否有我必须遵守的不成文约定?

为什么 TypeScript 编译器不抛出错误或警告,说明使用 PascalCase 可能无法正常工作?

【问题讨论】:

  • typescript 无论如何都不关心。最后它都被转换为javascript。我认为问题可能出在您的服务器在运行时返回的数据上。检查您的 http 请求以查看服务器如何发送数据并相应地命名您的属性。
  • 到达的数据使用 camelCase,但我不明白,为什么存储数据的本地类(在 TypeScript/JavaScript 中定义)应该或必须使用与 JSON 文件相同的命名。这根本没有意义(除了自动映射)
  • 您是使用 new 创建类的实例,还是直接使用服务器数据但断言类型为您定义的类型?如果您没有创建实例,您将获得 json 对象。 Typescript 仅在构建时出现,它被转译为 javascript,这就是在运行时运行的内容。
  • 不,我没有使用 new() 并创建新实例。我直接使用服务器数据。代码如下所示: this.Infos = await this.HttpClient.get(this.Url).toPromise();
  • @toskv 尚未测试,但这可能已经是答案了。我必须在不依赖自动映射的情况下迭代服务器数据并创建新实例。我会试试这个。非常奇怪的是,无论我使用 PascalCase 还是 camelCase,自动映射都能正常工作。在我看来,这应该会抛出一个错误,说数据无法正确映射。来自 C# 背景,这很奇怪。

标签: arrays angular typescript camelcasing pascalcasing


【解决方案1】:

为什么会这样?这是 TypeScript 问题还是 Angular 问题?

两者都没有。问题的原因是来自您的网络服务器的 json 数据与您在 typescript 中定义的类 Info 的结构/格式不完全相同。

我必须遵守一个不成文的约定吗?

嗯,是的。在将它们转换为特定类之前,您应该手动测试并确保您确实获得了正确的数据结构。为了澄清,您应该获取 json(HTTP 响应的主体),将其作为 JSON 解析为通用对象,然后测试它是否实际上具有与类 (Info) 完全相同的所有属性(具有相同的名称和类型)你将要投给他们。然后去做。

更新:实际上有一种很酷的方法可以确定一个对象是否是 一个特定的类型并让打字稿知道这提供了强大的保证/类型保护。 Typescript 有这个称为User-defined Typeguard functions 的功能,您可以在其中定义一个函数,如果该函数返回 true 或 false 对象被测试为特定类型。

// user-defined type-guard function
function isInfo(obj: Object): obj is Info {
    if ('ManagedType' in obj && 'ApiTemplate' in obj) {
        return true;
    } else {
    // object does not have the required structure for Info class
        return false;
    }
}

// lets assume that jsonString is a string that comes from an
// http response body and contains json data. Parse it "blindly" to a generic object
let obj = JSON.parse(jsonString);

if (isInfo(obj)) {
    obj.ApiTemplate; // typescript in this scope knows that obj is of type Info
} else {
    // in this scope, typescript knows that obj is NOT of type Info
}

为什么 TypeScript 编译器不抛出错误或警告,说明使用 PascalCase 可能无法正常工作?

因为您在使用 this.HttpClient.get&lt;Info[]&gt;(this.Url).toPromise&lt;Info[]&gt;(); 时使用了隐式转换,所以您告诉 typescript '嘿,我知道在运行时,服务器将发送一个 json 字符串,该字符串将被解析并且与 @987654324 完全兼容@(信息对象数组)。但实际上在运行时不会发生这种情况,因为属性名称的大小写敏感性存在细微差别。 Typescript 在这里不会出错,因为您隐含地告诉它您知道自己在做什么。

所以要详细说明:

很明显,您在运行时转换的 JSON 对象与您隐式转换到的 Info 类定义不完全兼容。 json 数据实际上具有使用 camelCase 的属性名称,但是您已经使用 PascalName 定义了 Info 类。看看这个例子:

//PascalCase
class Info 
{
    public ManagedType:string;

    public ApiTemplate:string;
}

let jsonString = `{
    "managedType": "1234asdf",
    "apiTemplate": "asdf1234"
}`;

// And HERE IS THE ISSUE. This does an implicit cast to Info object
// assuming that the JSON parsed object will strictly be the same as defined Info
// class. But that is not guaranteed. Typescript just assumes that you know
// what you are doing and what kind of data you will actually get in 
// runtime.
let obj: Info = JSON.parse(jsonString); 

上述示例的最后一行执行与此完全相同的“盲”转换/转换:

this.Infos = await this.HttpClient.get&lt;Info[]&gt;(this.Url).toPromise&lt;Info[]&gt;();

本质上,您是在告诉 typescript 响应将是一个 Info 类的数组,该数组完全定义为类定义,但实际上在实际的 json 数据中它们不是,因此 JSON.parse() 将返回一个对象它具有与 json 字符串中的属性名称完全相同的属性名称,采用驼峰式而不是您让 typescript 假设的 PascalCase。

// typescript just assumes that the obj will have PascalCase properties 
// and doesn't complain. but in reality this at runtime will not work,
// because the json properties in the json string are camelCase. Typescript
// can not know what data you will actually cast to this type in runtime.
// and cannot catch this error
console.log(`accessing Info.ManagedType property: ${obj.ManagedType}`);

// lets check at runtime all the actual properties names
// they will be in camelCase, just like in the jsonString.
Object.keys(obj).forEach(key => {
    console.log(`found property name: ${key}`);
});

【讨论】:

  • 好的,谢谢。我认为如果可能的话,抛出运行时错误或警告将是很大的帮助/想法,如果开发人员不想盲目投射,因为来自 C#,那肯定会抛出错误...... ..但是好的,它就是这样....感谢所有 cmets 和答案!
  • 感谢您的接受。我还用对您每个问题的具体答案更新了我的答案。如有任何其他问题,请随时提出更多说明。
猜你喜欢
  • 1970-01-01
  • 2022-07-15
  • 2022-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-13
  • 1970-01-01
  • 2017-11-15
相关资源
最近更新 更多