타입스크립트 기초 문법 - 함수 타입과 호환성
1. 함수에서의 타입 정의
보통의 함수에는 매개변수와 반환값이 존재한다.
반환값은 타입을 설정하지 않아도 자동으로 추론되지만
매개변수는 타입을 꼭 설정해줘야한다.
✔️화살표함수도 마찬가지
function foo(a:number,b:number){
return a+b;
}
const foo = (a:number,b:number) => a+b;
타입 별칭에서도 선택적 프로퍼티를 사용할 수 있듯이 함수의 매개변수도 선택적으로 사용할 수 있다.
function foo(name:string, age?:number){
...
}
하지만 이 경우 선택적 매개변수는 지정된 타입 혹은 undefined로 추측되기때문에 타입좁히기가 필요하다.
function foo(name:string, age?:number){
if(typeof age === "number"){
실행문
}
}
선택적 매개변수는 반드시 모든 변수의 맨 뒤에 와야한다.
- ...rest 매개변수
여러 숫자를 하나의 배열로 묶어주는 역할
때문에 ...rest의 타입을 정의할 땐 number[]로 정의한다.
function foo(...rest:number[]){
실행문
}
결국 배열의 형태이기때문에 튜플 타입으로 설정해도 무방하다.
function foo(...rest:[number,number]){
실행문
}
다만 이렇게 되면
foo(123,10) //정상
foo(12,5,90) //오류
함수의 타입을 정의하는 방법은 크게 두 가지가 있다.
2.함수 타입 표현식
객체에서 자주 써먹었던 타입 별칭을 이용한 방법을 함수에서도 쓸 수 있다.
type Add = (a:number, b:number) => number;
const add:Add = (a,b) => a+b;
함수의 선언&구현 부분과 타입을 분리할 수 있어 훨씬 읽기 깔끔한 코드가 된다.
그리고 반환값이 다른 여러 개의 함수가 동일한 타입을 갖고있을 때도 깔끔하게 코드 정리 가능(아래 예시)
type Add = (a:number, b:number) => number;
const add:Add = (a,b) => a+b;
const sub:Add = (a,b) => a-b;
const mul:Add = (a,b) => a * b;
3.호출 시그니쳐
객체에서의 타입별칭 형식처럼 정의하는 방법이다.
type Func = {
(a:number, b:number) : number;
}
이런식
4. 함수의 호환성
4-1. 반환값 타입에 따라
기본 타입의 호환성을 따졌을 때 처럼 a =b 일 때 반환값 a가 b의 슈퍼타입일 때 가능하다.
type A = () => number;
type B = () => 100;
let a:A = () => 100;
let b:B = () => 100;
a = b // 가능
b = a // 오류
함수 a의 반환값 타입은 number
함수 b의 반환값 타입은 number 리터럴타입으로 a함수타입이 b함수타입의 슈퍼타입
a=b의 관계에서 a가 b의 슈퍼타입이어야한다는 점을 생각하면 당연한 원리이다.
4-2. 매개변수의 타입에 따라
- 매개변수의 개수가 같을 때
하지만 조금 다르게 생각해야 하는 경우가 있다.
type A = (val: number) => void;
type B = (val: 10) => void;
let a:A = (val) => {};
let b:B = (val) => {};
이 경우
b = a // 서브 = 슈퍼 관계일 때 호환된다.
왜냐하면
매개변수 타입이 객체 타입일 때를 예로 들어 생각해볼 수 있다.
type A = {
name:string;
}
type B = {
name=string;
age:number
}
let a = (value:A) => {
console.log(value.name)
}
let b = (value2:B) {
console.log(value.name)
console.log(value. age)
}
a = b 결과
let a = (value:A) => {
console.log(value.name);
console.log(value.age)
}
매개변수 타입은 A를 따르고 있지만 함수 실행문 부분에서는 age라는 타입 B의 프로퍼티에도 접근하고있기기에 오류가 발생한다.
반대로
b=a 결과
let b = (value2:B) => {
console.log(value2.name);
}
으로 정상적으로 name 프로퍼티에 접근한다.
- 매개변수의 개수가 다를 때
type A = (a:number; b:number) => void;
type B = (a:number) => void;
이런 경우
let a:A = (a,b) => {};
let b:B = (a) => {};
a = b //일 때 호환된다.