본 포스팅은, '캡틴판교'님이 작성하신 '타입스크립트 핸드북'을 보고 스스로 정리하기 위해 작성하는 포스팅입니다. 자세한 내용은 https://joshua1988.github.io/를 참고해주세요!
인터페이스는 상호 간에 정의한 약속 혹은 규칙을 의미한다. TS에서의 인터페이스는 보통 다음과 같은 범주에 대해 약속을 정의할 수 있다.
let person = { name: 'Capt', age: 28};
function logAge(obj: { age: number }) {
console.log(obj.age); // 28
}
logAge(person); // 28
위 logAge() 함수에서 받는 인자의 형태는 age를 속성으로 갖는 객체이다. 이렇게 인자를 받을 때 단순한 타입 뿐만 아니라 객체의 속성 타입까지 정의할 수 있다.
아래는 위의 예제에 인터페이스를 적용한 코드이다.
interface personAge {
age: number;
}
function logAge(obj: personAge) {
console.log(obj.age);
}
let person = { name: 'Capt', age: 28 };
logAge(person);
이제는 logAge()
의 인자가 좀 더 명시적으로 바뀌었다. logAge()
의 인자는 personAge
라는 타입을 가져야한다.
또한, 인터페이스를 인자로 받아 사용할 때는 항상 인터페이스의 속성 갯수와 인자로 받는 객체의 속성 갯수를 일치시키지 않아도 된다.
다시 말해, 인터페이스에 정의된 속성, 타입의 조건만 만족한다면 객체의 속성 갯수가 더 많아도 상관 없고, 인터페이스에 선언된 속성 순서를 지키지 않아도 된다.
인터페이스를 사용할 때 인터페이스에 정의되어 있는 속성을 모두 다 꼭 사용하지 않아도 된다.
interface 인터페이스_이름 {
속성?: 타입;
}
=> 이렇게 속성의 끝에 ?
를 붙이면 가능하다.
interface personAge {
name: string;
age?: number;
}
function logAge(obj: personAge) {
console.log(obj.name);
}
let person = {name:'Capt'};
logAge(person);
personAge
인터페이스를 인자의 타입으로 선언했음에도 불구하고, age
을 옵션 속성으로 선언했기 때문에 객체에는 age
속성이 존재하지 않는다.
단순히 인터페이스를 사용할 때 속성을 선택적으로 적용할 수 있다는 것 뿐만 아니라, 인터페이스에 정의되어 있지 않은 속성에 대해서 인지시켜줄 수 있다는 장점이 있다.
interface CraftBeer {
name: string;
hop?: number;
}
function brewBeer(beer: CraftBeer) {
console.log(beer.from); // Error: Property 'from' does not exist on type 'Beer'
}
=> 속성을 선택적으로 적용하기 위해서는 ?
키워드를 쓰기 때문에 원래 같으면 정의된 속성 외에도 다른 속성에 대한 추가가 자유로웠지만, 더욱 엄격하게 제어할 수 있다.
인터페이스로 객체를 처음 생성할 때만 값을 할당하고 그 이후에는 변경할 수 없는 속성을 의미한다.
문법은 다음과 같이 readonly
속성을 앞에 붙인다.
interface CraftBeer {
readonly brand: string;
}
// 처음 생성시에만 값을 할당하고 그 이후로는 변경을 불가능하게 하기 위해! (== const)
인터페이스로 객체를 선언하고 나서 수정하려고 하면 오류 발생!
let myBeer: CraftBeer = {
brand: 'Belgian Monk'
};
myBeer.brand = 'Korean Carpenter'; // error!
배열을 선언할 때 ReadonlyArray<T>
타입을 사용하면 읽기 전용 배열을 생성할 수 있다.
let arr: ReadonlyArray<number> = [1,2,3];
arr.splice(0,1); // error
arr.push(4); // error
arr[0] = 100; // error
arr = [10, 20, 30]; // error
선언하는 시점에만 값을 정의할 수 있음!
타입스크립트는 인터페이스를 이용해 객체를 선언할 때 좀 더 엄밀한 속성 검사를 진행한다.
interface Person {
name?: string;
}
function print(person: Person) {
console.log(person.name);
}
let person = {nam : 'sol'}; // error: Object literal may only specify known properties, but 'nam' does not exist in type 'Person'. Did you mean to write 'name'?
위 코드를 보면 Person 인터페이스에는 name라고 선언되어 있지만 print() 함수에 인자로 넘기는 Person 객체에는 name이 선언되어 있어 오탈자 점검을 요하는 오류가 발생한다.
이러한 타입 추론을 무시하고 싶다면
let person = {nam : 'sol'};
print(person as Person); // undefined
이런식으로 선언하면 된다!
(* 대신 인터페이스에서 정의된 속성이 아니기 때문에 undefined가 출력된다. )
그럼에도 불구하고 만약 인터페이스 내에 정의하지 않은 속성들을 추가로 사용하고 싶을 때는 아래와 같은 방법을 사용한다.
interface Person {
name?: string;
[propName: string]: any;
// 정의되지 않은 속성들을 추가로 사용 가능!
}
인터페이스는 함수의 타입을 정의할 때에도 사용할 수 있다.
interface login {
(username: string, password: string): boolean;
}
// login 인터페이스는 string 타입의 매개 변수를 받고 boolean 타입을 return 하는 함수만 정의가 가능!
let loginUser: login = function(id: string, pw: string) {
console.log('로그인 했습니다.');
return true;
}
let logoutUser: login = function(id: string, pw: string) {
console.log('로그아웃 했습니다.');
return true;
}
C#이나 자바처럼 타입스크립트에서도 클래스가 일정 조건을 만족하도록 타입 규칙을 정할 수 있다.
interface user {
username: string;
print(name: string): void;
}
class Login implements user {
username: string = 'sol';
print(name: string) {
console.log(name);
}
constructor() {}
}