"값이 없다는 말 하나로 셋을 뭉개면, 호출자가 언제 무엇을 기대해야 하는지 사라져."
null과 undefined는 다른 값이야
null은 보통 ‘의도적으로 비어 있음’을 나타내고, undefined는 값이 아직 주어지지 않았거나 속성이 존재하지 않음을 나타내. JavaScript 실행에서는 둘이 별개의 값이고, 엄격한 null 검사에서도 각자 자기 타입을 가져. 둘 다 허용하려면 string | null | undefined처럼 명시해야 해.
프로젝트 안에서는 한 상황에 어느 값을 쓸지 관례를 정하는 편이 좋아. 데이터베이스의 빈 칸은 null, 생략 가능한 함수 인자는 undefined처럼 경계를 나누면 호출자가 뜻을 추측하지 않아도 돼. 둘을 아무 기준 없이 섞으면 모든 함수가 두 경우를 함께 처리하게 돼.
선택 매개변수와 선택 속성
name?: string은 값이 생략될 수 있다는 뜻이어서 읽는 자리에서는 대개 string | undefined로 다뤄. 다만 속성이 아예 없는 상태와 { name: undefined }는 실행 중 다를 수 있어. exactOptionalPropertyTypes를 켜면 이 차이를 타입에도 드러낼 수 있지.
기본값이 있는 매개변수는 함수 안에서 더 좁아져. function greet(name = 'anon')에서 name은 함수 몸 안에서 string이야. 호출자가 생략했거나 undefined를 넘겨도 기본값이 채워지기 때문이야.
void는 ‘undefined 타입’이 아니야
함수 반환 위치의 void는 호출자가 반환값을 사용하지 않겠다는 계약이야. 실제 함수가 값을 반환해도 그 값을 무시하는 콜백 자리에는 대입될 수 있어. 반면 () => undefined는 정말 undefined를 반환해야 한다는 더 좁은 약속이야.
변수 타입으로 void를 쓰는 일은 거의 없어. 함수의 반환값을 관찰하지 않는 API에서 의미가 있을 뿐이야. ‘아무것도 돌려주지 않는다’는 설명만 외우면 콜백 호환성에서 헷갈리니, ‘호출자가 돌려받은 값을 쓰지 않는다’고 이해해.
좁히기와 기본값
nullable 값을 쓰기 전에는 확인해야 해. 명시적 조건문, 옵셔널 체이닝 user?.name, null 병합 name ?? 'anonymous'을 상황에 맞게 골라. 빈 문자열과 0도 기본값으로 바꾸고 싶다면 ||가 맞을 수 있지만, null과 undefined만 처리하려면 ??가 더 정확해.