Как определить JSON-совместимый параметр, чтобы избежать ошибки «Отсутствует подпись индекса»?

#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 . Но я предполагаю, что это было бы менее желательно, поскольку (я думаю) вам пришлось бы расширить то, что разрешено.