Плохо
Вы могли бы определить свою форму как
interface Timings {
start: string | null;
end: string | null;
length: string | null;
}
, но этот дизайн не может правильно выразить вашу проблему.Например, { start: string, end: null, length: null }
является правильным на уровне типа, но представляет недопустимое состояние в вашей программе.
Если мы должны были сделать:
if (timingsObject.start) {
typeof timingsObject.length.toString(); // Compile-type error!
}
мы знаем, что timingsObject.length
являетсястрока, но TypeScript нет.Прежде чем можно будет использовать любое другое свойство timingsObject
, его необходимо проверить как первое string
.
Хорошо
Смоделируйте вашу проблему как суммудвух государств.Первый четко определен:
interface Timings {
start: string;
end: string;
length: string;
}
, второй является его пустым эквивалентом.Вы вводите либо тот, либо другой, что может быть выражено как:
declare const timings: Empty<Timings> | Timings;
Как мы моделируем Empty
?Давайте сделаем это отображаемым типом, который превращает все в null
.
type Empty<T> =
T extends object
? { [K in keyof T]: null }
: null
declare function isEmpty<T>(argument: T | Empty<T>): argument is Empty<T>;
Теперь компилятор знает, что если одно свойство является строкой, то любое другое свойство также является строкой.
if (!isEmpty(timings)) {
timings.start + timings.end; // Both are known to be strings!
}
Это решение может выглядеть как потраченная работа (и оно есть), но оно успешно превращает проблему вашего домена в систему типов.