이펙티브 타입스크립트 스터디(6)

오형근·2023년 4월 20일
1

Typescript

목록 보기
14/15
post-thumbnail

Effective Typescript에 대한 스터디 진행 내용을 요약한 것입니다.

함수 표현식에 타입 적용하기

TS에서 함수를 표현하는 방식은 두 가지가 있다.

함수 선언문과 함수 표현식이 그것인데,

아래 코드를 통해 둘의 차이를 살펴보자.

function rollDice1 (sides: number): number {
    return 0;
}
const rollDice2 = function (sides: number): number {
    return 0;
}
const rollDice3 = (sides: number): number => {
    return 0;
}

위 식에서 아래 두 개는 함수 표현식, 맨 위는 함수 선언문에 해당된다.

TS에서는 함수 표현식을 사용하기를 권장하는데, 그 이유는 아래와 같다.

TS에서는 함수 표현식을 사용하기를 권장하고 있습니다. 함수의 매개변수부터 반환 값까지 전체를 함수 타입으로 선언하여 함수 표현식을 재사용할 수 있다는 장점이 있기 때문입니다.

type DiceRollFn = (sides: number) => number
const rollDice: DiceRollFn = sides => 0

위와 같이 함수 표현식 포맷으로 함수 타입을 지정해두면 언제 어디서나 타입을 재사용할 수 있다는 얘기다!

함수 표현식은 다음과 같은 상황에서 유용하게 사용될 수 있다.

async function checkedFetch(input: RequestInfo, init?: RequestInit) {
    const response = await fetch(input, init)
    if (!response.ok) {
        throw new Error("Request failed: " + response.status)
    }
    return response
}

위의 상황처럼 fetching 함수를 선언할 때 직접 타입을 지정해주기보다는,

const checkedFetch: typeof fetch = async (input, init) => {
    const response = await fetch(input, init)
    if (!response.ok) {
        throw new Error("Request failed: " + response.status)
    }
    return response
}

요렇게 typeof fetch와 같이 미리 선언된 함수 타입을 통해 타입을 선언함으로써 좀 더 간결하고 명확한 코드를 작성할 수 있고, 코드의 불필요한 반복을 줄일 수 있다.

함수 표현식에 익숙해지고, 반복 가능성이 높은 함수들은 미리 함수 타입을 지정해두자!!

타입과 인터페이스의 차이점 알기

TS에서 타입을 정의하는 방법은 두 가지가 있다. 바로 타입과 인터페이스인데,
아래 예시처럼 타입 정의가 가능하다.

type TState = {
	name: string;
  	capital: string;
}

interface IState {
	name: string;
  	capital: string;
}

거의 대부분 둘 중 아무거나 사용해도 무방하지만, 괜히 타입 정의 방법이 두 가지가 있는 게 아니겠죠...?

둘의 차이를 확실히 해야 TS를 더 잘 사용할 수 있을 겁니다.

여기서 인터페이스를 정의할 때 앞에 I 키워드를 붙였는데, 이는 C#에서부터 시작된 관례이지만 현재는 지양해야할 스타일로 여겨진다고 합니다. 알고 쓰면 좋겠습니다!!

둘 모두 추가 속성과 함께 선언하면 문제가 발생합니다.

const wyoming: TState = {
	name: 'Wyoming',
  	capital: 'Cheyenne',
  	population: 500_000
}
// Error: 'TState'형식에 'population'이 없습니다...

당연한 타입 에러이다!

인덱스 시그니처는 인터페이스와 타입에서 모두 사용할 수 있습니다.

type TDict = { [key: string]: string };
interface IDict {
	[key: string]: string;
}

또한, 함수 타입도 인터페이스나 타입으로 정의할 수 있습니다.

type TFn = (x: number) => string;
interface IFn {
	(x: number): string;
}

사실 위와 같은 단순 함수 타입의 경우 타입 별칭을 사용하는 것이 더 좋을 수 있지만, 함수 타입에 추가적인 속성이 있다면 타입이나 인터페이스 중 무엇을 사용하던 괜찮을 것 같습니다!!

타입 별칭과 인터페이스 모두 제너릭 사용이 가능합니다.

type TPair<T> = {
	first: T;
  	second: T;
}
interface IPair<T> {
	first: T;
 	second: T;
}

!! 인터페이스와 타입은 상호 확장이 가능합니다!!

다만 뒤에서 언급할 주의사항을 제외하면 말이죠...

interface IStateWithPop extends TState {
	population: number;
}
type TStateWithPop = IState & { population: number };

이제 차이점을 알아봅시다!!

!!!! 인터페이스는 유니온 타입과 같은 복잡한 타입을 확장하지 못한다 !!!!

네, 그렇다고 합니다.

사실 생각해보면 유니온 타입 이라는 용어는 있지만 유니온 인터페이스 라는 용어는 없죠?? 저도 한 번도 못 들어봤습니다.

인터페이스는 타입을 확장할 수는 있지만, 유니온은 할 수 없습니다. 아래 예제를 살펴봅시다.

interface Tuple {
    name: string;
} | {
    name: number;
} // Error: 'number' only refers to a type, but is being used as a value here.(2693)

type TTuple = {
    name: string;
} | {
    name: number;
}

또한 유니온 타입에 name 속성을 붙인 타입을 만들 수도 있다.

type NameVariable = (Input | Output) & { name: string };

하지만 인터페이스로 위와 같은 형태를 만들고자 하면 수많은 에러를 쏟는다.

이처럼 type은 보통 interface보다 쓰임새가 많다. type 은 유니온이 가능하고, 매핑된 타입 또는 조건부 타입 같은 고급 기능들에 활용되기도 한다.

튜플과 배열 타입도 type 키워드를 통해 더 간결하게 표현할 수 있다.

type Pair = [number, number];
type StringList = string[];
type NamedNums = [string, ...number[]];

물론 이는 인터페이스로도 비슷하게 구현이 가능하다.
여기서 등장하는 것인 인터페이스의 유사배열객체 라는 개념이다.

interface Tuple {
	0: number;
  	1: number;
  	length: 2;
}

const t: Tuple = [10, 20]; // No Error!!

유사배열객체는 마지막에 length라는 속성을 통해 해당 인터페이스의 길이를 정해주고, 각 속성별로 타입을 지정해줌으로서 배열의 역할을 할 수 있도록 만들어진 객체이다. 물론 배열 관련 메서드를 사용할 수 있으며, 이때 Array.from()이라는 메서드를 사용해주어야함을 주의하자!!

!!!! 인터페이스는 타입과 다르게 보강이 가능하다 !!!!

이건 타입에는 없는 인터페이스만의 독특한 기능인데, 인터페이스의 확장성을 크게 높여준다.

interface IState {
	name: string;
  	capital: string;
}
interface IState {
	population: number;
}
const wyoming: IState = {
	name: 'Wyoming',
  	capital: 'Cheyenne',
  	population: 500000,
} // No Error!

위 예제처럼 IState라는 인터페이스를 다시 선언하듯 속성을 추가할 수 있다!!

요건 아주 좋은 기능인 것 같기는 한데 재 선언하듯이 속성을 추가하는게 얼마나 유용하게 쓰일 수 있을지 모르겠다...우선 정적 타입 언어에서 동적인 기능이라니...사용처가 궁금하다!!


이번에는 이펙티브 타입스크립트 아이템 12와 13의 내용을 살펴보았다!!!

0개의 댓글