Всем привет!

Долго думал какой заголовок написать. В голову приходило в том числе шуточное "Массив - это тоже объект, только маленький еще", но на самом деле все совсем наоборот: Массив (Array) - это производное от объекта (Object). То есть в массивах, как и в объектах, можно использовать не только "скобочную нотацию", но и "точечную".

Сейчас чуть подробнее разверну вводную часть. Когда мы изучали массивы, мы проходили Доступ к данным массива, используя индексы. Пример:
const array = [50,60,70]; array[0]; // равно 50 const data = array[1]; // равно 60
Это и есть "скобочная нотация", то есть использование квадратных скобок для доступа к элементам массивов. В объектах для доступа к свойствам тоже можно использовать скобочную нотацию. Это рассматривалось в уроке Доступ к свойствам объекта с помощью скобок. А в уроке Доступ к свойствам объекта через точку рассматривалась "точечная нотация". Но вот к элементам массива вы не можете обратиться через точку, так как имена свойств в объектах (как и прочие переменные) не могут начинаться с числа. А как нам известно, у массивов индексы являются числами (во всяком случае нам четко говорили, что очередность индексов в массивах, как и в строках, начинается с числа 0). Но действительно ли только числа могут быть индексами в массивах? На самом деле нет. И прежде, чем мы рассмотрим примеры, следует вот что запомнить: все имена свойств объектов автоматически приводятся к типу "строка". Это значит, что если объекту задать в качестве имени свойства какое-либо число, оно автоматически переводится в строку. Этот момент отдельно уточняется в уроке Создание объектов JavaScript. И что же тогда получается у нас с индексами массивов? Если мы говорим, что индекс - это то же имя свойства, и если все имена свойств в объектах переводятся в строки, то и передаваемые индексы в массивах тоже переводятся в строки? Да, так и есть. И давайте убедимся в этом.
arr = [10,20,30] arr // (3) [10, 20, 30] arr[1] // 20 arr['1'] // 20
То есть здесь, как мы видим, мы в качестве индекса передали строку и получили то же самое значение, что и по числовому индексу. Но это может еще ничего не доказывать. Как и в случае с нестрогим сравнением, у нас может просто происходить преведение строки в типу Число. Но это предположение легко опровергнуть.
arr['1str'] // undefined arr['1str'] = 123; arr['1str'] // 123 arr[parseInt('1str')] // 20
Как мы видим, при первом обращении к элементу по строковому индексу, мы получаем undefined, так как такого элемента нет. А вот после мы задаем значение с таким индексом и потом успешно получаем данное значение. При этом если мы выполним парсинг числа из этой строки (в результате чего получаем 1), то успешно получаем значение элемента с индексом 1. То есть никакого приведения строки к числу не выполняется.

Но тут вопрос: вот мы добавили значение с каким-то непонятным индексом. А как теперь будет выглядеть наш массив? А вот так:
(3) [10, 20, 30, 1str: 123] 0: 10 1: 20 1str: 123 2: 30 length: 3
То есть как мы видим, в нем есть и все 3 предыдущих элемента, да еще и наш новый добавленный элемент. При этом очередность свойств сохранена. То есть это не тот случай, когда котлеты отдельно, мухи отдельно. Тут явно все общим скопом лежит.
Но обратите внимание вот на что: length у него по-прежнему 3. То есть элемента 4, а длина массива - 3. Более того, если выполнить такой код:
arr.map((n) => console.log(n))
То получим результат
10 20 30
То есть массив нам отдает только те элемены, которые добавлены с цифровым индексом. Интересное поведение, не правда ли?

А вот можно еще вот такой забавный вариант рассмотреть:
arr = Object.assign([], { 0: 0, 1: 10, str: "Sdfdsf", '2': 20, }); // Результат (3) [0, 10, 20, str: 'Sdfdsf'] 0: 0 1: 10 2: 20 str: "Sdfdsf" length: 3
Если вам не известен метод Object.assign(), обязательно изучите его. Крайне полезный и довольно часто используется. Суть его в том, что он объединяет два и более объекта в один (прибавляя к первому все последующие переданные поочередно).

В нашем случае мы передали в него новый пустой массив [] и вторым объектом передали другой объект, содержащий произвольные свойства со значением. И как видим, на выходе мы получили массив с привычными свойствами :) То есть числовые ключи отправились в общий список элементов массива, а нечисловые в обычные свойства. Ну и на счет числовых: как мы видим, те свойства, которые могли быть легко из строк перебиты в числа (а у нас были передана строка '2'), так же ушли в список элементов. Вот такие интересности :)

Уже в процессе написания статьи, перепроверяя некоторые моменты и рассуждая, я задумался "А как же можно перехвотить обновление объекта?". Ведь очевидно, что на массив наложены многие обработчики, цель которых - при изменении его свойств разобраться, что отправить в общий набор элементов, а что просто как свойство применить. И тут я наткнулся на весьма занятную штуку - Proxy. Крутейшая же штука! :) Получается, есть возможность создать объект и навесить обработчики, которые будут срабатывать на всякие обновления. Пример:
const validator = { set: function(target, key, value) { console.log(`The property ${key} has been updated with ${value}`); return true; } }; const store = new Proxy({}, validator); store.a = 'hello';
Выполнив store.a = 'hello', то есть добавив объекту новое свойство со значением, вы получите сообщение "The property a has been updated with hello".

А выполнив такое:
Object.assign(store, { 1: "SDfsdf", 2: "Dfgfdg", '3': "Dfgfdg", })
Вы получите 3 сообщения:
The property 1 has been updated with SDfsdf
The property 2 has been updated with Dfgfdg
The property 3 has been updated with Dfgfdg

Таким образом, как бы мы не обновляли объект, у нас есть возможность перехватить этот момент и решить что с ним делать дальше. Вполне возможно что-то типа такого и происходит в массивах.

Заключение: этот материал вряд ли можно использовать как-то в практических задачах, но такие игры расширяют кругозор и учат более глубоко понимать изучаемую область. Так что не стесняйтесь тратить время на такое баловство :)

Дима, просьба: если есть ошибки, выливай с --no-verify их. А то я запустил проверку типов, а ошибок нигде нет. Пришлось в коммитах искать и у сбея по файлам. Конечно, это не сложно, но все-таки по ошибкам проще.

А проблема у тебя простая: тут какой тип?
export interface NextSeoProps { title?: string;
string | undefined

А ты что передаешь?
const title: string | string[] | undefined
Массив строк здесь явно лишний. Да, в УРЛ GET-параметры могут быть не только просто строки, но и массивы. Вот если ты в титл хочешь передать, то тебе надо исключить массивы, или привести их к строке.
Первый вариант:
const title = router.query.title && typeof router.query.title === "string" ? router.query.title : undefined;
Здесь если есть значение и тип этого значения - строка, то ее и получаем. В противном случае undefined. То есть чтобы тут ни пришло кроме строки, оно будет заменено на undefined. Тайпскрипт такие конструкции понимает и здесь ему четко ясно: title - это строка или undefined.

Обрати внимание на то, что здесь условие двойное. Первое - это проверка на значение. То есть если на вход придет пустая строка, она не будет соответствовать этому значению и в итоге будет undefined. Но ежели ты хочешь получать в том числе и пустую строку (что зависит от логики), то следует делать так:
const title = typeof router.query.title === "string" ? router.query.title : undefined;
Здесь title может получить в том числе пустую строку.

Второй вариант:
const title = typeof router.query.title === 'string' ? router.query.title : Array.isArray(router.query.title) ? router.query.title.join(', ') : undefined
Здесь, помимо предыдущей проверки, добавляется проверка на массив. Если значение - массив, то мы возвращаем его элементы, объединив запятой, используя метод Array.prototype.join().

Собственно, решение второй ошибки точно такое же.

Игорь, все верно, для доступа к свойствам объектам можно использовать точечную или скобочную нотацию. Но надо понимать между ними разницу: в скобочной ты щлесь передаешь переменную, которая может иметь разные значения, а в точечной у тебя не переменная передается, а именно само имя переменной. Вот этот урок тебе поможет лучше понять в чем тут твоя ошибка: Доступ к свойствам объектов через переменные.
// Setup var myObj = { gift: "pony", pet: "kitten", bed: "sleigh" }; function checkObj(checkProp) { // Your Code Here if (myObj.hasOwnProperty(checkProp)) { return myObj[checkProp]; //если поменять на return myObj.checkProp; то задание будет не выполнено, хотя в описании ранее было "Вы можете использовать точечную или скобочную нотацию для доступа к объектам." } else { return "Not Found"; } } // Test your code by modifying these values checkObj("gift");
Сергей, советую изучить Типы данных JavaScript и структуры данных.
Массив (Array) - это не самостоятельный примитивный тип, а производный от Object.
Array instanceof Object === true; typeof [] === 'object';
При этом Object и Array - это уже существующие объекты и они имеют самостоятельные методы (так же, как и объект Math имеет свои методы типа max, floor, random и т.п., часть из которых вы наверняка уже встречали ранее, например в этом и этом уроках).

А привычное нам объявление переменных чаще всего является просто синтаксическим сахаром, например:
const obj = {} const obj2 = new Object(); const arr = []; const arr2 = new Array();
И вот методы этих объектов (Array, Object, Number, Math и т.п.) могут быть использованы просто как самостоятельные функции для определенных задач.
К примеру метод Array.isArray(variables) позволяет проверить является ли переменная массивом (typeof нам в этом не поможет, потому что typeof {} === "object" и typeof [] === "object").
А вот метод Object.freeze() вы наверняка недавно встречали в этом уроке: Предотвращение мутации объектов. При этом, как я там писал в замечании, это работает не только с объектами, но и с массивами (что и есть тоже суть объекты).

Так вот, метод Object.keys() - это специальный метод, чтобы взять все ключи объекта и вернуть их в новом массиве. Напомню: суть объекта - это Структура Ключ-Значение. К примеру:
const obj = { key1: 'val1', key2: 'val2', key3: 'val3', }
Мы видим здесь у объекта три ключа (имена свойств) и по ним три значения. Вот в результате Object.keys(obj) мы получим массив со всеми этими ключами, то есть ['key1,' 'key2', 'key3'].

А есть еще Object.values(), и он работает так же, только возвращает не ключи, а значения.
Не понятно почему используется "Object." Какой это обьект? Откуда взялся? И вообще в задании вернуть масив а не обьект.