TypeScript/학습

TS 정리 (한 입 크기로 잘라먹는 타입스크립트(TypeScript)) - 04

lms0806 2025. 5. 4. 22:22
728x90
반응형

해당 포스팅은 한 입 크기로 잘라먹는 타입스크립트(TypeScript)를 학습하면서 알게된 정보들을 정리하였습니다.

https://inf.run/EvrS5

 

한 입 크기로 잘라먹는 타입스크립트(TypeScript) 강의 | 이정환 Winterlood - 인프런

이정환 Winterlood | ,   프론트엔드의 피할 수 없는 대세 타입스크립트,이제는 제대로 정복할 때가 왔습니다! 😎 [사진]인프콘 2023 '타입스크립트는 왜 그럴까?' 발표자의 강의입니다.   🧐 배워

www.inflearn.com

 

함수

기본적인 ts의 함수로는 function을 활용한 함수와 =>를 활용한 함수가 존재합니다.

function func(a: number, b: number): number {
    return a + b;
}

다음과 같이 number타입의 a라는 변수와 number 타입을 가지는 b라는 함수를 가지고, number타입의 값을 반환하는 함수를 선언할 수 있습니다.

function func(a: number, b: number) {
    return a + b;
}

또한 다음과 같이, a와 b에 대한 타입만 주어도 해당 연산을 통해 나오는 타입이 number라는 것을 ts가 추론할 수도 있습니다.

 

=>로 함수를 만드는 경우에도 마찬가지로 다음과 같이 선언하면 ts에서 반환값의 타입을 number이라고 추론할 수 있습니다.

const add = (a: number, b: number) => a + b;

만일, 함수에 선택적 매개 변수가 들어오는 경우에는 어떻게 될까요?

 

다음과 같이 선언되어져 있는 경우, tall은 number타입이나 undefined타입이 되게 됩니다.

function introduce(name: string, tall?: number) {
    console.log(`name : ${name}`);
    console.log(`tall: ${tall + 10}`);
}

그러나, 해당 값이 number타입인 경우에는 +10 연산을 수행가능하지만, undefined타입인 경우에는 해당 연산을 수행할 수 없어 에러가 발생하게 됩니다.

 

다음과 같이 선언하게 되면 tallnumber타입인 경우에만 +10 연산을 수행하도록 되어 있어 정상적으로 작동하게 됩니다.

function introduce(name: string, tall?: number) {
    console.log(`name : ${name}`);
    if (typeof tall == "number") {
        console.log(`tall: ${tall + 10}`);
    }
}

만일 여기서 하나의 매개 변수를 뒤에 추가하게 된다면 어떻게 될까요?

function introduce(name: string, tall?: number, age: number) {
    console.log(`name : ${name}`);
    if (typeof tall == "number") {
        console.log(`tall: ${tall + 10}`);
    }
}

선택적 매개 변수 뒤에 필수 매개 변수를 추가하게 되면, 해당 함수에 매개 변수로 전달하는 값에 문제가 생겨, 에러가 발생하게 됩니다.

 

name은 필수로 작성되어야 하고, tall은 없어도 되고, age는 필수로 있어야 하는 경우, 가운데 값만 제외하고 함수로 전달할 수 없기 때문입니다.

 

그래서, 선택적 매개 변수의 경우, 필수 매개 변수보다 뒤에 선언되어야 합니다.

 

만약, 매개 변수를 정해진 개수가 없는 같은 타입으로 받고 싶다면, 다음과 같이 가능합니다.

function getSum(...rest: number[]) {}

JAVA의 String...와 같이 정해진 개수가 없는 특정 타입의 배열 형식으로 값을 받아 처리하는 방식입니다.

 

그래서, 다음과 같이 해당 함수를 사용할 수 있습니다.

getSum(1, 2, 3);
getSum(1, 2, 3, 4, 5)

함수 타입 표현식 및 호출 시그니쳐

ts에서 함수를 선언하다보면, 같은 개수의 타입을 가진 매개 변수와, return 타입도 같은 경우, 이를 type으로 지정하여, 간편하게 사용할 수 있습니다.

 

다음과 같이 선언하는 경우, number타입으로 값 2개를 받고, number타입으로 반환합니다.

type opertaion = (a: number, b: number) => number;

이를, 함수에 적용한다면 다음고 같이 가능합니다.

// 적용 안한다면
const func: operation = (a: number, b: number) => a + b;
// 적용한다면
const func: operation = (a, b) => a + b;

함수 타입 표현식으로도 가능하지만, 다음과 같이 호츌 시그니처로도 활용 가능합니다.

type Opertaion2 = {
    (a: number, b: number): number;
}

함수 타입 호환성

특정 함수를 다른 함수로 취급하는 경우, 어떤 케이스들만 되는지 확인해보면, 다음과 같습니다.

type A = () => number;
type B = () => 10;

let a: A = () => 10;
let b: B = () => 10;

a = b;
b = a; // error

b = a에서만 에러가 발생하는가?

 

a 함수는 number타입으로 10을 반환하고, b 함수는 number 리터럴 타입으로 10을 반환하는 상황입니다.

 

a라는 함수의 반환값이 b보다 크므로, 반환 타입끼리 서로 다운 캐스팅이 이루어지는 경우 되지 않습니다.

 

그렇다면, 매개 변수가 있는 함수의 경우에는 어떻게 될까요?

type C = (value: number) => void;
type D = (value: 10) => void;

let c: C = (value) => { };
let d: D = (value) => { };

c = d; // error
d = c;

다음과 같은 경우에는 업캐스팅의 경우에만 error가 발생합니다.

 

c라는 함수는 모든 숫자가 허용되는 number타입을 매개 변수로 가지고, d라는 함수는 10이라는 값만 매개 변수로 가지는 경우, c에 d를 넣는 경우에만 문제가 발생합니다.

 

여기서 또 하나, 매개 변수의 개수가 다른 경우에는 어떻게 될까요?

type Func1 = (a: number, b: number) => void;
type Func2 = (a: number) => void;

let func1: Func1 = (a, b) => { };
let func2: Func2 = (a) => { };

func1 = func2;
func2 = func1; // error 발생

func1라는 함수는 2개의 매개 변수를 가지고 있고, func2라는 함수는 1개의 매개 변수를 가지고 있습니다.

 

이런 경우에는, 할당하고자 하는 함수에 매개 변수의 개수가 적은 경우에만 가능합니다.

 

또한, 적용하고자 하는 경우 두 함수가 필요로 하는 매개 변수의 타입이 동일해야지만 해당 방식이 적용 가능합니다.

함수 오버로딩

함수 오버로딩은, 같은 함수명을 가지지만 서로 다른 매개 변수를 가지는 경우, 매개 변수에 따라서 함수를 선택하여 실행하는 방식으로 실행됩니다.

 

타 언어인 java를 예시로 들자면 다음과 같이 가능합니다.

public static void func(int a) {

}
public static void func(int a, int b) {

}

그러나, ts에서 함수 오버로딩을 사용하기 위해서는 오버로드 시그니처와 구현 시그니처가 필요합니다.

 

다음과 같이 오버로드 시그니처를 구현합니다.

// 오버로드 시그니처
function func(a: number): void;
function func(a: number, b: number, c: number): void;

이후, 다음과 같이 선언하는 경우 에러가 발생하게 됩니다.

function func(a: number, b: number, c: number){}

왜냐하면, 해당 함수는 3개의 매개 변수만 가지고 있는 함수만 구현되어, func(a: number)에 대한 함수가 구현되지 않았기 때문입니다.

 

이를 해결하기 위해서는 다음과 같이 선택적 매개 변수를 사용해야 합니다.

function func(a: number, b?: number, c?: number){
    if (typeof b == 'number' && typeof c == 'number') {
    }
    else {
    }
}

개인적으로 해당 방식으로 구현하여 사용하기보다는, 만약 타입이 모두 같다면 function func(...a: number[]) { } 다음과 같이 선언하여, number 배열의 길이에 따른 처리 방식이 더 좋다고 생각합니다.

사용자 정의 타입 가드

현재 다음과 같이 타입이 존재하고

type Dog = {
    name: string,
    isBark: boolean
};

type Cat = {
    name: string,
    isScratch: boolean
};

type Animal = Dog | Cat;

다음과 같이 animal이 현재 무슨 타입인지 in을 통하여 처리가 가능합니다.

function warning(animal: Animal) {
    if ("isBark" in animal) {
        animal
    }
    else if ("isCScratch" in animal) {

    }
}

만약 "isBark"가 dog 뿐만 아니라 다른 타입에도 존재하는 경우, 다음과 같이 처리하게 되면 해당 타입에 대하여 정확히 판단이 불가능하게 됩니다.

function isDog(animal: Animal) {
    return animal.isBark !== undefined;
}

그래서, 다음과 같이 선언하였지만, animal의 형식에 isBark가 없어 error가 표출됩니다.

function isDog(animal: Animal) {
    return (animal as Dog).isBark !== undefined;
}

다음과 같이 animal이 Dog 타입이라는 것을 지정하게 된다면, Dog 타입에는 isBark가 있어 문제 없이 선언이 가능합니다.

 

그러나, 해당 함수를 통하여 Dog 타입을 판별하고자 하였으나, 해당 함수를 지난 후의 animal의 타입을 보면 Dog가 아닌 animal 타입인 것을 확인할 수 있습니다.

function warning(animal: Animal) {
    if (isDog(animal)) {
        animal // Animal 타입
    }
}

해당 경우에는 다음과 같이 "사용자 정의 타입 가드"라는 것을 활용하여 처리가 가능합니다.

function isDog(animal: Animal): animal is Dog {
    return (animal as Dog).isBark !== undefined;
}

animal을 Dog타입이라고 했을 때, isBark가 undefined가 아니면, animal을 Dog타입으로 지정한다. 라고 이해하시면 됩니다.

 

개인적으로, 현재까지는 타입을 나누는 방식으로는 사용할 것으로 보이지만 다른 방식이 있다면, 매개 변수의 타입을 바꾸는 로직이 들어간 방식은 사용하지 않을거 같습니다.

728x90
반응형