티스토리 뷰

함수와 타입

1. 함수 타입

타입스크립트에서 함수를 이야기 하려면, 어떤 타입의 매개변수를 받고, 어떤 타입의 값을 반환하는 지를 이야기 해야 한다.

 

따라서 함수의 타입은 매개변수반환값의 타입으로 결정된다. 이때 반환값은 리턴문을 기준으로 자동추론 해주기도 한다.

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

 

화살표 함수 타입 정의하기

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

 

매개변수 기본값 설정하기

매개변수의 타입을 정의하지 않아도 함수 매개변수에 기본값이 설정되어 있다면 타입을 잘 추론해준다. 이럴 때는 타입 정의를 생략해도 된다.

 

이때 몇 가지 주의할 점이 있는데, 기본값과 다른 타입으로 매개변수의 타입을 정의하면 오류 발생한다는 것이다. 또한 호출시 자동추론된 매개변수의 타입과 다른 타입의 변수를 전달하면 오류가 발생한다.

 

선택적 매개변수처럼 생략가능한 매개변수는 변수명 뒤에 ? 붙이면 된다. 선택적 매개변수를 넣어주지 않으면 기본적으로 undefined 타입으로 추론되기 때문에 선택적 매개변수를 활용하려면 타입 좁히기가 필요하다. 또한 선택적 매개변수 뒤에 필수 매개변수가 오면 안된다.

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

introduce("홍길동", 190);
introduce("홍길동");

 

Rest 파라미터(나머지 매개변수)

여러 매개변수를 인수로 받는 함수가 있을 때, 이 매개변수의 길이가 가변적이라면 어떻게 할까?

 

자바스크립트와 동일하게 rest를 활용하여 배열 형태로 매개변수를 담은 배열을 전달받을 수 있다.

function getSum(...rest: number[]) {
	let sum = 0;
	rest.forEach((it) => (sum += it));

	return sum;
}

 

2. 함수 타입 표현식과 호출 시그니쳐

함수 타입을 별도로 정의하는 방법에는 함수 타입 표현식을 활용하는 것과 호출 시그니쳐를 활용하는 방법이 있다.

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

함수 타입을 타입 별칭과 함께 별도로 정의하는 방법이다.

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;

함수 타입 표현식을 이용하면 함수 선언 및 구현 코드와 타입 선언을 분리할 수 있어 유용하다.

 

호출 시그니쳐(Call Signature)

함수 타입을 객체 정의하듯 별도로 정의하는 방법이다.

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

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

 

참고로 호출 시그니쳐 아래에 프로퍼티를 추가 정의하는 것도 가능하다. 이렇게 할 경우 함수이자 일반 객체를 의미하는 타입으로 정의되며 이를 하이브리드 타입이라고 부른다.

type Operation2 = {
  (a: number, b: number): number; // 호출 시그니쳐
  name: string; // 일반 프로퍼티
};

const add2: Operation2 = (a, b) => a + b;

add2(1,2)
add.name

 

3. 함수 타입의 호환성

특정 함수 타입을 다른 함수 타입으로 취급해도 괜찮은가를 판단하는 것에 대한 이야기이다.

이때 아래의 두 가지 조건을 모두 만족시켜야 두 함수가 호환 가능하다고 말할 수 있다.

 

  1. 반환값의 타입이 호환되는가?
  2. 매개변수의 타입이 호환되는가?

기준 1 : 반환값의 타입이 호환되는가?

업캐스팅만 허용

type A = () => number; // 반환값 : number
type B = () => 10; // 반환값 : number literal

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

a = b; // 허용 : 반환값 기준 업캐스팅(number literal -> number)
// b = a; // 비허용 : 반환값 기준 다운캐스팅(number -> number literal)

 

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

매개변수의 개수가 같을 때

다운캐스팅만 허용

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

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

// c = d; // 불가능 : 매개변수 기준 업캐스팅 (number literal -> number ==> 업캐스팅 비허용)
d = c; // 가능 : 다운캐스팅 (number -> number literal) 허용

 

 

이해를 돕기위해서 매개변수가 객체인 아래의 경우를 살펴보자.

// 슈퍼타입
type Animal = {
  name: string;
};

// 서브 타입
type Dog = {
  name: string;
  color: string;
};

let animalFunc = (animal: Animal) => {
  console.log(animal.name);
};
let dogFunc = (dog: Dog) => {
  console.log(dog.name, dog.color);
};

// animalFunc = dogFunc; // 에러 : 업캐스팅 불가능
dogFunc = animalFunc; // 가능 : 다운캐스팅 허용(매개변수에서만)

animalFunc에 dogFunc을 할당하는 것은 불가능하다.

전자의 매개변수의 타입이 후자의 매개변수 타입보다 작은 서브타입이기 때문이다.

 

 

animalFunc = dogFunc를 코드로 표현하면 다음과 같다.

let animalFunc = (animal : Animal) => {
    console.log(animal.name); // 가능
    console.log(animal.color); // 불가능
}

 

반대로 dogFunc = animalFunc를 코드로 표현하면 다음과 같다.

let dogFunc = (dog: Dog) => {
    console.log(dog.name); // 가능
}

 

매개변수의 개수가 다를 때

매개변수의 개수가 더 작은 쪽만 개수가 더 큰 쪽으로 호환 가능하다.

type F1 = (a: number, b: number) => void;
type F2 = (a: number) => void;

let f1: F1 = (a, b) => {};
let f2: F2 = (a) => {};

f1 = f2;
// f2 = f1; 호환 불가능

 

4. 함수 오버로딩

함수를 매개변수의 개수나 타입에 따라 여러가지 버전으로 정의하는 방법으로 타입스크립트에서만 지원한다.

step 1. 오버로드 시그니쳐 만들기

함수 오버로딩을 구현하려면 버전별로 오버로드 시그니쳐를 만들어야 한다. 즉, 구현할 버전 전체를 선언하는 것이다.

function f(a: number): void;
function f(a: number, b: number, c: number): void;

 

step 2. 구현 시그니쳐(실제 구현부) 정의하기

오버로드 시그니쳐를 만들었다면 다음으로는 구현 시그니쳐를 만들어줘야 한다. 실제로 함수가 어떻게 실행될 것인지 정의하는 부분이다.

이때, 구현 시그니쳐의 매개변수 타입은 모든 오버로드 시그니쳐와 호환되도록 만들어야 한다.

function f(a: number, b?: number, c?: number) {
  if (typeof b === "number" && typeof c === "number") {
    console.log(a + b + c);
  } else {
    console.log(a + 10);
  }
}

 

5. 사용자 정의 타입가드

사용자 정의 타입가드란 참 또는 거짓을 반환하는 함수를 이용해 사용자의 입맛대로 타입 가드를 만들 수 있도록 도와주는 타입스크립트의 문법이다.

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

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

type Animal = Dog | Cat; // Union 타입 정의

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

// Cat 타입인지 확인하는 타입 가드
function isCat(animal: Animal): animal is Cat {
  return (animal as Cat).isScratch !== undefined;
}

function warning(animal: Animal) {
  if (isDog(animal)) {
    console.log(animal.isBark ? "짖는다" : "안 짖는다");
  } else {
    console.log(animal.isScratch ? "할퀸다" : "안 할퀸다");
  }
}

isDog 함수는 매개변수로 받은 animal 객체가 Dog 타입이라면 true를 반환한다. 이때 반환값의 타입으로 animal is Dog라고 정의한다는 것은, 이 함수가 true를 반환하면 조건문 내부에서는 animal을 Dog 타입으로 보장한다는 의미가 된다.


본 포스팅은 인프런 한 입 크기로 잘라먹는 타입스크립트 강의를 기반으로 작성되었습니다. 사용된 이미지들은 모두 위 강의에서 가져왔습니다. 문제시 알려주세요.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함