Почему это так?Это проблема TypeScript или проблема Angular?
Ни то, ни другое.Причиной проблемы является то, что данные json, которые поступают с вашего веб-сервера, не имеют ту же структуру / формат, в котором вы определили класс Info в машинописи.
Существует ли неписанное соглашение, которое ядолжны следовать?
Ну да, есть.Вы должны вручную проверить и убедиться, что вы действительно получили правильные структуры данных, прежде чем приводить их к определенным классам.Чтобы уточнить, вы должны взять json (тело HTTP-ответа), проанализировать его как JSON в универсальном объекте, а затем проверить, действительно ли он имеет все свойства (с тем же именем и типами), что и у класса (Info)что вы собираетесь бросить их.А затем сделайте это.
ОБНОВЛЕНИЕ: на самом деле, есть классный способ определить, является ли объект определенным типом, и сообщить машинописи об этом, обеспечивая надежную защиту типа / типа.В Typescript есть эта функция, которая называется Определяемые пользователем функции Typeguard , где вы определяете функцию, которая возвращает 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[]>();
, который вы указали для машинописного текста, что«эй, я знаю, что во время выполнения сервер отправит строку json, которая будет проанализирована и будет абсолютно точно совместима с Info[]
(массивом информационных объектов).Но на самом деле во время выполнения этого не происходит, потому что есть небольшая разница в чувствительности к регистру имен свойств.Typescript здесь не будет ошибкой, потому что вы неявно сказали ему, что знаете, что делаете.
Итак, чтобы сказать:
Очевидно, что во время выполнения вы конвертируете объект JSON, который не является полностьюсовместим с определением класса Info, к которому вы неявно приводите его.Данные json на самом деле имеют имена свойств с помощью camelCase, но вы определили класс Info с помощью PascalName.Взгляните на этот пример:
//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[]>();
По сути, вы говорите машинописному тексту, что ответом будет класс Array of Info, определенный точно как определение класса, но в действительности в реальных данных json они не различаются, поэтому JSON.parse () вернет объект, которыйимеет имена свойств в точности такие, как они есть в строке json, в camelCase вместо 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}`);
});