인터페이스는 연관된 이름으로 객체 형태를 설명하는 또 다른 방법이다.
타입 별칭보다 ① 더 읽기 쉬운 오류 메시지 ② 더 빠른 컴파일러 성능 ③ 클래스와의 더 나은 상호 운용성을 가지고 있다.
// 두 구문은 거의 같다.
type information = {name: string; age: number; };
interface information2 {name: string; age: number; };
타입 별칭과 인터페이스의 차이점
여기서 소개하는 속성 타입은 별칭 객체 타입에도 사용할 수 있다.
:?
로 생략 가능한 선택적(optional) 속성을 나타낼 수 있다.
interface Book {
movie?: boolean;
title: string;
}
const book:Book = {
movie: true,
title: '해리포터와 마법사의 돌',
}
const book: Book = {
title: '러닝 타입스크립트',
}
속성 이름 앞에 readonly
키워드를 추가해서,
인터페이스에 정의된 객체의 속성을 재할당하지 못하도록 할 수 있다.
interface Page { readonly text: string; }
function read(page: Page){
/* text 속성을 읽는 것은 가능하지만 */
console.log(page.text);
/* text 속성을 수정하는 것은 에러가 발생한다. */
page.text += "!";
// Cannot assign to 'text' because it is a read-only property.
}
속성 구문: 인터페이스 멤버를 member: () => void
와 같이 독립 함수와 동일하게 선언
메서드 구문: 인터페이스 멤버를 member(): void
와 같이 객체의 멤버로 호출되는 함수로 선언
interface FuncTypes {
property: () => string;
method(): string;
}
const funcObj: FuncTypes = {
property: () => "",
method(){
return "";
}
};
두 구문의 차이점
- 메서드는 readonly로 선언할 수 없지만 속성은 가능하다.
- 인터페이스 병합은 메서드와 속성을 다르게 처리한다.
- 타입에서 수행되는 일부 작업은 메서드와 속성을 다르게 처리한다.
호출 시그니처는 값을 함수처럼 호출하는 방식에 대한 타입 시스템의 설명이다.
호출 시그니처가 선언한 방식으로 호출되는 값만 인터페이스에 할당할 수 있다.
type FunctionAlias = (input: string) => number;
const funcAlias: FunctionAlias = (input) => input.length;
/* 위와 동일한 타입이다. */
interface CallSignature {
(input: string): number;
}
const funcCallSig: CallSignature = (input) => input.length
💡호출 시그니처의 interface 부분 잘 모르겠음, 스터디에서 질문하기
()
소괄호가 함수 타입을 나타내는 건가?
()
내부는 파라미터 타입,:
오른쪽은 리턴값 타입interface a { (): number }
인터페이스 객체가 임의의 키를 받고 해당 키 아래의 특정 타입을 반환할 수 있다.
키 다음에 타입이 있고, {[i:string]: ...}
과 같이 배열의 대괄호를 갖는다.
interface WordCount {
[i: string]: number;
}
const counts: WordCount = {};
counts.apple = 0;
counts.banana = 1;
counts.cherry = false;
// Type 'boolean' is not assignable to type 'number'.
실제 값은 없지만 타입이 정의된 키는 타입 오류가 발생하지 않는다.
interface DateByName {
[i: string]: Date;
}
const birthday: DateByName = {
Jeny: new Date('2000.01.01'),
};
birthday.Jeny; // Sat Jan 01 2000 00:00:00 GMT+0900 (한국 표준시)
// 타입은 Date이지만 런타임값은 undefined 이다.
birthday.Kai;
속성과 인덱스 시그니처 혼합
/* 속성의 타입과 인덱스 시그니처의 타입이 같아야 한다. */
interface Book {
title: string;
[i: string]: number;
} // Property 'title' of type 'string' is not assignable to 'string' index type 'number'.
/* title을 포함한 1개 이상의 string: string 타입의 프로퍼티가 있어야 한다.*/
interface Book {
title: string;
[i: string]: string;
}
const novel: Book = {
title: '해리포터와 불의 잔',
author: '조앤K.롤링'
}
/* title만 있어도 ok */
const novel2: Book = {
title: '해리포터와 불사조 기사단',
}
/* string: string 타입의 프로퍼티가 있어도,
타입에 정의된 title 속성은 반드시 있어야 한다. */
const novel3: Book = {
author: '조앤K.롤링'
} // Property 'title' is missing in type '{ author: string; }' but required in type 'Book'.
숫자 인덱스 시그니처
💡잘 모르겠는 부분/* 가능 */ interface Book { [i: number] : string | undefined; [i: string]: string | undefined; } /* 가능 */ interface Book { [i: number] : string; [i: string]: string | undefined; } /* 오류 */ interface Book { [i: number] : string | undefined; [i: string]: string; } // 'number' index type 'string | undefined' is not assignable to 'string' index type 'string'.
interface Book {
title: string;
author: Author;
}
interface Author {
name: string;
famous: boolean;
}
const HarryPoter: Book = {
title: '해리포터와 마법사의 돌',
author: {
name: '조앤K.롤링',
famous: true,
}
}
다른 인터페이스의 모든 멤버를 포함하고, 거기에 몇 개의 멤버가 추가된 인터페이스인 경우
extends
키워드를 추가한다.interface Person {
name: string;
age: number;
}
interface Friend extends Person {
school: string;
}
/* Person 과 Friend에 있는 모든 속성을 가지고 있어야 하며, 그 외 추가 속성(프로퍼티)은 가질 수 없다.*/
const friend: Friend = {
name: 'Janny',
age: 15,
school: 'K-highschool'
}
interface Person {
name: string | null;
}
// 타입을 더 구체적인 하위 집합으로 만드는 것은 가능하지만
interface Friend extends Person {
name: string;
}
// 다른 타입으로 확장하거나 바꾸는 것은 오류가 발생한다.
interface classMate extends Person {
name: number | null
}
/* Interface 'classMate' incorrectly extends interface 'Person'.
Types of property 'name' are incompatible.
Type 'number | null' is not assignable to type 'string | null'.
Type 'number' is not assignable to type 'string'.*/
extends
키워드 뒤에 쉼표,
로 인터페이스 이름을 구분해 사용하면 여러 개의 다른 인터페이스를 확장해서 선언할 수 있다.
interface Person {
name: string | null;
}
interface Greeting{
sayHello(): string;
}
// 여러 인터페이스를 확장할 수 있다.
interface Friend extends Person, Greeting {
phone: number;
}
function friend(input: Friend){
console.log(`${input.sayHello()} ${input.name}, Call ${input.phone}.`);
}
let friend1 = {
name: 'Jay',
sayHello: () => `Hi!`,
phone: 777
}
friend(friend1); // "Hi! Jay, Call 777."
두 개의 인터페이스가 동일한 이름으로 동일한 스코프에서 선언된 경우, 선언된 모든 필드를 포함하는 더 큰 인터페이스가 코드에 추가된다.
interface Same {
first: string;
}
interface Same {
second: number;
}
// interface Same { first: string; second: number; } 와 같다.
const sameObj: Same = {
first: 'one',
second: 1
}
동일한 이름이지만 타입이 다른 속성을 선언할 수 없다.
// first 는 동일한 string 타입이므로 문제 없지만,
// second는 타입이 다르기 때문에 오류가 발생한다.
// third는 메서드이므로 문제 없다.
interface Same {
first: string;
second: string;
third(input: string): string;
}
interface Same {
first: string;
second: number;
third(input: boolean): number;
}
// Subsequent property declarations must have the same type.
// Property 'second' must be of type 'string', but here has type 'number'.