typescript

함수와 타입

becky(지은) 2023. 6. 1. 22:08

 

0. 함수타입


자바스크립트 함수가 있을 때, 이 함수를 다른 사람에게 설명하는 가장 좋은 방법은 
이 함수가 어떤 매개변수를 받고 어떤 값을 반환 하는지 이야기 하는 것 입니다.

그럼 타입스크립트에서는 어떨까요?
어떤 타입의 매개변수를 받고, 어떤 타입의 값을 반환하는지 이야기 하면 됩니다. 


/**
 * 함수의 타입 정의하기
 */


//함수를 설명하는 가장 좋은 방법
// 어떤 매개변수를 받고, 어떤 결과값을 반환하는 지 이야기
// 어떤 [타입의] 매개변수를 받고, 어떤 [타입의] 결과값을 반환하는 지 이야기
function func(a:number, b:number){
 return a+b;
 //function func(a: number, b: number): number 
 //return 문을 보고 함수 타입을 추론함
}

/**
 * 화살표함수의 타입을 정의하는 방법
 */

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

/**
 * 함수의 매개변수
 */

function introduce(name = "정지은", tall?:number){ //name: string
    console.log(`name : ${name}`)
    if(typeof tall === "number"){
        console.log(`tall : ${tall +10}`) //'tall'은(는) 'undefined'일 수 있습니다. => 조건문으로 타입가드를 만들어서 타입 좁히기
    }
    
}

introduce("정지은", 163);
introduce("정지은") // 2개의 인수가 필요한데 1개를 가져왔습니다. = > 선택적(?) 매개변수 사용

// 선택적(?) 매개변수 쓸때 주의할점
// 선택적(?) 매개변수는 필수 매개변수 앞에 오면 X

function getSum(...rest: number[]){// ...rest 펼쳐져서 배열에 각각 담긴다 [1,2,3,4]
 let sum =0;
 rest.forEach((it)=> (sum += it))

 return sum;
}
getSum(1,2,3) //6
getSum(1,2,3,4,5) //15

//매개변수를 튜플 타입(길이가 정해져있는)으로도 만들 수 잇음
function getSum2(...rest: [number, number, number]){ //...rest 펼쳐져서 배열에 각각 담긴다 [1,2,3,4]
 let sum =0;
 rest.forEach((it)=> (sum += it))

 return sum;
}
getSum2(1,2,3) //6
getSum2(1,2,3,4,5) //15, 오류: 3개의 인수가 필요한데 5개를 가져왔습니다.








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


다음과 같이 함수 타입을 타입 별칭과 함께 별도로 정의할 수 있습니다. 
이를 함수 타입 표현식(Function Type Expression)이라고 부릅니다.

더불어, 호출 시그니쳐(Call Signature)는 함수 타입 표현식과 동일하게 함수의 타입을 별도로 정의하는 방식입니다.



/**
 * 함수 타입 표현식과 호출시그니쳐
 * => 함수의 타입을 별도로 정의(타입별칭사용)하는 방법
 */


type Operation = (a:number, b:number) => number; // 함수타입 표현식 사용, 범용적 사용하기 굳

const add0 = (a:number, b:number):number => a + b; // 타입별칭이 없다면
const add1: 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;

/**
 * 호출(콜)시그니처 
 * (위에 처럼 함수의 타입을 분리해서 정의가능)
 */

type Operation2 ={ // 함수도 객체라서 {} 쓰는것
    (a:number,b:number):number;
}
// function func(a:number, b:number):void; {} 이런 형태를 톡 떼온것!

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;







2. 함수 타입의 호환성



함수 타입의 호환성이란 특정 함수 타입을 다른 함수 타입으로 괜찮은지 판단하는 것을 의미합니다.
다음 2가지 기준으로 함수 타입의 호환성을 판단하게 됩니다.
두 함수의 반환값 타입이 호환되는가?
두 함수의 매개변수의 타입이 호환되는가?
첫번째 기준부터 차례대로 살펴보겠습니다.


/**
 * 함수 타입의 호환성
 * 기본타입, 객체타입 호환성과 마찬가지로
 * 특정함수 타입을 다른 함수 타입으로 취급해도 괜찮은가를 판단
 * 1.반환값(리턴문)의 타입이 호환되는가
 * 2.매개변수의 타입이 호환되는가
 */

//기준1. 반환값이 호환되는가
//특이하게! 매개변수 기준으로 타입호환을 판단하려고 하면, 업케스팅은 불가
type A = () => number;
type B = () => 10; 

let a:A = () => 10; //number 타입
let b:B = () => 10; //number literal 타입

a = b
b = a //오류,  'number' 형식은 '10' 형식에 할당할 수 없습니다. (다운캐스팅, 오류)

//기준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 // 매개변수 기준으로 하면 다운캐스팅 가능


//매개변수가 객체 타입인 경우, 예시 보면 쉬움

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);
    console.log(dog.color);
};

animalFunc = dogFunc  //(dog: Dog) => void' 형식은 '(animal: Animal) => void' 형식에 할당할 수 없습니다.
//animalFunc의 매개변수에 dogFunc의 {} 안의 값을 출력
let testFunc = (animal: Animal) =>{
    console.log(animal.name);
    console.log(animal.color); //오류,Animal' 형식에 'color' 속성이 없습니다.
}

dogFunc = animalFunc 
//dogFunc의 매개변수에 animalFunc 의 {} 안의 값을 출력
let testFunc2 = (dog: Dog) => {
    console.log(dog.name)
}

//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; //오류, 매개변수 기준으로 (a)에 (a,b)가 있다는 조건 만족 X





 3. 함수 오버로딩



함수 오버로딩이란 하나의 함수를 매개변수의 개수나 타입에 따라 다르게 동작하도록 만드는 문법입니다.

/**
 * 함수의 오버로딩 (오직 타입스크립트에서만, 자스는 안됨)
 * 하나의 함수를 매개변수의 개수나 타입에 따라
 * 여러가지 방법으로 정의하는 방법
 */

/**
 * 하나의 함수 func를 만들것임
 * 모든 매개변수의 타입은 number
 * Ver1. 매개변수가 1개 -> 이 매개변수에 20을 곱한값을 출력
 * Ver2. 매개변수가 3개 -> 이 매개변수들을 다 더한 값을 출력
 */

//매개변수 버전들 -> 오버로드 시그니쳐
function func(a:number):void; // 만약, 실제 구현부에서 function func(a:number, b:number, c:number){} 이렇게 쓰면, 이 오버로드 시그니처는 해당 구현 시그니처와 호환되지 않습//오류, 함수 구현이 없거나 선언 바로 다음에 나오지 않습니다.
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{
    console.log(a*20);
    }
}

//호출해보면
func() //오류, 매개변수 버전들과 맞지 않음
func(1);
func(1,2); //오류, 매개변수 버전들과 맞지 않음
func(1,2,3);



4. 사용자 정의 타입가드



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

/**
 * 사용자 정의 타입가드
 */

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

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

type Animal = Dog | Cat;

//만약 서로소 유니온 타입을 쓸수 없는상황, 즉 기존 타입별칭을 손댈 수 없는 상황이라면,,,
//"isBark" in animal 이런식으로 쓰면 타입이 잘 좁혀지지 않음

// 사용자 정의 타입가드
function isDog(animal:Animal):animal is Dog{//리턴문의 animal타입은 Dog다
  return (animal as Dog).isBark !== undefined;
};
function isCat(animal:Animal):animal is Cat{//리턴문의 animal타입은 Cat다
 return (animal as Cat).isScratch !== undefined;
}

function warning(animal:Animal){
 if(isDog(animal)){
    // 강아지 타입
    animal; //if문이 참이면 리턴문의 animal의 타입은 Dog 이다
 }else if(isCat(animal)){
    // 고양이 타입
    animal; //if문이 참이면리턴문의 animal의 타입은 Cat이다
 }
}