0

我正在将 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 类的一个特定实例时,结果总是未定义/空。

  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 可能无法正常工作?

4

1 回答 1

2

为什么呢?这是 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<Info[]>(this.Url).toPromise<Info[]>();您告诉 typescript '嘿,我知道在运行时,服务器将发送一个 json 字符串,该字符串将被解析并且绝对与Info[](信息对象数组)完全兼容。但实际上在运行时不会发生这种情况,因为属性名称的大小写敏感性存在细微差别。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<Info[]>(this.Url).toPromise<Info[]>();

本质上,您是在告诉 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}`);
});

于 2019-04-03T23:17:29.797 回答