우연히 오픈소스 코드를 보던 중 한 오픈소스에서 재미있는 코드를 보게 되었다.
문제의 코드는 다음과 같다.
interface Foo{
bar? : string | undefined
}
얼핏보면 크게 문제가 없는데 한가지 이상한 점이 있다
?
프로퍼티의 타입을 정의하면서 | undefined
로 굳이 한번 더 불필요해 보이는 타입을 덮어 씌운 것이다.
도대체 이 코드를 작성한 사람은 어떤 생각으로 이렇게 굳이 필요없어보이는 타입을 넣어준 것일까 하고 고민해보며 나름대로 그럴듯한 답을 찾아냈고 자바스크립트의 미묘한 동작에 대해서 이야기해보고자 이렇게 글을 쓰게 되었다.
우선 아래 코드에서 나는 중요한 힌트를 얻을 수 있었다.
type A = {
a? : string
}
type B = {
b : string | undefined
}
type C = {
c? : string | undefined
}
function aa(a : A){}
function bb(b : B){}
function cc(c : C){}
aa({}) // ok
aa({a : undefined}) // ok
aa({a : ''}) // ok
bb({}) // not ok
bb({b : undefined}) // ok
bb({b : ''}) // ok
cc({}) // ok
cc({c : undefined}) // ok
cc({c : ''}) // ok
말로 설명하기에 좀 모호하지만 처음 타입스크립트를 마주하면 일반적으로 위의 A
같은 타입을 보고
아 이 A
라는 타입은 구조체나 클래스와 유사하게 키와 값이 결합된 유도자료형이구나 라고 생각하면서 a?
를 A.a
필드의 타입이 string
인 형태라고 생각한다.
즉 A
를 보고 아래와 같은 타입들을 기대한다.
type A = {
a? : string
}
type A0 = {
}
type A1 = {
a : string
}
여기서 A
는 A0
혹은 A1
의 결합이라고 판단하기 쉽다.
그런데 사실 A
는 A0
, A1
말고도 하나의 경우의 수가 더 존재한다. 이는 아래와 같다.
type A2 = {
a : undefined
}
이는 자바스크립트의 어찌보면 기묘하고, 나는 개인적으로 언어적인 오류라고 생각하는 사항인 undefined 역시 값으로 취급 가능하다는 특징에서 기인하는데 오브젝트는 존재하지 않는 키로 접근할 때 undefined를 반환하나, 오브젝트에 존재하는 값이더라고 undefined가 될 수도 있다
아래와 같은 사례를 생각하면 쉬운데
const a = {}
const b = {foo:undefined}
console.log('foo' in a) // false
console.log('foo' in b) // true
console.log(a.foo === undefined) // true
console.log(b.foo === undefined) // true
여기서 a
에는 foo
라는 필드는 없지만 a.foo
의 값은 undefined
이다.
또 b
에는 foo
라는 필드가 있고 b.foo
의 값은 역시 undefined
이다.
이는 a와 b는 분명 다른 오브젝트 형태를 가지고 있으나 둘은 결과적으로 같은 오브젝트처럼 호환이 된다는 뜻으로 for in
구문이나 in
연산자 등을 이용할 때 예상치 못한 결과를 초래할 수 있다.
이제 처음 이야기했던 내용으로 다시 돌아가자.
interface Foo{
bar? : string | undefined
}
위의 타입은 3가지 Foo
라는 인터페이스 형태를 지원한다.
{}
빈 오브젝트{bar:undefined}
bar
가 존재하나 bar
의 값이 undefined
{bar:'???'}
bar
가 존재하고 bar
의 값이 string
사실 타입스크립트의 ?
필드는 자동으로 이 타입의 값에 undefined를 넣어 주기에 아래 두 타입은 같기는 하다.
interface Foo0{
bar? : string
}
interface Foo1{
bar? : string | undefined
}
하지만 아마도 위 코드를 작성했던 사람은 이 미묘한 차이를 표현하고 이 타입의 값으로 필드에 undefined를 넣은 값도 동일한 동작을 할 것이다라는 것을 보장하기 위해 이렇게 타입을 작성했을 것이라 생각한다.
그 증거로 vscode의 language server는 위의 Foo0 bar 필드의 값을 string | undefined로 표시한다.
아마로 이는 일반적으로는 문제가 되지 않지만 주의하지 않으면 실수하기 쉬운 자바스크립트의 특징, undefined를 신경쓴 프로그래머가 아니였을까?
나 역시도 이런 문제를 주의하며 코드를 작성해야 할 것 같다.
포스팅 감사합니다~ 저도 vscode language server를 보면서 왜이렇게 작성했지? 라고 생각했는데.. 그럴 듯한 해석인 것 같아요!