В JavaScript (и, соответственно, TypeScript) есть два нижних типа: null
и undefined
. Они предназначены для обозначение разных вещей:
- Что-то еще не инициализированное:
undefined
. - Что-то недоступное в данный момент:
null
.
Факт в том, что вам придется столкнуться с обоими. Просто проверьте их через ==
/// Представьте, что вы делаете `foo.bar == undefined`, где bar может быть одним из:
console.log(undefined == undefined); // true
console.log(null == undefined); // true
// Вам не нужно переживать из-за ложных значений в этой проверке
console.log(0 == undefined); // false
console.log('' == undefined); // false
console.log(false == undefined); // false
Поручите == null
проверить undefined
или null
. Как правило, нет необходимости делать между ними различия.
function foo(arg: string | null | undefined) {
if (arg != null) {
// arg должен быть строкой, поскольку `!=` исключает и null и undefined.
}
}
Одно исключение: значения undefined корневого уровня, которые мы обсудим далее.
Помните как я сказал вам использовать == null
? Конечно помните (ведь это было только что ^). Не используйте его для вещей корневого уровня. В строгом режиме, если вы используете foo
и foo
является undefined, вы получите исключение ReferenceError
и весь стек вызовов прервётся.
Вы должны использовать строгий режим ... и на самом деле компилятор TS включит его для вас, если вы используете модули ... подробнее об этом будет сказано позже, так что не парьтесь :)
Поэтому чтобы проверить задана ли переменная на глобальном уровне, используйте typeof
:
if (typeof someglobal !== 'undefined') {
// someglobal теперь можно безопасно использовать
console.log(someglobal);
}
Потому что TypeScript дает вам возможность документировать ваши структуры отдельно от значений. Вместо того чтобы делать так:
function foo(){
// if Something
return {a:1,b:2};
// else
return {a:1,b:undefined};
}
вы должны использовать аннотацию типов:
function foo():{a:number,b?:number}{
// if Something
return {a:1,b:2};
// else
return {a:1};
}
Функции колбеков в стиле Node (вроде (err,somethingElse)=>{ /* что-нибудь */ }
) обычно вызываются с err
равным null
, если нет ошибок. Как бы то ни было, в общем случае просто используйте проверку на истинность:
fs.readFile('someFile', 'utf8', (err,data) => {
if (err) {
// сделать что-нибудь
} else {
// ошибок нет
}
});
Когда создаёте свои собственные API, в подобных ситуациях нормально использовать null
для согласованности. Но, честно говоря, лучше посмотреть в сторону промисов, тогда вам вообще не придется париться из-за пустых значений ошибок (вы обработаете их через .then
и .catch
).
Для примера, ужасная функция:
function toInt(str:string) {
return str ? parseInt(str) : undefined;
}
может быть написана намного лучше:
function toInt(str: string): { valid: boolean, int?: number } {
const int = parseInt(str);
if (isNaN(int)) {
return { valid: false };
}
else {
return { valid: true, int };
}
}
В стандарте JSON есть поддержка кодирования null
, но нет undefined
. При кодировании в JSON объекта, с атрибутом равным null
, атрибут будет включён с нулевым значением, в то время как атрибут со значением undefined
будет полностью исключён.
JSON.stringify({willStay: null, willBeGone: undefined}); // {"willStay":null}
В результате, базы данных, основанные на JSON могут поддерживать значения null
, но не undefined
. Поскольку атрибуты со значением null
закодированы, вы можете намеренно очистить атрибут, установив его значение в null
перед кодированием и передав объект в удаленное хранилище.
Установка значений undefined
может сэкономить память и затраты на передачу, поскольку названия атрибутов не будут закодированы. Однако, это может усложнить семантику очистки значений по сравнению с отсутствующими значениями.
Команда TypeScript не использует null
: TypeScript coding guidelines и это не вызвало никаких проблем. Дуглас Крокфорд считает, что null
- это плохая идея и всем нам лучше использовать undefined
.
Как бы то ни было, стиль NodeJS использует null
для аргументов Error в качестве стандарта, посколько это означает Что-то в настоящий момент недоступно
. Лично я не хочу разрываться между ними двумя, ведь большинство проектов используют библиотеки с разными подходами, и в обоих случаях использую просто == null
.