SPACE RUMI

Hi, I am rumi. Let's Splattack!

[STUDY] 스터디/TypeScript

[TypeScript] 타입스크립트 인터페이스 / typescript interface / typescript readonly / interface extends

백루미 2023. 6. 10. 04:11
반응형

인터페이스 (interface)

interface ProductModel { // 인터페이스 선언
  id: number;
}

// productModel 인터페이스를 타입으로 갖는 product 객체를 받아 콘솔로 찍음
const printProduct = (product: ProductModel) => { 
  console.log(product); 
};

let myProduct = { id: 10, productName: "my first product" };

printProduct(myProduct); // {id: 10, productName: "my first product"}

인터페이스는 정의된 프로퍼티가 있는지와, 이 프로퍼티의 타입이 맞는지만 검사한다.
정의되지 않은 프로퍼티는 검사하지 않고, 프로퍼티의 순서도 상관없다.

 

옵셔널 프로퍼티 (Optional Properties)

interface ProductModel {
    id: number;
    productName?: string;
}

옵셔널 프로퍼티는 프로퍼티명 끝에 ?를 붙인다.
productName 프로퍼티는 있을수도있고 없을수도 있지만, 있다면 string 타입을 가진다.

 

읽기 전용 프로퍼티 (Readonly properties)

interface ReadModel {
    readonly a: number;
    readonly b: number;
}

let test:ReadModel = {
    a:10;
    b:15;
}

test.a = 11; // Cannot assign to 'a' because it is a read-only property.

말 그대로 읽기전용이므로, 값 할당이후 수정할 수 없다.
타입스크립트는 Array<T>ReadonlyArray<T> 타입을 제공하는데, 이 둘은 변경 메서드가 제거되었기 때문에 재할당이 불가능하다.

let readtest: ReadonlyArray<number> = [1, 2, 3];
console.log("첫번째", readtest); // [2,2,3] 이거 왜 2찍혀??

readtest[0] = 2; // Index signature in type 'readonly number[]' only permits reading.
readtest.push(4); // Property 'push' does not exist on type 'readonly number[]'.
readtest.length = 3; // Cannot assign to 'length' because it is a read-only property.
readtest = [1, 2, 3, 4]; // 근데 이건 왜 돼??

console.log("두번째", readtest); // [1,2,3,4]

 

let으로 선언한 변수 readtest가 읽기전용이지만, 변수자체를 다른값으로 할당하는것은 된다. (다른 주소값을 할당)
참고로 위 예제에서 readtest[0]을 2로 할당한 줄에서 에러가 발생 했지만 콘솔은 찍힌다ㅋㅋ

 

readonly 와 const 어떤것을 사용해야할까?

변수에는 const를 쓰고, 프로퍼티에는 readonly를 사용한다.

 

초과 프로퍼티 검사 (Excess Property Checks)

Typescript는 객체 리터럴을 다른 변수에 할당할때인수로 전달할 때 초과 프로퍼티 검사를 한다.
예를들어, 없는 프로퍼티를 넘길때 에러가 발생한다.

interface ProductModel {
  id?: number;
  productName?: string;
}

function createProduct(product: ProductModel){
  console.log(product);
}

let productOne = createProduct({ ids: 123, productName: "product One" });
// Object literal may only specify known properties, 
// and 'ids' does not exist in type 'ProductModel'.

이럴때는 해결하는 방법은 3가지가 있다.

1. as 타입단언

let productOne = createProduct({ ids: 123, productName: "product One" } as ProductModel);

타입을 단언하는것은 별로 좋아보이진 않는다.

 

2. 문자열 인덱스 서명(string index signature)

interface ProductModel {
  id?: number;
  productName?: string; 
  [holly: string]:any; //프로퍼티명은 string이고, 그 타입은 any인 것
}

이렇게 문자열 인덱스 서명방식을 추가하면 어떤 것이 들어와도 에러가 발생하지 않는다. 

 

3. 객체를 다른 변수에 할당

interface ProductModel {
  id?: number;
  productName?: string;
}

function createProduct(
  product: ProductModel
){
  console.log(product);
}

let obj = { ids: 123, test:12, productName: "product One" }; 
let productOne = createProduct(obj); //이게 왜 돼?

obj는 초과 프로퍼티 검사를 받지 않으므로 에러가 발생하지 않는다. (인자에 그대로 넘기면 발생)
하지만 이 방법은 obj 변수에 ProductModel에서 명시한 프로퍼티가 존재하지 않다면 에러가 난다. 좋은 방법은 아니다.

 

함수 타입

인터페이스는 함수 타입도 선언할수있다.

interface UserFunction { // name, age를 인자로 받고 boolean을 리턴하는 함수
  (name: string, level: number): boolean;
}

let firstUserCreator: UserFunction; // 변수에 타입선언

firstUserCreator = (name, level) => { // 변수에 함수할당. 이때는 타입선언을 안해줘도 타입추론이 된다.
  console.log(name);
  return level > 19;
};

console.log(firstUserCreator("rumi", 999) ? "참" : "거짓"); // 참

 

인덱서블 타입

name['rumi'] 나 level[99] 처럼 타입을 인덱스로 지정할수있다.

import "./global.css";
import * as React from "react";

interface levelArray {
  [level: number]: number;
}

let userLevel: levelArray = [99, 12, 43];
let level: number = userLevel[0];
console.log(level); //99

이 인덱스 서명을 지원하는 타입은 string와 number 두가지다. 숫자 인덱서에서 반환된 타입은 문자열 인덱서에서 반환된 타입의 하위 타입(subtype)이어야 하는데, 그 이유는 실제로 javascript가 객체를 인덱싱 하기 전에 string으로 변환하기 때문이다.

interface Origin {
  [index: string]: number;
  length: number; // length는 숫자입니다
  name: string; // Property 'name' of type 'string' is not assignable to 'string' index type 'number'.
}

// index:string으로 선언한 타입이 number이므로 name도 number 타입을 가질수있어야한다.

 

인터페이스 확장 (interface extends)

인터페이스는 확장할 수 있다.

interface UserModel{
    id:number;
    name:string;
    level:number;
}

interface VipUserModel extends UserModel{
    priceing:string;
    hiddenLevel:number;
}

let user:UserModel;
let vipUser:VipUserModel;

 

하이브리드 타입 (Hybrid Types)

함수 인터페이스, as 타입단언 등을 여러가지 타입패턴을 섞어만든 타입.

반응형