https://poiemaweb.com/typescript-interface
인터페이스는 여러가지 타입을 갖는 프로퍼티로 이루어진 새로운 타입을 정의하는 것과 유사하다.
인터페이스는 일반적으로 타입 체크를 위해 사용되며 변수, 함수, 클래스에 사용할 수 있다.
인터페이스에 선언된 프로퍼티 또는 메소드의 구현을 강제하여 일관성을 유지할 수 있도록 하는 것이다.
ES6는 인터페이스를 지원하지 않지만 TypeScript는 인터페이스를 지원한다.
인터페이스는 프로퍼티와 메소드를 가질 수 있다는 점에서 클래스와 유사하나 직접 인스턴스를 생성할 수 없고 모든 메소드는 추상 메소드이다. 단, 추상 클래스의 추상 메소드와 달리 abstract 키워드를 사용하지 않는다.
2. 변수
// 인터페이스의 정의
interface Todo {
id: number;
content: string;
completed: boolean;
}
// 변수 todo의 타입으로 Todo 인터페이스를 선언하였다.
let todo: Todo;
// 변수 todo는 Todo 인터페이스를 준수하여야 한다.
todo = { id: 1, content: 'typescript', completed: false };
3. 함수
함수의 인자와 return 에 대해서 타입을 정의 한다. 해당 인터페이스를 구현하는 함수는 정의된 타입을 준수해야됨.
// 함수 인터페이스의 정의
interface SquareFunc {
(num: number): number;
}
// 함수 인테페이스를 구현하는 함수는 인터페이스를 준수하여야 한다.
const squareFunc: SquareFunc = function (num: number) {
return num * num;
}
console.log(squareFunc(10)); // 100
4. 클래스
인터페이스를 구현하는 클래스의 일관성을 유지할 수 있는 장점을 갖는다.
// 인터페이스의 정의
interface IPerson {
name: string;
sayHello(): void;
}
/*
인터페이스를 구현하는 클래스는 인터페이스에서 정의한 프로퍼티와 추상 메소드를 반드시 구현하여야 한다.
*/
class Person implements IPerson {
// 인터페이스에서 정의한 프로퍼티의 구현
constructor(public name: string) {}
// 인터페이스에서 정의한 추상 메소드의 구현
sayHello() {
console.log(`Hello ${this.name}`);
}
}
function greeter(person: IPerson): void {
person.sayHello();
}
const me = new Person('Lee');
greeter(me); // Hello Lee
5. 덕 타이핑
만약 어떤 새가 오리처럼 걷고, 헤엄치고, 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를 것이다.
interface IDuck { // 1
quack(): void;
}
class MallardDuck implements IDuck { // 3
quack() {
console.log('Quack!');
}
}
class RedheadDuck { // 4
quack() {
console.log('q~uack!');
}
}
function makeNoise(duck: IDuck): void { // 2
duck.quack();
}
makeNoise(new MallardDuck()); // Quack!
makeNoise(new RedheadDuck()); // q~uack! // 5
makeNoise 함수는 인터페이스 IDuck을 구현한 클래스의 인스턴스 duck을 인자로 전달받는다.
case1.
makeNoise 함수에, 클래스 MallardDuck은 인터페이스 IDuck을 구현하였기 때문에 정상적으로 동작한다.
case2.
makeNoise 함수에, 클래스 RedheadDuck 에 대해서도 오류가 발생하지 않는다.
-> 클래스 RedheadDuck은 인터페이스 IDuck을 구현하지는 않았지만 quack 메소드를 가지고 있기 때문에 에러 없이 처리된다.
TypeScript는 해당 인터페이스에서 정의한 프로퍼티나 메소드를 가지고 있다면 그 인터페이스를 구현한 것으로 인정한다.
이것을 덕 타이핑(duck typing) 또는 구조적 타이핑(structural typing)이라 한다.
interface IPerson {
name: string;
}
function sayHello(person: IPerson): void {
console.log(`Hello ${person.name}`);
}
const me = { name: 'Lee', age: 18 };
sayHello(me); // Hello Lee
변수 me는 인터페이스 IPerson과 일치하지는 않는다.
하지만 IPerson의 name 프로퍼티를 가지고 있으면 인터페이스에 부합하는 것으로 인정된다.
인터페이스는 개발 단계에서 도움을 주기 위해 제공되는 기능으로 자바스크립트의 표준이 아니다.
따라서 위 예제의 TypeScript 파일을 자바스크립트 파일로 트랜스파일링하면 아래와 같이 인터페이스가 삭제된다.
"use strict";
function sayHello(person) {
console.log(`Hello ${person.name}`);
}
const me = { name: 'Lee', age: 18 };
sayHello(me); // Hello Lee
6. 선택적 프로퍼티
인터페이스의 프로퍼티는 반드시 구현되어야 한다. 하지만 인터페이스의 프로퍼티가 선택적으로 필요한 경우가 있을 수 있다.
선택적 프로퍼티(Optional Property)는 프로퍼티명 뒤에 ?를 붙이며 생략하여도 에러가 발생하지 않는다.
-> 원하면 구현하고 아니면 안해도 되는 느낌
-> db로 치면 not null 을 기본값으로 하는 테이블에 대해서, ? 를 쓰면 null 을 허용하는 느낌
interface UserInfo {
username: string;
password: string;
age? : number;
address?: string;
}
const userInfo: UserInfo = {
username: 'ungmo2@gmail.com',
password: '123456',
age: 18
}
console.log(userInfo); // { username: 'ungmo2@gmail.com', password: '123456', age: 18 }
7. 인터페이스 상속
인터페이스는 extends 키워드를 사용하여 인터페이스 또는 클래스를 상속받을 수 있다.
interface Person {
name: string;
age?: number;
}
interface Student extends Person {
grade: number;
}
const student: Student = {
name: 'Lee',
age: 20,
grade: 3
}
복수의 인터페이스를 상속받을 수도 있다.
interface Person {
name: string;
age?: number;
}
interface Developer {
skills: string[];
}
interface WebDeveloper extends Person, Developer {}
const webDeveloper: WebDeveloper = {
name: 'Lee',
age: 20,
skills: ['HTML', 'CSS', 'JavaScript']
}
인터페이스는 인터페이스 뿐만 아니라 클래스도 상속받을 수 있다. 단, 클래스의 모든 멤버(public, protected, private)가 상속되지만 구현까지 상속하지는 않는다.
class Person {
constructor(public name: string, public age: number) {}
}
interface Developer extends Person {
skills: string[];
}
const developer: Developer = {
name: 'Lee',
age: 20,
skills: ['HTML', 'CSS', 'JavaScript']
}
타입스크립트에서 인터페이스는 변수나 함수 클래스의 타입을 미리 정의한다.
Type Alias
타입 앨리어스는 새로운 타입을 정의한다. 인터페이스와 유사하지만
타입 앨리어스는 extends 또는 implements될 수 없다. 즉, 확장이 불가능하다.
유니온 또는 튜플을 사용해야한다면 타입 앨리어스를 사용한는 편이 유리하다.
interface Person {
name: string,
age?: number
}
// 빈 객체를 Person 타입으로 지정
const person = {} as Person;
person.name = 'Lee';
person.age = 20;
person.address = 'Seoul'; // Error
// 타입 앨리어스
type Person = {
name: string,
age?: number
}
// 빈 객체를 Person 타입으로 지정
const person = {} as Person;
person.name = 'Lee';
person.age = 20;
person.address = 'Seoul'; // Error
타입 앨리어스는 원시값, 유니온 타입, 튜플 등도 타입으로 지정할 수 있다.
// 문자열 리터럴로 타입 지정
type Str = 'Lee';
// 유니온 타입으로 타입 지정
type Union = string | null;
// 문자열 유니온 타입으로 타입 지정
type Name = 'Lee' | 'Kim';
// 숫자 리터럴 유니온 타입으로 타입 지정
type Num = 1 | 2 | 3 | 4 | 5;
// 객체 리터럴 유니온 타입으로 타입 지정
type Obj = {a: 1} | {b: 2};
// 함수 유니온 타입으로 타입 지정
type Func = (() => string) | (() => void);
// 인터페이스 유니온 타입으로 타입 지정
type Shape = Square | Rectangle | Circle;
// 튜플로 타입 지정
type Tuple = [string, boolean];
const t: Tuple = ['', '']; // Error
유니온 타입
or 와 같이 여러개중 1개의 타입을 가진다.
타입 가드
타입 가드는 TypeScript에서 변수의 타입을 좁히는 데 사용됩니다.
다음과 같은 코드에 대해서는 에러가 발생하며
typeof를 사용해 변수의 타입을 확인하고, 그에 따라 다른 로직 실행 가능.
function greet(person: string | number) {
if (typeof person == 'string'){
console.log(person.length);
}
}
greet('Lee') // 3