인터페이스는 타입스크립트에서 객체 유형의 타입을 정의하는 도구이다.
데이터의 구조와 동작을 정의하는데 사용되며, 속성의 이름, 타입을 지정해 객체의 형태를 정의한다.
interface UserInfo {
name: string;
age: number;
preferences: {
theme: string;
language: string;
};
}
위 예제와 같이, 일종의 타입 설계도를 만든다고 이해하면 될 것 같다.
인터페이스는 두개의 글로 나눠서 정리할 것인데, 이 글에서는 인터페이스의 속성과 관련된 내용을 다뤄볼 것이다.
속성 수정자는 타입스크립트에서 객체의 속성에 적용할 수 있는 키워드 or 문법이다.
다음과 같은 키워드로, 속성의 동작과 접근 방식을 제어한다.
readonly : 읽기 전용 속성, 값을 변경할 수 없는 속성을 정의
optional/?
: 선택적 속성, 값을 생략할 수 있는 속성을 정의
public, private, protected: 접근 제어자, 속성에 대한 접근 수준을 제한
static: 정적 속성, 클래스 수준의 속성을 정의
오늘 내용에서 정리할 개념은 readonly와 optional이다.
interface UserInfo {
name: string;
readonly age: number; // 읽기 전용 속성
preferences?: {
theme: string;
language: string;
};
}
const user = { name: 'hanni', age: 18 };
const logUserInfo = (user: UserInfo) => {
console.log(user.name);
user.age = 0; // error: 읽기 전용 속성이므로 'age'에 할당할 수 없습니다.
};
logUserInfo(user);
위와 같이, interface 내에서 속성 이름(age
) 앞에 readonly 키워드를 붙여주면, age
속성을 변경할 수 없게 된다.
하지만, 이는 값을 완전히 변경하지 못한다는게 아니다. 내부의 값은 변경할 수 있지만, 속성 자체를 다시 쓸 수 없다는 것을 뜻한다.
또한, readonly 키워드는 런타임 시 동작에 영향을 주지 않는다.
interface UserInfo {
name: string;
age: number;
preferences?: { // 옵션 속성
theme: string;
language: string;
};
}
const user = { name: 'hanni', age: 18 };
const logUserInfo = (user: UserInfo) => {
console.log(user.name);
};
logUserInfo(user);
위 코드를 실행해보면, interface 내에 정의된 preferences
가 인자로 넘어오지 않았는데도, 오류가 나지 않는다.
이렇게, 속성 이름 끝에 ?
를 추가하여, 해당 속성을 선택 사항으로 지정할 수 있다.
옵션 속성을 사용하면 속성이 필수가 아니게 되지만,
인터페이스 내부에 없는 속성의 사용을 방지할 수 있어 적절한 상황에 사용하면 장점이 있다.
인덱스 시그니처란?
타입스크립트에서 동적으로 속성에 접근하기 위해 사용되는 기능이다.
또한, 미리 모든 속성의 이름을 알 수 없지만 값의 형태는 알고 있을 때,
인덱스 시그니처를 통해 가능한 유형의 값을 정의할 수 있다.
interface MyDictionary {
[index: string]: number;
}
인덱스 시그니처는 위와 같은 형태로 작성한다.
index
는 인덱스에 타입, value 자리에 있는 number
는 해당 인덱스에 접근했을 때, 반환되는 타입을 나타낸다.
const dictionary: MyDictionary = {
one: 1,
two: 2,
three: 3,
};
console.log(dictionary['two']); // 2
console.log(dictionary['four']); // undefined
위 예제와 같이 정의한 인터페이스를 타입으로 갖는 객체에 동적으로 접근할 수 있다.
인덱스 시그니처를 활용하면, 간편하게 객체의 동적인 속성을 처리할 수 있다.
초과 속성 검사란?
타입스크립트에서 객체 리터럴이 다른 변수에 할당, 혹은 함수의 매개변수로 전달될 때, 정의되지 않은 추가적인 속성이 있는지 검사하는 동작이다.
interface IdolGroup {
group?: string;
member?: string;
}
const logIdol = (idol: IdolGroup) => {
console.log(idol.group, idol.member);
};
logIdol({ group: '뉴진스', member: '강해린', strange: '강해린 이상하다' });
/* error : 개체 리터럴은 알려진 속성만 지정할 수 있으며
'IdolGroup' 형식에 'strange'이(가) 없습니다 */
함수 logIdol
은 매개변수로 IdolGroup
인터페이스 형식의 idol
을 받는데,
strange라는 추가 속성이 존재해서, 초과 속성 검사가 발생하고 오류를 표시해준다.
위 예제는 함수로 예를 들었지만, 객체 리터럴이 변수에 할당되는 경우에도 해당 동작이 발생한다고 한다. 타입스크립트는 여러모로 안정성 측면에서 이점이 있는 것 같다.
인덱스 시그니처 / 초과 속성 검사가 익숙한 개념이 아니라서 조금 헤맸다.
공식 문서가 읽기에는 조금 딱딱해도 필요한 내용이 잘 정리되어있는 것 같다. 다음 글에서는 인터페이스의 확장과 교차 유형에 대해 알아보자. 👍
참고 문서