#json #typescript
#json #typescript
Вопрос:
У меня есть функция, определенная как:
export function useSubmitHandler(url: string, data: Json): [FormEventHandler<HTMLFormElement>, boolean] {}
Где Json
:
type JsonPrimitive = string | number | boolean | null | undefined
interface JsonMap extends Record<string, JsonPrimitive | JsonArray | JsonMap> {}
interface JsonArray extends Array<JsonPrimitive | JsonArray | JsonMap> {}
export type Json = JsonPrimitive | JsonMap | JsonArray
Однако, если я попытаюсь вызвать его с помощью произвольного интерфейса, я получу сообщение об ошибке:
TS2345: Argument of type 'Fee' is not assignable to parameter of type 'Json'.
Type 'Fee' is not assignable to type 'JsonMap'.
Index signature is missing in type 'Fee'
Но если я вызываю его с тем же объектом, но распространяю, как {...store.data}
тогда ошибка исчезнет.
Как я могу ввести useSubmitHandler
правильно, чтобы он принимал любой объект, который можно преобразовать в строку JSON?
Я думаю Json
, что тип правильный, но ему нужно что-то большее, чтобы разрешить передачу произвольных типов в функцию.
Fee
является ли:
interface Fee {
id: number|null;
name: string;
type: string;
amount: string;
default: number;
company_id: number;
deleted_at?: any;
active: number;
fee_or_discount: string;
}
Конечно, я бы хотел, чтобы это работало с любым типом.
Комментарии:
1. Как
Fee
выглядит тип?2. JSON — это текстовый формат. Единственный тип, совместимый с JSON, это
string | undefined | null
3. @Evert Я добавил определение
Fee
к вопросу. Функция должна принимать любой тип, который может быть преобразован в JSON.4. @HereticMonkey Я знаю, что JSON — это строка, но когда люди говорят «JSON», они обычно имеют в виду простой старый объект JS, а не текстовое представление. Мы говорим
JSON.stringify
здесь, а неJSON.parse
.5. @mpen Ну, «люди» ошибаются, и чем чаще они ошибаются, тем сложнее обсуждать проблемы сериализации в формат и из формата. Когда вы говорите JSON и имеете в виду объект, вы повышаете вероятность того, что люди будут писать
JSON.parse(jsonObject)
, когдаjsonObject
это уже объект, и мы получаем 1552-й вопрос о «Неизвестном токене ‘o’ …»
Ответ №1:
Вариант 1: переопределить Json
тип
type JsonPrimitive = string | number | boolean | null;
type JsonMap = {
[key: string]: JsonPrimitive | JsonMap | JsonArray;
}
type JsonArray = Array<JsonPrimitive | JsonMap | JsonArray>;
type Json = JsonPrimitive | JsonMap | JsonArray;
Вариант 2: добавьте подпись индекса в Fee
интерфейс
interface Fee {
[property: string]: any;
id: number|null;
name: string;
type: string;
amount: string;
default: number;
company_id: number;
deleted_at?: any;
active: number;
fee_or_discount: string;
}
Вариант 3. добавьте встроенное утверждение типа для подписи индекса, например:
useSubmitHandler(Router.route('fees.store')!, store.data as {[property: string]: any})
Смотрите также
Проблема с TypeScript GitHub Пожалуйста, укажите json
базовый тип # 1897
Комментарии:
1. Первое решение, похоже, работает. Не уверен, почему он ведет себя иначе, чем
extends
решение, но я возьму его!
Ответ №2:
Один из возможных способов справиться с этим — включить store.data
функцию, которая применяет подпись индекса.
const indexed = <T extends {}>(obj: T): T amp; {[key: string]: never} => obj;
Эта функция возвращает тот же объект, который был задан, но добавляет дополнительную информацию о typescript. Мы сохраняем все значения ранее известного типа T
без изменений, но добавляем подпись индекса {[key: string]: never}
, которая сообщает typescript, что любые ключи, отличные от тех, что в типе, не могут быть ничем иным, кроме как неопределенным.
Вы можете вызвать свой обработчик следующим образом
useSubmitHandler(url, indexed(store.data))
Другой вариант — изменить ваше определение Json
таким образом, чтобы у него не было подписи индекса, которая исходит от использования Record
. Но я предполагаю, что это было бы менее желательно, поскольку (я думаю) вам пришлось бы расширить то, что разрешено.