#arrays #angular #typescript #camelcasing #pascalcasing
#массивы #angular #typescript #camelcasing #pascalcasing
Вопрос:
Я рефакторингую настольное приложение C # в веб-приложение Angular / TypeScript.
Все свойства класса внутри приложения 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;
}
Вот странное поведение:
-
Я загружаю данные JSON с веб-сервера и создаю массив вышеупомянутого класса Info. Кажется, не имеет значения, использует ли класс TypeScript PascalCase или camelCase. Пока все хорошо.
this.Infos = await this.HttpClient.get<Info[]>(this.Url).toPromise<Info[]>();
-
Когда я регистрирую свой массив в консоли, я вижу, что на выходе используется camelCase для свойств, независимо от того, использует ли класс Info PascalCase или camelCase. Немного странно, но пока все хорошо.
-
Теперь мне стало странно: когда я использую PascalCase и фильтрую массив, чтобы получить один конкретный экземпляр класса Info, результат всегда неопределенный / нулевой.
-
Когда я использую camelCase и фильтрую массив, чтобы получить один конкретный экземпляр класса Info, результат найден и корректен.
//This doesn't work: Info is always undefinded, although the Array exists. let Info = Infos.filter(i => i.ManagedType == "Something" amp;amp; i.ApiTemplate == "Something else")[0]; //This works: Info is found let Info = Infos.filter(i => i.managedType == "Something" amp;amp; i.apiTemplate == "Something else")[0];
Мои вопросы:
Почему это так? Это проблема с TypeScript или это проблема Angular?
Существует ли неписаное соглашение, которому я должен следовать?
Почему компилятор TypeScript не выдает ошибку или предупреждение о том, что использование PascalCase может работать некорректно?
Комментарии:
1. typescript в любом случае не волнует. В конце все это преобразуется в javascript. Я думаю, что проблема может быть в данных, которые ваш сервер возвращает во время выполнения. Проверьте свои http-запросы, чтобы увидеть, как сервер отправляет данные, и соответствующим образом назовите свои свойства.
2. Поступающие данные используют camelCase, но я не понимаю, почему локальный класс, который хранит данные, определенные в TypeScript / JavaScript, должен использовать то же имя, что и файл JSON. Это вообще не имеет смысла (кроме автоматического сопоставления)
3. Вы создаете экземпляры своего класса, используя new, или вы используете данные сервера напрямую, но утверждаете, что тип является тем, который вы определили? Если вы не создаете экземпляры, вы получаете объекты json. Typescript присутствует только во время сборки, он переносится в javascript, и это то, что выполняется во время выполнения.
4. Нет, я не использую new() и не создаю новые экземпляры. Я использую данные сервера напрямую. Код выглядит так: this. Информация = ожидает этого. HttpClient.get<Info[]>(this.Url). toPromise<Info[]>();
5. @toskv еще не тестировался, но, возможно, это уже ответ. Я должен перебирать данные сервера и создавать новые экземпляры, не полагаясь на автоматическое сопоставление. Я попробую это. Очень странно то, что автоматическое сопоставление работает должным образом, независимо от того, использую ли я PascalCase или camelCase. На мой взгляд, это должно вызвать ошибку, в которой говорится, что данные не могут быть правильно сопоставлены. Исходя из фона C #, это просто странно.
Ответ №1:
Почему это так? Это проблема с TypeScript или это проблема Angular?
Ни то, ни другое. Причина проблемы заключается в том, что данные json, поступающие с вашего веб-сервера, имеют не совсем ту структуру / формат, которые вы определили для информации о классе в typescript.
Существует ли неписаное соглашение, которому я должен следовать?
Ну, да, есть. Вы должны вручную протестировать и убедиться, что вы действительно получаете правильные структуры данных, прежде чем приводить их к определенным классам. Чтобы уточнить, вы должны взять json (тело HTTP-ответа), проанализировать его как JSON для универсального объекта, а затем проверить, действительно ли он имеет точно все свойства (с тем же именем и типами), что и класс (Информация), к которому вы собираетесь их применить. А затем сделайте это.
ОБНОВЛЕНИЕ: на самом деле есть отличный способ определить, относится ли объект к определенному типу, и сообщить typescript об этом, обеспечивая надежную защиту / защиту типа. В Typescript есть эта функция, называемая определяемыми пользователем функциями Typeguard, где вы определяете функцию, которая возвращает true или false, если объект проверяется на принадлежность к определенному типу.
// user-defined type-guard function
function isInfo(obj: Object): obj is Info {
if ('ManagedType' in obj amp;amp; '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, но вы определили класс 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[]>();
По сути, вы сообщаете typescript, что ответом будет массив информационных классов, определенных точно так же, как определение класса, но на самом деле в фактических данных json они таковыми не являются, поэтому difference JSON.parse() вернет объект, у которого имена свойств точно такие, как они есть в строке json, в camelCase вместо PascalCase, которые вы позволяете typescript предполагать.
// 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}`);
});
Комментарии:
1. Хорошо, спасибо вам. Я думаю, что было бы отличной помощью / идеей, если бы это было возможно, выдать ошибку времени выполнения или предупреждение, если разработчик не хочет слепого приведения, потому что, исходя из C #, это определенно выдало бы ошибку ….. но ладно, так оно и есть…. Спасибо всем комментариям и ответам!
2. спасибо за принятие. Я также обновил свой ответ конкретными ответами на каждый из ваших вопросов. не стесняйтесь спрашивать что-либо еще для получения дополнительных разъяснений.