users
를 맵핑한다고 가정하자. 아래와같이 age
라는 새로운 키값을 추가했지만 타입스크립트는 에러를 뱉지 않는다.
타입스크립트는 객체 타입에 대하여 excess property checking을 수행한다. 하지만 map의 경우 콜백으로 새로운 객체를 생성한다. 그렇기 때문에 런타임에 문제가 없다면 excess property checking 수행하지 않는다.
추가적인 검증을 위해 return 타입을 추가해야 한다.
return 타입을 User
로 지정하면 excess property checking을 수행하게 된다.
혹은 satisfies
를 추가해주면 된다.
그렇다면 이런 경우는 어떨까
user
를 Object.keys
한 값인 keys
가 ["id", "name"]
이길 예상하지만 타입스크립트는 keys
를 string[]
으로 추론하고 있다.
즉, 아래처럼 키값으로 객체에 접근하는 것이 불가능한다.
그 이유는 새로 반환될 객체가 런타임 환경에서 달라질 수도 있기 때문에 타입스크립트가 "키"를 보장하지 못하는 것이다.
위 상항에서 순회를 한다고 가정하자.
인덱스 시그니처를 찾을 수 없다는 에러가 나온다. keyof
로 간단하게 해결 할 수 있다.
아니면 Record<string, any>
로 타입 체크를 루즈하게 할 수 있다.
아래와 같은 이벤트 함수가 있다고 가정하자
listenToEvent
함수는 callback 인자를 받는데 인자는 다음과 같이 존재한다.
빈값이거나
event만 있거나
event과 x,y 값도 있거나
거기에 id 까지 올 수 있다.
위와 같은 상황에서 CallbackType
을 어떻게 지정해야 모든 경우의 타입을 검증할 수 있을까
가장 먼저 생각나는 것은 아래처럼 모든 경우를 유니언 타입으로 만드는 것이다
하지만 유니언 타입으로 하게 되면 콜백이 (event)와 (event, x, y) 인 경우에서 타입 에러가 나온다.
CallbackType
이 유니언 타입으로 정의되어 있을 때, 타입스크립트는 이 타입을 하나의 함수 타입으로 취급하지 않고 여러 개의 함수 타입 중 하나로 취급한다.
CallbackType
을 아래와 같이 하나의 타입으로만 정의하자. 그럼 모든 테스트가 통과되는 것을 알 수 있다.
이는 타입스크립트의 타입 체크가 선언 시점에서 이루어지기 때문에 가능하다. 그렇기 때문에 타입스크립트는 실행시점에 어떤 인자를 사용할 것인지는 관심이 없다
더 쉬운 예제를 살펴보자.
위와 같이 map
함수를 사용한다고 했을때, map
은 value
와 index
, array
를 인자로 갖는다.
하지만 실제로 map
을 사용할 때는 모든 인자를 사용하지 않는다. 즉, 함수가 어떤 인자를 받을 수 있다는 것은 그 인자를 가지고 있어야 한다는 뜻이 아니다.
각각 다른 obj 를 인자를 받는 두 함수 logId
와 logName
이 있다. 이를 하나의 배열 loggers
에 담았다.
그리고 이를 logAll
이라는 loggers
를 순회하는 함수에서 사용한다고 했을 때, logAll
의 인자의 타입은 어떻게 해야할까
loggers
의 콜백인 func
는 logId
와logName
의 유니언 타입으로 추론된다.
그래서 obj
인자를 유니언 타입으로 넣게 되면
func
의 타입은 아래와 같이 합쳐진다.
이유는 logAll
이 순회하며 함수을 실행할 때, 함수의 인자엔 인자가 될 모든 가능성이 있는 인자가 다 있어야 하기 때문이다.
쉽게 생각하여 유니언으로 인자를 받는 경우 아래처럼 타입 좁히기를 해야 하는 경우다.
반대로 모든 함수를 호출하는 것이므로 &
로 인자를 합쳐서 받아야 한다. (집합의 개념으로)
참고 : total typescript