매개변수 이름 뒤에 ?를 붙이면 부를 때 비워도 되는 선택 매개변수(optional parameter)가 되고, 그 타입에는 undefined가 더해집니다. 기본값을 준 매개변수는 그 값에서 타입이 추론됩니다.
앞 레슨에서 검사기는 인자 수가 매개변수 수와 다르면 막았습니다(TS2554). 메모처럼 없어도 되는 인자는 이 두 꼴로 적어 검사기에게 알립니다.
1선택 매개변수는 이름 뒤에 ?
memo?: string은 'memo는 비워도 된다'는 뜻입니다. 그래서 7번 줄처럼 memo를 비우고 불러도 검사를 지납니다.
function receipt(
name: string,
memo?: string,
) {
console.log(name, memo);
}
receipt('라테');
receipt('라테', '얼음 적게');
라테 undefined
라테 얼음 적게함수 안에서 memo의 지금 타입은 string | undefined입니다(|는 '이거나'). 부르는 쪽이 비우면 실행 때 undefined가 오기 때문에, 검사기는 memo의 선언 타입 모음에 undefined를 더해 둡니다(검사 규칙 2).
undefined일 수 있는 memo를 확인 없이 글자처럼 쓰면 막힙니다. 이것은 자주 틀리는 곳 첫째에서 봅니다.
인자는 차례로 매개변수와 짝지어집니다. 그래서 비워도 되는 매개변수는 꼭 있어야 하는 매개변수 뒤에 둡니다.
function receipt(
memo?: string,
name: string,
) {
console.log(name, memo);
}
receipt('얼음 적게', '라테');
main.ts(3,3): error TS1016: A required parameter cannot follow an optional parameter.라테 얼음 적게TS1016은 '꼭 있어야 하는 매개변수는 선택 매개변수 뒤에 올 수 없다'는 뜻입니다. 3번 줄 name이 선택 매개변수 memo 뒤에 있습니다. 인자를 하나만 넘기면 그 인자가 앞의 memo와 짝지어지고 name이 비게 되니, 이런 머리는 처음부터 막습니다. Node는 검사하지 않으니 그대로 돌립니다.
2기본값 매개변수는 값에서 타입이 정해진다
JS 다시 보기: 매개변수 기본값은 인자가 없거나 undefined일 때만 쓰입니다(JS '기본값 매개변수').
qty = 1처럼 기본값을 준 매개변수도 부를 때 비워도 됩니다. 아래 3번 줄 qty에는 타입을 적지 않았습니다.
9번 줄은 검사를 지나갈까요? 답을 정한 뒤 펼쳐 보세요.
function order(
item: string,
qty = 1,
) {
console.log(item, qty);
}
order('쿠키');
order('쿠키', 3);
order('쿠키', '2');
검사 결과와 실행 결과 보기
main.ts(9,13): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.쿠키 1
쿠키 3
쿠키 2예상과 다르면 그곳이 배울 곳입니다. 9번 줄이 막힙니다. 타입을 적지 않았어도 qty의 선언 타입은 기본값 1에서 추론한 number입니다(검사 규칙 3).
앞 레슨의 TS7006(타입이 정해지지 않은 매개변수)이 나지 않은 것도 그래서입니다. 기본값이 있으면 그 값에서 타입이 정해집니다.
함수 안에서 qty의 지금 타입은 number이고 undefined가 더해지지 않습니다. 비우면 기본값 1이 쓰이기 때문입니다.
두 꼴을 비우고 불렀을 때를 견주면 이렇습니다.
| 꼴 | memo?: string | qty = 1 |
|---|---|---|
| 비우고 부르면 | undefined | 기본값 1 |
| 함수 안의 지금 타입 | string | undefined | number |
예제
영수증 한 줄을 찍는 함수입니다. 메모가 없으면 기본값 '없음'을 씁니다.
function receipt(
name: string,
memo = '없음',
) {
console.log(name, memo);
}
receipt('라테');
receipt('쿠키', '포장');
receipt('녹차', undefined);
라테 없음
쿠키 포장
녹차 없음7번 줄처럼 memo를 비우거나 9번 줄처럼 undefined를 넘기면 기본값 '없음'이 쓰입니다.
함수 안 memo의 지금 타입은 string이라, 확인하지 않고 글자로 써도 검사를 지납니다.
자주 틀리는 곳
틀린 생각: 선택 매개변수도 함수 안에서는 늘 값이 있다.
함수 머리에 memo?: string이라고 타입을 적었으니, 함수 안의 memo도 글자일 것 같습니다. 그러나 부르는 쪽이 비우면 memo는 undefined입니다.
function receipt(
name: string,
memo?: string,
) {
return memo.length;
}
console.log(receipt('라테'));
main.ts(5,10): error TS18048: 'memo' is possibly 'undefined'.main.ts:5 return memo.length; ^ TypeError: Cannot read properties of undefined (reading 'length') at receipt (main.ts:5:15) at main.ts:7:13TS18048은 'memo가 undefined일 수 있다'는 뜻입니다. 검사기는 memo의 지금 타입 string | undefined 가운데 undefined에는 length가 없어서 막습니다.
Node로 돌리면 7번 줄이 memo를 비웠으니 5번 줄에서 TypeError로 멈춥니다.
맞는 규칙: ?를 붙인 매개변수의 선언 타입에는 undefined가 더해지고, 부르는 쪽이 비우면 실행 때 정말 undefined가 옵니다. 그래서 쓰기 전에 확인하거나(4단원 null·undefined 거르기) 기본값을 줍니다(검사 규칙 2).
틀린 생각: ?와 기본값을 함께 쓰면 비워도 된다는 것이 더 분명해진다.
기본값이 있는 매개변수도 비워도 되니, ?까지 붙이면 뜻이 더 또렷할 것 같습니다. 그러나 둘을 함께 쓰는 것은 문법 오류입니다.
function receipt(
name: string,
memo?: string = '없음',
) {
console.log(name, memo);
}
receipt('라테');
main.ts(3,3): error TS1015: Parameter cannot have question mark and initializer.main.ts:3 name: string, memo?: string = '없음', ^^^^^^^^^^^^^) { SyntaxError [ERR_INVALID_TYPESCRIPT_SYNTAX]: Parameter cannot have question mark and initializer { code: 'ERR_INVALID_TYPESCRIPT_SYNTAX'}TS1015는 '매개변수에 물음표와 기본값을 함께 둘 수 없다'는 뜻입니다. 이번에는 Node도 돌리지 못합니다. 타입 글자를 지워 돌리려다 지울 수 없는 문법을 만나, 한 줄도 실행하지 않고 멈춥니다.
맞는 규칙: 기본값을 준 매개변수는 이미 비워도 되는 매개변수입니다. ?와 기본값 가운데 하나만 씁니다.
확인 문제
1. 예제의 3번 줄 memo = '없음',을 memo?: string,으로 바꿨습니다. 검사는 지나갈까요? Node로 돌리면 무엇이 찍힐까요? 마지막 줄까지 글자 그대로 쓰세요.
function receipt(
name: string,
memo?: string,
) {
console.log(name, memo);
}
receipt('라테');
receipt('쿠키', '포장');
receipt('녹차', undefined);
힌트
?도 비워도 된다는 표시입니다. 비웠을 때 memo에 무엇이 오는지 2절 표의 memo?: string 칸에서 찾습니다.
정답
라테 undefined
쿠키 포장
녹차 undefined검사는 오류 없이 지나갑니다. memo를 비우거나 undefined를 넘기는 부르기 모두 ?로 받을 수 있습니다.
실행 결과는 첫 줄과 셋째 줄이 바뀝니다. 기본값이 없으니 비운 memo는 undefined이고, console.log가 그대로 찍습니다.
흔한 오답은 '없음'이 그대로 찍힌다는 답입니다. ?도 기본값처럼 무엇인가를 채워 준다고 본 것입니다. ?는 비워도 된다는 표시일 뿐입니다.
2. 실행 결과가 쿠키 1 · 라테 2 두 줄이 되도록 3번 줄의 빈칸을 채우세요. 검사도 지나야 합니다.
function order(
item: string,
qty____,
) {
console.log(item, qty);
}
order('쿠키');
order('라테', 2);
힌트
7번 줄은 qty를 비웠는데 1이 찍힙니다. 비웠을 때 쓸 값을 정해 두는 꼴을 떠올립니다.
정답
빈칸은 = 1입니다. 3번 줄이 qty = 1,이 됩니다.
function order(
item: string,
qty = 1,
) {
console.log(item, qty);
}
order('쿠키');
order('라테', 2);쿠키 1
라테 2다르게 써도 되는 곳: qty: number = 1처럼 타입과 기본값을 함께 적어도 같습니다. ?: number도 검사는 지나지만, 7번 줄이 쿠키 undefined를 찍습니다.
흔한 오답은 ?: number입니다. 비워도 된다는 것만 맞추고 비웠을 때 오는 값을 놓친 것입니다.
3. 장본 물건 한 줄의 값을 내는 함수입니다. 개수를 비우면 1개로 셈하고, 쿠폰이 있으면 그 값만큼 뺍니다. 검사 결과와 Node로 돌린 실행 결과를 각각 쓰세요.
function pay(
price: number,
qty = 1,
coupon?: number,
) {
return price * qty - coupon;
}
console.log(pay(1200, 3, 500));
console.log(pay(1200));
힌트
6번 줄에 쓰인 매개변수 셋의 지금 타입을 하나씩 봅니다. qty와 coupon은 2절 표의 어느 칸과 같은 꼴인가요?
정답
main.ts(6,24): error TS18048: 'coupon' is possibly 'undefined'.3100
NaN검사는 6번 줄에서 막힙니다. coupon은 ?를 붙여 지금 타입이 number | undefined라, 비었을 수도 있는 값을 빼는 셈을 검사기가 막습니다(TS18048). qty는 기본값이 있어 지금 타입이 number이니 price * qty는 막히지 않습니다.
Node는 그대로 돌립니다. 9번 줄은 qty에 기본값 1이 쓰여 price * qty가 1200이고, coupon이 undefined라 1200 - undefined가 NaN입니다.
흔한 오답은 '검사를 지난다'입니다. coupon에도 number라고 적었으니 늘 수가 온다고 본 것입니다(꺼낸 값은 늘 있다는 생각). qty에서도 막힌다는 답은 기본값도 ?처럼 undefined를 더한다고 본 것입니다.
쿠폰이 없을 때 0을 빼게 하려면 4번 줄을 coupon = 0,으로 씁니다. 그러면 검사 오류가 없고 9번 줄은 1200을 찍습니다.
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
1절 첫 코드를 main.ts로 저장해 npx tsc로 검사하고 node main.ts로 돌립니다. 그다음 3번 줄 memo?: string,에서 ?를 빼면 검사 결과가 어떻게 바뀔지 먼저 적고, 다시 검사해 맞춰 봅니다.
검사하고 돌리는 법은 내 컴퓨터에서 검사하고 돌리기에 있습니다.