Вопрос или проблема
type LanguageKey =
| 'c++'
| 'python'
| 'javascript'
| 'typescript';
class Test {
languages: Partial<Record<LanguageKey, string>> = {};
needsString(str: string): void { console.log(str);}
useLangKey(langKey: LanguageKey): void {
if (langKey in this.languages) {
const language = this.languages[langKey];
this.needsString(language); // <-- "'string | undefined' не может быть присвоен 'string'" ??
}
}
}
TS жалуется на отмеченной строке, что “Аргумент типа ‘string | undefined’ не может быть присвоен параметру типа ‘string’.”. Разве проверка langKey in this.languages
не должна быть достаточной?
Рассмотрим:
const foo = {
bar: undefined
}
console.log('bar' in foo); // равно 'true'
Поскольку вы использовали Partial
, вы сделали undefined
допустимым значением для свойств в этом объекте.
Проверка, которая вам нужна:
if (this.languages[langKey] !== undefined) {
🤔
Как вы гарантируете, что запись существует? Undefined не входит ни в String, ни в LanguageKey, и я думаю, что именно это вызывает жалобу.
Вы можете исправить это, добавив проверку на пустое значение
...
const language = this.languages[langKey];
if(!language) throw Error();
this.needsString(language);
...
Ответ или решение
Вопрос, с которым вы столкнулись, требует внимательного рассмотрения особенностей работы TypeScript и механизма, который стоит за проверкой наличия ключей в объектах. Давайте разберемся, почему конструкция key in object
не достаточно, чтобы удовлетворить TypeScript при работе с типом, который может принимать значение undefined
.
Введение в проблему
Вам представлена классическая ситуация, когда вы используете тип Partial<Record<LanguageKey, string>>
для определения свойств объекта, который может содержать не все ключи и допускает undefined
в качестве значений. Данная конструкция позволяет заявить, что свойства объекта могут отсутствовать, но также подразумевает, что при этом элементы, которые существуют, могут иметь значение string
или undefined
.
Почему key in object
недостаточно
-
Проверка на существование ключа: Когда вы используете
if (langKey in this.languages)
, TypeScript успешно определяет, что ключ действительно существует в объекте. Однако TypeScript не может гарантировать, что значение, соответствующее данному ключу, не будетundefined
. Это связано с тем, что вы используетеPartial
, что делает возможным существование свойств с неопределёнными значениями. -
Тип значения переменной: После проверки ключа вы выполняете
const language = this.languages[langKey];
. На этом этапеlanguage
может иметь типstring | undefined
, так как значением свойства, которое вы извлекаете, вполне может бытьundefined
. Когда вы пытаетесь передатьlanguage
в методneedsString
, TypeScript не позволяет это сделать, так как метод ожидает аргумент типаstring
, иundefined
не подходит для этого параметра.
Способы решения проблемы
Чтобы справиться с этой проблемой, есть несколько подходов:
-
Проверка на
undefined
: Вы можете добавить дополнительную проверку наundefined
перед передачей переменной в методneedsString
. Например:const language = this.languages[langKey]; if (language !== undefined) { this.needsString(language); } else { // Обработка случая, когда язык отсутствует }
-
Использование нулевых значений через оператор опциональной цепочки: Вы можете использовать оператор опциональной цепочки, чтобы избежать передачи
undefined
значений:this.needsString(this.languages[langKey] ?? "default value");
Заключение
Таким образом, конструкция key in object
не может сама по себе гарантировать, что значение свойства не окажется undefined
. Поэтому важно не забывать о дополнительных проверках, чтобы обеспечить строгую типизацию и избежать возможных ошибок во время выполнения программы. Это ключевой момент, которымTypeScript стремится поддерживать качество кода, делая его более устойчивым к ошибкам.