본문 바로가기

TYPESCRIPT

타입스크립트: 인터페이스

인터페이스(Interface)


 

인터페이스: 타입스크립트 여러 객체를 정의하는 일종의 규칙, 구조

interface IUser {
  name: string,
  age: number,
  isAdult: boolean
}

let user1: IUser = {
  name: 'Neo',
  age: 123,
  isAdult: true
};

 

 

인터페이스 사용시,  빠짐없이 모두 초기화 해줘야함.

//Error - TS2741: Property 'isAdult' is missing in type '{ name: string; age: number; }' but required in type 'IUser'.
let user2: IUser = {
  name: 'Evan',
  age: 456
};

 

 

+예외

선택적 속성(Optional properties)

?를 붙이면 선택적 속성 정의 가능

필수가 아닌 속성

interface IUser {
  name: string,
  age: number,
  isAdult?: boolean // Optional property
}

// `isAdult`를 초기화하지 않아도 에러가 발생하지 않습니다.
let user: IUser = {
  name: 'Neo',
  age: 123
};

 

 

:(colon) ,(comma) 생략 가능

interface IUser {
  name: string,
  age: number
}
// Or
interface IUser {
  name: string;
  age: number;
}
// Or
interface IUser {
  name: string
  age: number
}

 

 

 

 

읽기 전용 속성(Readonly properties)


초기화 값 유지

 

interface IUser {
  readonly name: string,
  age: number
}

// 초기화
let user: IUser = {
  name: 'Neo',
  age: 36
};

user.age = 85; //Ok
user.name = 'Evan'; //Error - TS2540: Cannot assign to 'name' because it is a read-only property.

 

 

모든 속성이 readonly인 경우

유틸리티단언타입 활용

 

 

All readonly properties

interface IUser {
  readonly name: string,
  readonly age: number
}
let user: IUser = {
  name: 'Neo',
  age: 36
};
user.age = 85; // Error
user.name = 'Evan'; // Error

 

 

Readonly Utility

interface IUser {
  name: string,
  age: number
}
let user: Readonly<IUser> = {
  name: 'Neo',
  age: 36
};
user.age = 85; // Error
user.name = 'Evan'; // Error

 

 

Type assertion

let user = {
  name: 'Neo',
  age: 36
} as const;
user.age = 85; // Error
user.name = 'Evan'; // Error

 

type assertion => as const 사용

모든 속성에 readonly를 붙인 것과 같은 효과를 낸다

 

 

 

 

인터페이스-함수


함수인터페이스로 정의 하는 경우

 

호출 시그니처 사용(call signature)

매개변수(parameter)반환 타입 지정

interface IName {
  (PARAMETER: PARAM_TYPE): RETURN_TYPE // Call signature
}

 

 

예시

interface IUser {
  name: string
}
interface IGetUser {
  (name: string): IUser
}

// 매개 변수 이름이 인터페이스와 일치할 필요가 없습니다.
// 또한 타입 추론을 통해 매개 변수를 순서에 맞게 암시적 타입으로 제공할 수 있습니다.
const getUser: IGetUser = function (n) { // n is name: string
  // Find user logic..
  // ...
  return user;
};
getUser('Heropy');

 

 

 

 

인터페이스-클래스


implememts 키워드 사용

interface IUser {
  name: string,
  getName(): string
}

class User implements IUser {
  constructor(public name: string) {}
  getName() {
    return this.name;
  }
}

const neo = new User('Neo');
neo.getName(); // Neo

 

 

인터페이스를 함수의 인수로 사용하는 경우

interface ICat {
  name: string
}

class Cat implements ICat {
  constructor(public name: string) {}
}

function makeKitten(c: ICat, n: string) {
  return new c(n); // Error - TS2351: This expression is not constructable. Type 'ICat' has no construct signatures.
}
const kitten = makeKitten(Cat, 'Lucy');
console.log(kitten);

❗️문제 발생

인터페이스 ICat은 호출이 불가하기 때문

 

 

new 키워드를 사용해하면 해결할 수 있음

interface IName {
  new (PARAMETER: PARAM_TYPE): RETURN_TYPE // Construct signature
}

문제없이 동작

interface ICat {
  name: string
}
interface ICatConstructor {
  new (name: string): ICat;
}

class Cat implements ICat {
  constructor(public name: string) {}
}

function makeKitten(c: ICatConstructor, n: string) {
  return new c(n); // ok
}
const kitten = makeKitten(Cat, 'Lucy');
console.log(kitten);

 

 

 

 

인덱싱 가능 타입(Indexable types)


인덱스 시그니처(index signature)

 

숫자 인덱싱  arr[2]

문자 인덱싱  obj['name']

 

 

인덱싱 가능타입

 

인덱싱 가능타입을 정의하는 인터페이스=인덱스시그니처

interface INAME {
  [INDEXER_NAME: INDEXER_TYPE]: RETURN_TYPE // Index signature
}

 

 

 

 

⚠️: 인덱서 타입 string, number만 지정 가능

 

 

 

 

배열에서 위치를 가리키는 숫자(문자) 인덱스[Index]
각 배열 요소(객체 속성)에 접근하기 위해 인덱스를 사용하는 것 인덱싱[indexing]
배열을 구성하는 각각의 값 요소[element]

 

 

interface IItem {
  [itemIndex: number]: string // Index signature
}
let item: IItem = ['a', 'b', 'c']; // Indexable type
console.log(item[0]); // 'a' is string.
console.log(item[1]); // 'b' is string.
console.log(item['0']); // Error - TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.

인터페이스 ITtem은 인덱스 시그니처를 가지고 있음(item)

인덱싱값은 무조건 숫자여야함 :number

반환값은 무조건 문자 :string

 

 

 

반환타입에 유니온 사용하는 경우

interface IItem {
  [itemIndex: number]: string | boolean | number[]
}
let item: IItem = ['Hello', false, [1, 2, 3]];
console.log(item[0]); // Hello
console.log(item[1]); // false
console.log(item[2]); // [1, 2, 3]

 

 

 

인덱스시그니처문자인 경우

interface IUser {
  [userProp: string]: string | boolean
}
let user: IUser = {
  name: 'Neo',
  email: 'thesecon@gmail.com',
  isValid: true,
  0: false
};
console.log(user['name']); // 'Neo' is string.
console.log(user['email']); // 'thesecon@gmail.com' is string.
console.log(user['isValid']); // true is boolean.
console.log(user[0]); // false is boolean
console.log(user[1]); // undefined
console.log(user['0']); // false is boolean

인터페이스에 정의되지 않은 속성 사용 시 유용

interface IUser {
  [userProp: string]: string | number
  name: string,
  age: number
}
let user: IUser = {
  name: 'Neo',
  age: 123,
  email: 'thesecon@gmail.com',
  isAdult: true // Error - TS2322: Type 'true' is not assignable to type 'string | number'.
};
console.log(user['name']); // 'Neo'
console.log(user['age']); // 123
console.log(user['email']); // thesecon@gmail.com

isAdult 속성은

정의된 string이나 number타입을 반환하지 않지 않기 때문에 에러 발생

keyof

인덱싱 가능 타입에서 keyof를 사용하면 속성 이름을 타입으로 사용 가능

인덱싱 가능 타입의 속성 이름들이 유니온 타입으로 적용됨

interface ICountries {
  KR: '대한민국',
  US: '미국',
  CP: '중국'
}
let country: keyof ICountries; // 'KR' | 'US' | 'CP'
country = 'KR'; // ok
country = 'RU'; // Error - TS2322: Type '"RU"' is not assignable to type '"KR" | "US" | "CP"'.

인덱싱 가능

interface ICountries {
  KR: '대한민국',
  US: '미국',
  CP: '중국'
}
let country: ICountries[keyof ICountries]; // ICountries['KR' | 'US' | 'CP']
country = '대한민국';
country = '러시아'; // Error - TS2322: Type '"러시아"' is not assignable to type '"대한민국" | "미국" | "중국"'.

인터페이스 확장

클래스처럼 extends 해서 상속 가능

interface IAnimal {
  name: string
}
interface ICat extends IAnimal {
  meow(): string
}

class Cat implements ICat { // Error - TS2420: Class 'Cat' incorrectly implements interface 'ICat'. Property 'name' is missing in type 'Cat' but required in type 'ICat'.
  meow() {
    return 'MEOW~'
  }
}

같은 이름의 인터페이스를 여러개 만들 수 있음

기존에 만들어진 인터페이스에 내용을 추가하는 경우 유용

interface IFullName {
  firstName: string,
  lastName: string
}
interface IFullName {
  middleName: string
}

const fullName: IFullName = {
  firstName: 'Tomas',
  middleName: 'Sean',
  lastName: 'Connery'
};

 

타입별칭(Aliasis)


type키워드로 새로운 타입 조합을 만들 수 있음

하나 이상의 타입을 조합해 별칭(이름)을 부여, 정확히는 조합한 각 타입들을 참조하는 별칭을 만듦

일반적인 경우 둘 이상의 조합으로 구성하기 위해 유니온을 많이 사용함

TUser에서 T는 Type을 의미하는 별칭

type MyType = string;
type YourType = string | number | boolean;
type TUser = {
  name: string,
  age: number,
  isValid: boolean
} | [string, number, boolean];

let userA: TUser = {
  name: 'Neo',
  age: 85,
  isValid: true
};
let userB: TUser = ['Evan', 36, false];

function someFunc(arg: MyType): YourType {
  switch (arg) {
    case 's':
      return arg.toString(); // string
    case 'n':
      return parseInt(arg); // number
    default:
      return true; // boolean
  }
}

'TYPESCRIPT' 카테고리의 다른 글

타입스크립트: 유틸리티 타입  (0) 2022.04.11
타입스크립트: 모듈  (0) 2022.04.11
타입스크립트: 함수, 클래스  (0) 2022.04.10
타입스크립트: 제네릭  (0) 2022.04.10
타입스크립트 학습내용 정리  (0) 2022.04.07