#typescript #loops #typescript-typings #typescript-generics
#typescript #циклы #typescript-типизации #typescript-generics
Вопрос:
Я знаю, что об этом спрашивали уже много раз, и я проверил все другие ответы — но все еще не удалось решить мою ситуацию, так что здесь…
У меня довольно сложная структура из-за внешних ограничений (форма, выбор входных данных, DB API).
Я получаю входные данные из пользовательской формы в свой объект. Одним из этих элементов является поле выбора с несколькими вариантами выбора, которое возвращает массив значений (дни недели). Он может возвращать переменное количество значений в виде массива: одно значение или даже семь значений.
Я хочу выполнить цикл по этим значениям массива и использовать их в качестве ключей для установки логического значения моего другого объекта.
С этой попыткой # 1 TypeScript не компилируется и жалуется, что:
Type 'true' is not assignable to type 'undefined'.(2322)
С попыткой # 2 Typescript не компилируется, потому что:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'IDayOfWeek'.
No index signature with a parameter of type 'string' was found on type 'IDayOfWeek'.(7053)
Сначала я намеревался сообщить Typescript, что все эти значения являются boolean
. Я пытался фильтровать ключи, чтобы принимать только логические значения, но не справился, затем я попытался поиграть с реализацией некоторых идей, которые я нашел здесь, но пока у меня ничего не получалось, или я не смог реализовать это из-за ограничений.
Раньше в JS это было легко, но в Typescript … ну что ж … 😉
Я был бы признателен за любую помощь, спасибо!
// I can't change this:
interface IDayOfWeek {
sunday?: boolean;
monday?: boolean;
tuesday?: boolean;
wednesday?: boolean;
thursday?: boolean;
friday?: boolean;
saturday?: boolean;
someOtherValue?: string;
andAnother?: Date;
}
// I am obligate to use this Select structure:
interface ISelectOption<T> {
label: string;
value: T;
}
// I have a limited control of this (React form is mapped to this object with a hook):
interface IUserInput {
someInputName: string;
weekday: Array<ISelectOption<keyof IDayOfWeek>>;
}
// and here's where I'm stuck:
function saveUserInput(inp: IUserInput) {
let dayOfWeek: IDayOfWeek = {};
// Attempt #1
for(let key in inp.weekday) {
dayOfWeek[inp.weekday[key].value] = true;
}
// Attempt #2
for(let key in dayOfWeek) {
if(inp.weekday.hasOwnProperty(key)) {
dayOfWeek[key] = true;
}
}
}
Ответ №1:
Это вполне возможно сделать без изменений IDayOfWeek
.
Проблема в том, что keyof IDayOfWeek
(в IUserInput
) недостаточно конкретен, т. Е. Мы получаем дни недели, которые имеют логические значения, но мы также получаем someOtherValue
значения, которые не являются логическими. Таким образом, мы не знаем, правильный ли у нас тип, когда мы назначаем true
.
Решение состоит в том, чтобы использовать только ключи, которые имеют логические значения, поэтому один из вариантов — просто вручную вывести их список:
interface IUserInput {
someInputName: string;
weekday: Array<ISelectOption<'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday'>>;
}
Другой, более популярный вариант — использовать вспомогательный тип для автоматического получения ключей, соответствующих логическому значению:
// Helper to only get the keys that match a particular value.
type FilterKeys<T, U> = NonNullable<{ [K in keyof T]: T[K] extends (U | undefined) ? K : never }[keyof T]>;
interface IUserInput {
someInputName: string;
weekday: Array<ISelectOption<FilterKeys<IDayOfWeek, boolean>>>;
}
Комментарии:
1. Это именно то, что мне было нужно! Когда я попробовал что-то подобное — он пожаловался, потому что пытался установить значение типа «никогда». Я думаю, что это ваше добавление NonNullable исправляет эту часть. Спасибо!
2. Да, определенно, мне пришлось немного повозиться с вспомогательным типом, а не с наиболее читаемым кодом. NP
Ответ №2:
Мне удалось исправить ситуацию, чтобы она работала так, как, я думаю, вы предполагаете, чтобы они работали, смотрите Код ниже или эту ссылку на игровую площадку.
Единственное, что не работает, — это наличие someOtherValue
и andAnother
в IDayOfWeek
интерфейсе, потому что они не имеют логического типа, поэтому попытка присвоить логическое значение любому из них приводит к ошибке типа.
Кроме этого, с несколькими изменениями из вашего кода все компилируется
interface IDayOfWeek {
sunday?: boolean;
monday?: boolean;
tuesday?: boolean;
wednesday?: boolean;
thursday?: boolean;
friday?: boolean;
saturday?: boolean;
// These ⬇ won't work if you're assigning boolean values ❌
// someOtherValue?: string;
// andAnother?: Date;
}
interface ISelectOption<T> {
label: string;
value: T;
}
interface IUserInput {
someInputName: string;
weekday: ISelectOption<keyof IDayOfWeek>[]; // Note the use of keyof, not typeof
}
function saveUserInput(inp: IUserInput) {
let dayOfWeek: IDayOfWeek = {};
// This works ✅ as long as IDayOfWeek doesn't have any keys with non-boolean values!
for (const key of inp.weekday) {
dayOfWeek[key.value] = true;
}
}
Комментарии:
1. Я знаю об этом 🙂 но, к сожалению, я не могу изменить эту часть. Оно поступает из внешнего источника.
2. Почему вы пытаетесь присвоить логические значения не булевым полям?