본문 바로가기
개발공부

#4. 함수와 타입

by 반류연 2025. 2. 25.

※ 이 강의는 이정환 강사님의 '한 입 크기로 잘라먹는 타입스크립트' 강의를 듣고 정리한 글입니다.

 

 

함수 타입을 정의하는 방법

// 기본 함수 정의
function func(a:number, b: number):number {
	return a+b;
}

// 화살표 함수 정의
const add = (a:number, b:number):number => a+b;
  • 함수의 반환값 타입은 자동으로 추론되기 때문에 생략 가능
  • 매개변수에 기본값이 설정되어 있다면 타입이 자동으로 추론됨.
    • 기본값과 매개변수의 타입이 다르면 오류!
    • 기본값과 다른 타입의 값을 인수로 전달해도 오류!
// 정상 코드(타입 정의 생략한 버전)
function introduce(name="이정환"){
	console.log(`name:${name}`)
}

//에러 발생 코드
function introduce1(name:number = "이정환"){  // number 타입인데 string 값 넣음
	console.log(`name:${name}`)
}

function introduce2(name="이정환"){
	console.log(`name:${name}`)
}

introduce2(1) // string 타입 인수에 숫자를 전달함 -> 오류!

 

선택적 매개변수 설정

  • 매개변수 이름 뒤에 물음표(?)를 붙이면 생략 가능한 매개변수가 됨
  • 선택적 매개변수의 경우, 자동으로 undefined와 유니온 된 타입으로 추론됨.
    => 특정 타입의 값으로 기대하고 쓰기 위해선 타입 좁히기가 필요하다!
  • 필수 매개변수 앞에 올 수 없음. 반드시 뒤에 배치해야 함.
function introduce(name="이정환", tall?: number){
	console.log(`name: ${name}`)'
    
    if(typeof tall === "number"){
    	console.log(`tall: ${tall + 10}`);
    }
}

introduce("이정환", 156); // 정상
introduce("이정환"); // 정상

 

 

나머지 매개변수(rest 매개변수)

  • 개별 요소를 배열로 묶는 변수. 함수에 전달한 인수들을 순차적으로 배열에 저장.
  • 다른 매개변수와 함께 사용하는 경우, 왼-> 오 순서로 인수를 할당함. 이 때 반드시 마지막에 선언되어야 함.
  • 기호 '...' 으로 표기
  • 타입 정의하는 법: 초기값 타입에 맞춘 배열로 정의
function getSum(...rest: number[]){
	let sum = 0; 
    rest.forEach((it) => (sum += it));
    return sum;
}

// sum이 number 타입이기 때문에 rest의 타입은 number[]로 정의
  • 매개변수의 길이를 고정하고 싶을 때? : 튜플 타입 이용
function getSum(...rest: [number, number, number]){
	let sum = 0;
    rest.forEach((it) => (sum += it));
    return sum;
}

getSum(1,2,3) // 정상
getSum(1,2) // 오류!

 

 

함수 타입 표현식(Function Type Expression)

  • 타입 별칭과 별개로 함수 타입을 정의하는 것.
  • 여러개의 함수가 동일한 타입을 가질 때 유용함.
// 함수 타입 표현식 사용 안 한 코드
const add = (a: number, b: number) => a + b;
const sub = (a: number, b: number) => a - b;
const multiply = (a: number, b: number) => a * b;
const divide = (a: number, b: number) => a / b;


// 함수 타입 표현식 사용 한 코드
type Operation = (a:number, b:number) => number;

const add:Operation = (a, b) => a+b;
const sub: Operation = (a, b) => a - b;
const multiply: Operation = (a, b) => a * b;
const divide: Operation = (a, b) => a / b;

 

호출 시그니처(Call Signature)

  • 함수 타입 표현식처럼 함수의 타입을 별도로 정의하는 방식
  • 객체를 정의하듯이 함수의 타입을 정의할 수 있음(이유: JS에선 함수도 객체이기 때문!)
  • 호출 시그니처 아래에 추가 프로퍼티 정의 가능.
    => 함수이자 일반 객체를 의미하는 '하이브리드 타입' 으로 정의됨.
type Operation2 = {
	(a:number, b:number):number;
    
    //추가 프로퍼티 정의
    name: string;
}

const add2: Operation2 = (a, b) => a + b;
const sub2: Operation2 = (a, b) => a - b;
const multiply2: Operation2 = (a, b) => a * b;
const divide2: Operation2 = (a, b) => a / b;

add(1,2) // 정상
add.name; // 정상

 

 

함수 타입의 호환성

  • 특정 함수 타입을 다른 함수 타입으로 보아도 괜찮은지 판단하는 것
  • 기준
    1. 두 함수의 반환값 타입이 호환되는가?
    2. 두 함수의 매개변수 타입이 호환되는가?

 

기준 1: 반환값 타입의 호환 여부

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

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

a = b // 정상
b = a // 오류! 넘버를 넘버리터럴 값에 넣을 수 없음(다운캐스팅 x)

 

기준2: 매개변수의 타입이 호환되는가?

매개변수의 개수가 같은지 다른지에 따라 판단 과정이 달라짐

 

2-1) 매개변수의 개수가 같을 때

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

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

c = d; // 오류
d = c; // 정상
  • 반환값 타입과 반대로 다운캐스팅이 허용되는 것 처럼 보임.
  • 매개변수의 타입에서는 값이 많은 쪽이 수퍼! 적은쪽이 서브!

 

2-2) 매개변수의 개수가 다를 때

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 // 오류!

 

 

함수 오버로딩

  • 하나의 함수를 매게변수의 개수나 타입에 따라 다르게 동작하게 만드는 문법.
  • 동작별 선언부인 '오버로드 시그니처'와, 실제 구현하는 부분인 '구현 시그니처'를 만들어야 함.
  • 구현 시그니처의 매개변수 타입은 모든 오버로드 시그니처와 호환되어야 함.
// 버전들 -> 오버로드 시그니처
function func(a:number): void;
function func(a:number, b:number, c:number): void;


// 실제 함수 구현 -> 구현 시그니처
function func(a:number, b?:number, c?:number) {
	if(typeof b === "number" && typeof c === "number"){
    	console.log(a+b+c);
    }else if{
    	console.log(a*20);
    }
}

func(1) // 20 
func(1,2) // 에러! 매개변수가 2개인 오버로드 시그니처는 만들어두지 않았음
func(1,2,3) // 6

 

사용자 정의 타입 가드

  • 참/거짓을 반환하는 함수를 이용하여 타입 가드를 커스텀 할 수 있는 문법.
// 예시
type Dog = {
  name: string;
  isBark: boolean;
};

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

type Animal = Dog | Cat;

function warning(animal: Animal) {
  if ("isBark" in animal) {
    console.log(animal.isBark ? "짖습니다" : "안짖어요");
  } else if ("isScratch" in animal) {
    console.log(animal.isScratch ? "할큅니다" : "안할퀴어요");
  }
}

 

  • 위 코드의 경우, Dog나 Cat 타입의 프로퍼티가 바뀌면 warning에서 오류가 발생할 수 있음. (ex: isBark -> isBarked 로 바뀐다던가..) => 타입 좁히기에 in 연산자를 사용하는 것은 비추천!
  • 따라서 함수를 이용해 커스텀 타입 가드를 만들어 타입을 좁히는 게 좋다!
type Dog = {
	name: string;
    isBark: boolean;
}

type Cat = {
	name: string;
    isScrcatch: boolean;
}

type Animal = Dog | Cat


// 타입가드
function isDog(animal:Animal):animal is Dog{
	return (animal as Dog).isBark !== undefined; 
}

function isCat(animal:Animal):animal is Cat {
	return (animal as Cat).isScratch !== undefined; // true
}

function warning(animal: Animal) {
  if (isDog(animal)) {
    console.log(animal.isBark ? "짖습니다" : "안짖어요");
  } else {
    console.log(animal.isScratch ? "할큅니다" : "안할퀴어요");
  }
}

 

※ 위 코드 해석

  1. isDog 함수가 매개변수로 받은 값이 Dog 타입이라면 true, 아니면 false를 반환함
  2. 반환값의 타입을 'animal is Dog' 로 정의했기 때문에, true값이 Dog 타입임을 보장한다는 의미가 됨.
  3. warining 함수에서 isDog 합수를 호출할 시, 매개변수의 값을 확인하고 타입 좁히기를 할 수 있음.

 

'개발공부' 카테고리의 다른 글

#6. 클래스  (0) 2025.02.27
#5. 인터페이스  (0) 2025.02.26
#3. 타입스크립트 이해하기  (0) 2025.02.20
#2. 타입스크립트 기본  (0) 2025.02.18
#1. 타입스크립트 개론  (0) 2025.02.17