인터페이스란?
상호 간에 정의한 약속 혹은 규칙
사용하는 경우
프로퍼티를 정의해서 객체를 표현하고자 할 때
타입스크립트에서의 인터페이스는 보통 다음과 같은 범주에 대해 약속을 정의 가능
특정 속성 값에 대한 정보가 없음
// 객체: object 타입으로 정의 가능.
let user: object; // user라는 객체를 만듦.
user = {
name: 'xx',
age: 30
}
console.log(user.name)
/*
에러 발생
- 메세지: Property 'name' does not exist on type 'object'.(2339)
- 이유: 👉 object에는 특정 속성 값에 대한 정보가 없음.*/
interface User {
name: string;
age: number;
}
// 👉 속성을 적지 않은 경우 -> 에러 발생
let user: User = { // user는 User 타입으로 명시함.
}
// 메세지: Type '{}' is missing the following properties from type 'User': name, age(2739)
// 👉 속성을 다 적은 경우 -> 에러 발생 x
let user: User = {
name: 'xx',
age: 30
}
console.log(user.age)
/*
- 결과: 30
👉 property 파악 가능: user. 작성한 상태에서 어떤 property들이 있는지 나온다. */
user.age = 10 // 👉 프로퍼티 값 바꾸기 가능
user.gender = 'male' // 👉 없는 프로퍼티 입력 시 -> 에러 발생
let person = { name: 'Capt', age: 28 };
function logAge(obj: { age: number }) { // 👉 인자의 형태: age를 속성으로 갖는 객체
console.log(obj.age); // 28
}
logAge(person); // 28
인자를 받을 때 객체의 속성 타입까지 정의 가능
interface personAge {
age: number;
}
function logAge(obj: personAge) { // 👉 인자의 형태: 명시적 ('personAge' 타입 필요)
console.log(obj.age);
}
let person = { name: 'Capt', age: 28 };
logAge(person); // 28
인터페이스에 정의된 속성, 타입의 조건만 만족하면 됨
인터페이스를 인자로 받아 사용 시
사용하는 경우
있어도 되고 없어도 되는 경우
형식
속성의 끝에 ?
를 붙임
interface 인터페이스_이름 {
속성?: 타입;
}
예시 코드
interface User {
name: string;
age: number;
gender?: string;
}
let user: User = {
name: 'xx',
age: 30
}
user.gender = 'male' // 나중에 추가
인터페이스로 객체를 첫 생성할 때만 값 할당.
이후 변경 불가.
문법
앞에 readonly
붙임.
예시 코드
// ✨ 비교: 일반적인 경우
interface User {
birthYear: number
}
let user: User = {
birthYear: 2000 // 작성 시, suggest 생김
}
user.birthYear = 1990; // 바꿔도 문제 없음.
// ✨ 비교: readonly 프로퍼티 사용한 경우
interface User {
readonly birthYear: number
}
let user: User = {
birthYear: 2000 // 👉 최초에 생성할 때만 할당 가능. 이후 수정 불가
}
user.birthYear = 1990; // [에러 발생] 이유: 읽기 전용 속성이라서 수정 불가함.
배열을
ReadonlyArray
로 선언 시, 배열 내용 변경 불가
생성 방법
ReadonlyArray<T>
타입 사용예시 코드
let arr: ReadonlyArray<number> = [1,2,3];
arr.splice(0,1); // error
arr.push(4); // error
arr[0] = 100; // error
interface CraftBeer {
brand?: string;
}
function brewBeer(beer: CraftBeer) {
// ..
}
let myBeer = { brandon: 'what' }; // brand가 아닌 brandon 선언
// 선언 방식 (기존)
brewBeer(myBeer) // 오류 발생 (오탈자 점검 요구)
// 선언 방식 (타입 추론을 무시하고 싶을 경우)
brewBeer(myBeer as CraftBeer); // 오류 無
에러 발생
// ex. 학년 별 점수 기입
interface User {
// 1~4학년 까지 interface를 정의함.
1: string;
2: string;
3: string;
4: string;
}
// user가 2학년이라면 1학년 성적 밖에 없음.
let user: User = {
1: 'A'
}
/*
에러 발생.
이유: 2~4가 입력이 안되어 있어서.*/
해결 | 옵셔널
// [1. 해결] 1~4를 모두 옵셔널하게 만들기.
interface User {
1?: string;
2?: string;
3?: string;
4?: string;
}
리팩토링 1 | 문자열 인덱스
// [2. 리팩토링 1] 문자열 인덱스 서명?을 추가
interface User {
[grade:number]:string;
// grade가 number인 이유: 학년은 숫자로 받을 것
// 학점은 string
}
// 다른 단어 가능: grade 단어에 의미 없음.
// 의미: number를 key로 하고 string을 value로 받는 property를 여러 개 받을 수 있다.
let user: User = {
1: 'A',
2: 'B' // 에러 없이 정보 추가.
}
리팩토링 2 | 문자열 리터럴 타입
// [3. 리팩토링 2] '문자열 리터럴 타입' 활용
type Score = 'A' | 'B' | 'C' | 'F';
interface User {
[grade:number]: Score;
}
/*
문자열 리터럴 타입: string으로 받기엔 범위가 넓을 때 사용
- string: 어떤 문자열도 입력 가능
- 문자열 리터럴 타입(Score): 해당 값 외에 입력 불가 */
// 다른 값을 넣으면 에러 발생
let user: User = {
1: 's',
2: 'a'
}
- 함수 타입 정의 시, 사용 가능
- 함수의 인자 타입 & 반환 값 타입 정함.
add
함수interface Add { // Add라는 인터페이스를 만듦.
(num1:number, num2:number): number; // 내부: 괄호(인자 값), 리턴 값 적음.
}
const add:Add = function(x, y) { // 👉 interface의 인자명(ex. num1)을 다른 문자(ex. x)로 사용 가능
return x + y
}
add(10, 20) // 30
add(10, 20, 30) // [에러 발생] 인자 1개 더 적음
add(10, '20') // [에러 발생] 다른 타입(string)으로 적음
// 나이를 받아서 성인인지 아닌지 boolean값을 리턴해주는 함수 정의
interface IsAdult {
(age:number):boolean
}
// 19보다 크면 true, 아니면 false를 반환하도록 만듦.
const a:IsAdult = (age) => {
return age > 19
}
a<(33) // true
클래스가 (일정 조건을 만족하도록) 타입 규칙 정하기 가능
implements
사용// Car라는 interface 정의
interface Car {
color: string;
wheels: number;
start(): void;
}
// Car를 이용해서 Bmw라는 클래스 만듦.
class Bmw implements Car { // Car에 있는 속성 값을 모두 입력해야 함.
color = 'red'
wheels = 4
start(){
console.log('go')
}
}
// [constructor 활용]
class Bmw implements Car {
color;
constructor(c: string){ // 생성될 때 color를 입력 받음. c는 string이 되야 함.
this.color = c;
}
wheels = 4;
start(){
console.log('go');
}
}
const b = new Bmw('green')
console.log(b)
/*
Bmw: {
"wheels": 4,
"color": "green"
}
*/
b.start()
// "go"
사용하는 경우
인터페이스는 확장 (여러 개) 가능. 이때 사용
인터페이스 간 확장 가능
// Car라는 interface 정의
interface Car {
color: string;
wheels: number;
start(): void;
}
interface Benz extends Car { // Car가 가지고 있던 속성들을 그대로 받게 됨.
door: number // 추가로 정의 가능.
stop(): void
}
const benz: Benz = { // Car & Benz의 속성 모두 입력해야 함. 아니면 에러 발생
door: 5,
stop(){
console.log('stop')
},
color: "red",
wheels: 4,
start(){
console.log('start')
}
}
interface Person {
name: string;
}
interface Developer extends Person {
skill: string;
}
let fe = {} as Developer;
fe.name = 'josh';
fe.skill = 'TypeScript';
여러 인터페이스를 상속받아 사용 가능
interface Car {
color: string;
wheels: number;
start(): void;
}
interface Toy {
name: string;
}
interface ToyCar extends Car, Toy { // 동시 확장
price: number
}
interface Person {
name: string;
}
interface Drinker {
drink: string;
}
interface Developer extends Person,Drinker {
skill: string;
}
let fe = {} as Developer;
fe.name = 'josh';
fe.skill = 'TypeScript';
fe.drink = 'Beer';
인터페이스도 여러 타입 조합하여 제작 가능
(자바스크립트의 (유연하고 동적인) 타입 특성에 따라)
interface CraftBeer {
(beer: string): string;
brand: string;
brew(): void;
}
function myBeer(): CraftBeer {
let my = (function(beer: string) {}) as CraftBeer;
my.brand = 'Beer Kitchen';
my.brew = function() {};
return my;
}
let brewedBeer = myBeer();
brewedBeer('My First Beer');
brewedBeer.brand = 'Pangyo Craft';
brewedBeer.brew();
참고