Чи є спосіб проаналізувати рядки як JSON у Typescript.
Приклад: У JS ми можемо використовувати JSON.parse(). Чи існує подібна функція в Typescript?
У мене є рядок об’єкта JSON наступним чином:
{"name": "Bob", "error": false}
Чи є спосіб проаналізувати рядки як JSON у Typescript.
Приклад: У JS ми можемо використовувати JSON.parse(). Чи існує подібна функція в Typescript?
У мене є рядок об’єкта JSON наступним чином:
{"name": "Bob", "error": false}
JSON.parse, в результаті ви отримуєте об'єкт, а не a string(докладніше див. Мою відповідь). Якщо ви хочете перетворити об'єкт на рядок, тоді вам потрібно використовувати його JSON.stringify.
Відповіді:
Typescript - це (надмножина) javascript, тому ви просто використовуєте, JSON.parseяк і в javascript:
let obj = JSON.parse(jsonString);
Тільки те, що в машинописі ви можете мати тип для отриманого об'єкта:
interface MyObj {
myString: string;
myNumber: number;
}
let obj: MyObj = JSON.parse('{ "myString": "string", "myNumber": 4 }');
console.log(obj.myString);
console.log(obj.myNumber);
'{ "myString": "string", "myNumber": 4 }'на '{ "myString": "string", "myNumberBAD": 4 }'не провалиться, і obj.myNumber повернеться невизначеним.
Json.parse(text).validate[MyObj]. playframework.com/documentation/2.6.x/ScalaJson, як ви можете зробити те саме в машинописі (можливо, для цього існує зовнішня бібліотека?)?
MyObjне існує. Існує безліч інших потоків в SO про цю тему, наприклад: Перевірте, чи об’єкт реалізує інтерфейс під час виконання за допомогою TypeScript
JSON.parseВи можете продовжувати використовувати JSON.parse, оскільки TS - надмножина JS. Залишилася проблема: JSON.parseповернення any, що підриває безпеку типу. Ось два варіанти для сильніших типів:
На замовлення типу є найпростішим рішенням і часто достатнім для зовнішньої перевірки даних:
// For example, you expect to parse a given value with `MyType` shape
type MyType = { name: string; description: string; }
// Validate this value with a custom type guard
function isMyType(o: any): o is MyType {
return "name" in o && "description" in o
}
Потім JSON.parseобгортка може взяти захист типу як вхідне та повернути проаналізоване, набране значення:
const safeJsonParse = <T>(guard: (o: any) => o is T) => (text: string): ParseResult<T> => {
const parsed = JSON.parse(text)
return guard(parsed) ? { parsed, hasError: false } : { hasError: true }
}
type ParseResult<T> =
| { parsed: T; hasError: false; error?: undefined }
| { parsed?: undefined; hasError: true; error?: unknown }
Приклад використання:
const json = '{ "name": "Foo", "description": "Bar" }';
const result = safeJsonParse(isMyType)(json) // result: ParseResult<MyType>
if (result.hasError) {
console.log("error :/") // further error handling here
} else {
console.log(result.parsed.description) // result.parsed now has type `MyType`
}
safeJsonParse може бути розширено до швидкого збою або JSON.parseпомилок спроби / лову .
Запис захисних функцій типу вручну стає громіздким, якщо вам потрібно перевірити багато різних значень. Є бібліотеки, які можуть допомогти у виконанні цього завдання - приклади (без вичерпного списку):
io-ts: відн. популярний (на сьогодні 3,2 тис. зірок), fp-tsзалежність від однолітків, функціональний стиль програмуванняzod: досить новий (репо: 2020-03-07), прагне бути більш процедурним / об'єктно-орієнтованим, ніжio-tstypescript-is: Трансформатор TS для API компілятора, потрібна додаткова обгортка типу ttypescripttypescript-json-schema/ ajv: Створіть схему JSON із типів і перевіріть її за допомогоюajvЯкщо ви хочете, щоб ваш JSON мав перевірений тип Typescript, вам доведеться виконати цю перевірку самостійно. Це нічого нового. У звичайному Javascript вам потрібно буде зробити те саме.
Я люблю висловлювати свою логіку перевірки як набір "перетворень". Я визначаю Descriptorяк карту перетворень:
type Descriptor<T> = {
[P in keyof T]: (v: any) => T[P];
};
Тоді я можу зробити функцію, яка застосовуватиме ці перетворення до довільного введення:
function pick<T>(v: any, d: Descriptor<T>): T {
const ret: any = {};
for (let key in d) {
try {
const val = d[key](v[key]);
if (typeof val !== "undefined") {
ret[key] = val;
}
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
throw new Error(`could not pick ${key}: ${msg}`);
}
}
return ret;
}
Тепер я не тільки перевіряю свій вхід JSON, але й будую тип Typescript по ходу. Вищезазначені загальні типи гарантують, що результат визначає типи з ваших "перетворень".
У випадку, якщо перетворення видає помилку (саме так ви б реалізували перевірку), мені подобається обернути її іншою помилкою, показуючи, який ключ спричинив помилку.
У вашому прикладі я б використав це наступним чином:
const value = pick(JSON.parse('{"name": "Bob", "error": false}'), {
name: String,
error: Boolean,
});
Тепер valueбуде набрано, оскільки Stringі Booleanє обома "трансформаторами" в тому сенсі, що вони беруть вхід і повертають набраний вихід.
Крім того, valueбуде фактично буде таким типом. Іншими словами, якщо вони nameбули насправді 123, він буде перетворений на "123"так, щоб у вас був дійсний рядок. Це тому, що ми використовували Stringпід час виконання вбудовану функцію, яка приймає довільне введення і повертає astring .
Ви можете бачити, як це працює тут . Спробуйте наступні речі, щоб переконати себе:
const valueвизначення, щоб побачити, що спливаюче вікно показує правильний тип."Bob"щоб 123і повторно запустити зразок. У вашій консолі ви побачите, що ім'я було належним чином перетворено у рядок "123".nameнасправді 123це було перетворено на "123". Це, здається, неправильно. Я valueповертаюся {name: 123..не {name:"123"..тоді, коли
123замість "Bob").
Transformedтип. Ви можете просто використовувати Object. type Descriptor<T extends Object> = { ... };
TransformedТип абсолютно НЕ потрібен. Відповідь я оновив відповідно.
123автоматично перетворюватися на рядок "123", оскільки це число в об'єкті JSON.
Ви можете додатково використовувати бібліотеки, які виконують перевірку типу вашого json, наприклад Sparkson . Вони дозволяють визначити клас TypeScript, на який ви хочете проаналізувати свою відповідь, у вашому випадку це може бути:
import { Field } from "sparkson";
class Response {
constructor(
@Field("name") public name: string,
@Field("error") public error: boolean
) {}
}
Бібліотека перевірить, якщо обов’язкові поля присутні в корисному навантаженні JSON і якщо їх типи правильні. Він також може здійснити купу перевірок та перетворень.
Для нього існує чудова бібліотека ts-json-object
У вашому випадку вам потрібно буде запустити такий код:
import {JSONObject, required} from 'ts-json-object'
class Response extends JSONObject {
@required
name: string;
@required
error: boolean;
}
let resp = new Response({"name": "Bob", "error": false});
Ця бібліотека перевірить json перед розбором
JSON.parse доступний у TypeScript, тому ви можете просто використовувати його:
JSON.parse('{"name": "Bob", "error": false}') // Returns a value of type 'any'
Однак вам часто хочеться проаналізувати об'єкт JSON, переконавшись, що він відповідає певному типу, а не мати справу зі значенням типу any. У цьому випадку ви можете визначити таку функцію, як наступна:
function parse_json<TargetType extends Object>(
json: string,
type_definitions: { [Key in keyof TargetType]: (raw_value: any) => TargetType[Key] }
): TargetType {
const raw = JSON.parse(json);
const result: any = {};
for (const key in type_definitions) result[key] = type_definitions[key](raw[key]);
return result;
}
Ця функція приймає рядок JSON та об'єкт, що містить окремі функції, які завантажують кожне поле об'єкта, який ви створюєте. Ви можете використовувати його так:
const value = parse_json(
'{"name": "Bob", "error": false}',
{ name: String, error: Boolean, }
);