자바스크립트에서는 기본형을 사용하더라도 경우에 따라 내부적으로 객체 레퍼로 형변환하여 사용한다. 객체 레퍼는 종종 아래와 같이 당황스러운 결과를 보여준다.
function hasName(input: String) {
return ['A','B'].includes(input) // Type Error
}
위 오류의 이유는 String
에 string
을 할당할 순 있지만, string
에 String
을 할당할 수 없기 때문이다.
객체 레퍼타입은 사용을 지양한다.
타입이 명시된 변수에 객체 리터럴을 할당할 때 타입스크립트는 해당 타입의 속성이 있는지, 그리고 잉여속성은 없는지 확인한다. 아래에 두 예제를 보자. 첫번째 예제와 두번째 예제는 동일한 코드처럼 보이지만 전자는 타입에러가 발생하고, 후자는 타입에러가 발생하지 않는다.
interface Person {
name: string
}
const person: Person = {name: 'koo', age: 26} // Type Error
interface Person {
name: string
}
const koo = {name: 'koo', age: 26}
const person: Person = koo // No Error
구조적 타이핑 관점에서 생각해보면 타입에러가 나지 않아야 정상인데, 전자의 경우 타입 에러가 발생하는 것은 왜일까? 두 예제의 차이점은 임시변수를 도입한 것인데, 첫 번째 예제에서는 '잉여속성 체크'라는 과정이 수행된 것이다.
잉여 속성 체크가 할당 가능 검사와는 별도의 과정이라는 것을 알아야 개념을 정확히 이해할 수 있다.
interface Options {
title: string
darkMode?: boolean
}
function createWindow(input: Options) { }
createWindow({title: 'window', darkmode: true})
만약에 위 코드를 실행하면 런타임에 어떠한 에러로 발생하지 않는다. 하지만 Options
이란 타입은 title
이라는 필드를 가진 모든 객체이므로 범위가 아주 넓어질 수 있다.
const o1: Options = document
const o2: Options = new HTMLAnchorElement
따라서 타입스크립트는 의도와 다르게 작성된 코드까지 찾기 위해서, 잉여 속성 체크를 하는데 타입시스템의 구조적 타이핑을 해치지 않기 위해서 타입에 객체 리터럴
을 할당할 때 잉여 속성 체크
를 수행한다.
잉여속성 체크를 이용하면 객체 리터럴에 알 수 없는 속성을 허용하지 않으므로 위 문제점을 방지할 수 있다.
선택적 속성만 가지는 타입을 약한 타입
이라고 한다.
interface Options {
opt1?: string
opt2?: string
}
구조적 관점에서 위 Options
타입은 모든 객체를 포함할 수 있는데, 이런 약한 타입에 객체 리터럴
을 할당할 때도 타입스크립트는 잉여 속성과 마찬가지로 알 수 없는 속성을 허용하지 않는다.
그러나 약한 타입에서 다른 점이 있는데, 약한 타입과 관련된 할당문마다 수행되는 점이다.
interface Options {
opt1?: string
opt2?: string
}
const temp = {opt3:'123'}
const opt: Options = temp // 구조적 관점, 잉여속성관점에서는 문제가 없으나 타입에러 발생
- 객체 리터럴을 변수에 할당하거나 함수의 매개변수로 전달할 때 잉여 속성 체크가 수행된다.
- 잉여 속성 체크는 타입체커가 수행하는 구조적 할당 가능성 체크와 역할이 다르다.
- 임시변수를 도입하면 문제를 우회할 수 있게 된다. (약한 타입은 제외)