본문으로 건너뛰기

선택·기본값 매개변수

타입스크립트 목차기본 타입과 함수레슨 10 / 51

목차

매개변수 이름 뒤에 ?를 붙이면 부를 때 비워도 되는 선택 매개변수(optional parameter)가 되고, 그 타입에는 undefined가 더해집니다. 기본값을 준 매개변수는 그 값에서 타입이 추론됩니다.

앞 레슨에서 검사기는 인자 수가 매개변수 수와 다르면 막았습니다(TS2554). 메모처럼 없어도 되는 인자는 이 두 꼴로 적어 검사기에게 알립니다.

6단계 중 1단계. 1~4번 줄: name은 적은 대로 string입니다. memo 뒤의 ?는 비워도 된다는 표시입니다.

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('얼음 적게', '라테');
검사 결과 오류 1개
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');
검사 결과와 실행 결과 보기
검사 결과 오류 1개
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?: stringqty = 1
비우고 부르면undefined기본값 1
함수 안의 지금 타입string | undefinednumber

예제

영수증 한 줄을 찍는 함수입니다. 메모가 없으면 기본값 '없음'을 씁니다.

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('라테'));
검사 결과 오류 1개
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:13

TS18048은 '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('라테');
검사 결과 오류 1개
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절 표의 어느 칸과 같은 꼴인가요?

정답
검사 결과 오류 1개
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,에서 ?를 빼면 검사 결과가 어떻게 바뀔지 먼저 적고, 다시 검사해 맞춰 봅니다. 검사하고 돌리는 법은 내 컴퓨터에서 검사하고 돌리기에 있습니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

타입스크립트 목차로