※ 이 강의는 이정환 강사님의 '한 입 크기로 잘라먹는 타입스크립트' 강의를 듣고 정리한 글입니다.
함수 타입을 정의하는 방법
// 기본 함수 정의
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: 반환값 타입의 호환 여부
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 ? "할큅니다" : "안할퀴어요");
}
}
※ 위 코드 해석
- isDog 함수가 매개변수로 받은 값이 Dog 타입이라면 true, 아니면 false를 반환함
- 반환값의 타입을 'animal is Dog' 로 정의했기 때문에, true값이 Dog 타입임을 보장한다는 의미가 됨.
- 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 |