반응형
인터페이스
인터페이스란 타입 별칭과 동일하게 타입에 이름을 지어주는 또다른 문법
//간단한 Person 객체의 타입을 정의한다면 다음과 같이 할 수 있다
interface Person {
name: string;
age: number;
}
//이렇게 정의한 인터페이스를 타입 주석과 함께 사용해 변수의 타입을 정의할 수 있다
const person: Person = {
name: "이재민",
age : 30
};
- 인터페이스는 타입 별칭과 문법만 조금 다를 뿐 기본적인 기능은 거의 같다고 볼 수 있다.
선택적 프로퍼티
인터페이스에서도 동일한 방법으로 선택적 프로퍼티 설정이 가능함
interface Person {
name: string;
age?: number;
}
const person: Person = {
name: "이재민",
// age: 30,
};
읽기전용 프로퍼티
interface Person {
readonly name: string;
age?: number;
}
const person: Person = {
name: "이재민",
// age: 30,
};
person.name = '홍길동' // ❌
메서드 타입 정의하기
// 다음과 같이 메서드의 타입을 정의하는 것도 가능하다
interface Person {
readonly name: string;
age?: number;
sayHi: () => void;;
}
- 함수 타입 표현식을 이용해 sayHi 메서드의 타입을 정의했다.
- 함수 타입 표현식 말고 다음과 같이 호출 시그니쳐를 이용해 메서드의 타입을 정의할 수도 있다.
interface Person {
readonly name: string;
age?: number;
sayHi(): void;
}
메서드 오버로딩
- 함수 타입 표현식으로 메서드의 타입을 정의하면 메서드의 오버로딩 구현이 불가능하다.
interface Person {
readonly name: string;
age?: number;
sayHi: () => void;
sayHi: (a: number, b: number) => void; // ❌
}
그러나 호출 시그니처를 이용해 메서드의 타입을 정의하면 오버로딩 구현이 가능하다.
interface Person {
readonly name: string;
age?: number;
sayHi(): void;
sayHi(a: number): void;
sayHi(a: number, b: number): void;
}
하이브리드 타입
//인터페이스또한 함수이자 일반 객체인 하이브리드 타입을 정의할 수 있다
interface Func2 {
(a: number): string;
b: boolean;
}
const func: Func2 = (a) => "hello";
func.b = true;
주의할 점 1
- 인터페이스는 대부분의 상황에 타입 별칭과 동일하게 동작하지만 몇가지 차이점이 존재한다.
- 타입 에서는 다음과 같이 Union이나 Intersection 타입을 정의할 수 있었던 반면 인터페이스에서는 할 수 없다.
type Type1 = number | string;
type Type2 = number & string;
interface Person {
name: string;
age: number;
} | number // ❌
- 따라서 인터페이스로 만든 타입을 Union 또는 Intersection으로 이용해야 한다면
다음과 같이 타입 별칭과 함께 사용하거나 타입 주석에서 직접 사용해야 한다.
type Type1 = number | string | Person;
type Type2 = number & string & Person;
const person: Person & string = {
name: "이재민",
age: 30,
};
인터페이스 확장하기
인터페이스 확장이란 하나의 인터페이스를 다른 인터페이스들이 상속받아 중복된 프로퍼티를 정의하지 않도록 도와주는 문법
- 다음과 같이 3개의 타입이 정의되어있다고 가정
interface Animal {
name: string;
age: number;
}
interface Dog {
name: string;
age: number;
isBark: boolean;
}
interface Cat {
name: string;
age: number;
isScratch: boolean;
}
interface Chicken {
name: string;
age: number;
isFly: boolean;
}
- 각 타입 들을 자세히 살펴보면 Animal 타입을 기반으로 Dog, Cat, Chicken이
각각의 추가적인 프로퍼티를 갖고 있는 형태임을 알 수 있다. - name 그리고 age 프로퍼티가 모든 타입에 중복해서 정의되어 있다는 점도 함께 발견할 수 있다.
- 중복 코드는 좋지 않으므로, 만약 다음과 같이 Animal 타입의 프로퍼티가 변경된다면
interface Animal {
name: string;
ages: number; // 수정
}
- 이를 기반으로 하는 나머지 타입들도 다음과 같이 다 수정되어야 할 것 이다.
interface Dog {
name: string;
ages: number; // 수정
isBark: boolean;
}
interface Cat {
name: string;
ages: number; // 수정
isScratch: boolean;
}
interface Chicken {
name: string;
ages: number; // 수정
isFly: boolean;
}
- 이렇게 특정 인터페이스를 기반으로 여러개의 인터페이스가 파생되는 경우 중복 코드가 발생할 수 있는데
이럴 때에는 인터페이스의 확장 기능을 이용하면 좋다.
확장은 다음과 같이 사용한다
interface Animal {
name: string;
color: string;
}
interface Dog extends Animal {
breed: string;
}
interface Cat extends Animal {
isScratch: boolean;
}
interface Chicken extends Animal {
isFly: boolean;
}
- (interface 타입이름 extends 확장_할_타입이름) 형태로 extends 뒤에 확장할 타입의 이름을 정의하면
해당 타입에 정의된 모든 프로퍼티를 다 가지고 오게 된다. - Dog, Cat, Chicken 타입은 모두 Animal 타입을 확장하는 타입이기 때문에 name, age 프로퍼티를 갖게 된다.
interface Animal {
name: string;
color: string;
}
interface Dog extends Animal {
breed: string;
}
(...)
const dog: Dog = {
name: "돌돌이",
color: "brown",
breed: "진도",
};
- 이때 확장 대상 타입인 Animal은 Dog 타입의 슈퍼타입이 된다.
프로퍼티 재정의 하기
interface Animal {
name: string;
color: string;
}
interface Dog extends Animal {
name: "doldol"; // 타입 재 정의
breed: string;
}
- Dog 타입은 Animal 타입을 확장하며 동시에 name 프로퍼티의 타입을
String 타입에서 “doldol” String Literal 타입으로 재정의 했다.
이렇게 확장받는 타입에서 프로퍼티의 타입을 재정의 할 수 있다. - 그런데 한가지 주의할 점은 프로퍼티를 재 정의할 때
원본 타입을 A 재 정의된 타입을 B라고 하면 반드시 A가 B의 슈퍼 타입이 되도록 재정의 해야 한다. - 따라서 다음과 같이 name을 Number 타입으로 재 정의 하는 것은 불가능하다.
interface Animal {
name: string;
color: string;
}
interface Dog extends Animal {
name: number; // ❌
breed: string;
}
- 그 이유는 Dog 타입이 Animal 타입을 확장한 다는 것은 Animal 타입의 서브타입이 된다는 의미이다.
- name 프로퍼티를 Number 타입으로 재 정의 해 버리면 이제는 Dog는 Animal의 서브 타입이 아니게 된다.
- 따라서 이런 재 정의는 불가한 것
타입 별칭을 확장하기
인터페이스는 인터페이스 뿐만 아니라 타입 별칭으로 정의된 객체도 확장할 수 있다
type Animal = {
name: string;
color: string;
};
interface Dog extends Animal {
breed: string;
}
다중 확장
여러개의 인터페이스를 확장하는 것 또한 가능
interface DogCat extends Dog, Cat {}
const dogCat: DogCat = {
name: "",
color: "",
breed: "",
isScratch: true,
};
인터페이스 선언 합치기
타입 별칭은 동일한 스코프 내에 중복된 이름으로 선언할 수 없는 반면 인터페이스는 가능하다
type Person = {
name: string;
};
type Person = { ❌
age: number;
};
interface Person {
name: string;
}
interface Person { // ✅
age: number;
}
- 이렇게 되는 이유는 중복된 이름의 인터페이스 선언은 결국 모두 하나로 합쳐지기 때문이다.
- 따라서 위 코드에 선언한 Person 인터페이스들을 결국 합쳐져 다음과 같은 인터페이스가 된다.
interface Person {
name: string;
age: number;
}
// -----------------------
interface Person {
name: string;
}
interface Person {
age: number;
}
const person: Person = {
name: "이정환",
age: 27,
};
- 이렇게 동일한 이름의 인터페이스들이 합쳐지는 것을 선언 합침(Declaration Merging)이라고 부른다.
주의할 점
- 그런데 만약 다음과 같이 동일한 이름의 인터페이스들이 동일한 이름의 프로퍼티를
서로 다른 타입으로 정의한다면 오류가 발생한다.
interface Person {
name: string;
}
interface Person {
name: number; // 에러
age: number;
}
반응형
'Typescript' 카테고리의 다른 글
Typescript | 제네릭, 타입 변수 응용, map, forEach 메서드 타입 정의 (1) | 2024.11.03 |
---|---|
Typescript | 클래스, 접근 제어자, 인터페이스와 클래스 (1) | 2024.11.02 |
Typescript | 함수 오버로딩, 사용자 정의 타입가드 (0) | 2024.11.02 |
Typescript | 함수 타입, 함수 타입 표현식과 호출 시그니처, 호환성 (0) | 2024.11.02 |
Typescript | 타입 추론, 타입 단언, 타입 좁히기, 서로소 유니온 타입 (0) | 2024.10.29 |