Внимание! Это моя интерпретация (теория) того, что происходит. Это не подтверждено.
Похоже, что проблема связана с обработкой необязательных полей.
Когда вы определяете этот тип:
interface MyTypeOptional {
value?: number;
}
TypeScript ожидает, что значение будет иметь тип number | never
.
Так что при его распространении
const withOptional: MyTypeOptional = {};
const spread = { ...withOptional };
Тип объекта распространения должен быть либо { value: never | number }
, либо { value?: number }
.
К сожалению, из-за неоднозначного использования необязательного поля и неопределенного TypeScript выводит объект распространения следующим образом:
value?: number | undefined
Таким образом, необязательный тип поля приводится к number | undefined
при распространении.
Что не имеет смысла, так это то, что когда мы распространяем не необязательный тип, он переопределяет необязательный тип:
interface MyTypeNonOptional {
value: number;
}
const nonOptional = { value: 1 };
const spread2 = { ...nonOptional, ...withOptional }
Похоже на ошибку? Но нет, здесь TypeScript не приводит дополнительное поле к number | undefined
. Здесь он преобразуется в number | never
.
. Мы можем подтвердить это, изменив необязательный параметр на явный |undefined
:
interface MyTypeOptional {
value: number | undefined;
}
Здесь следующий спред:
const withOptional: MyTypeOptional = {} as MyTypeOptional;
const nonOptional = { value: 1 };
const spread2 = { ...nonOptional, ...withOptional }
будет выведено как { value: number | undefined; }
И когда мы изменим его на необязательный или неопределенный:
interface MyTypeOptional {
value?: number | undefined;
}
, оно снова будет сломано и выведено как { value: number }
Так в чем же обходной путь?
Я бы сказал, не используйте дополнительные поля, пока команда TypeScript их не исправит.
Это имеет несколько последствий:
- Если вам нужно, чтобы ключ объекта был пропущен, если он не определен, используйте
omitUndefined(object)
- . Если вам нужно использовать Partial, ограничьте его использование фабрикой:
<T>createPartial (o: Partial<T>) => Undefinable<T>
. Так что результат createPartial<MyTypeNonOptional>({})
будет выводиться как { value: undefined }
или Undefinable<MyTypeNonOptional>
. - Если вам нужно, чтобы значение поля было
unset
, используйте для этого null
.
Существует открытый вопрос об этом ограничении дополнительных полей: https://github.com/microsoft/TypeScript/issues/13195