본문으로 건너뛰기

단원 연습 문제

타입스크립트 목차모듈·선언·설정문제 12개

10단원 '모듈 · 선언 파일 · 설정'의 다섯 레슨에서 여덟 문제를 내고, 앞 단원에서 두 문제를 섞었습니다. 끝의 만들어 보기 둘은 직접 코드를 채우고 짜 보는 문제입니다. 이 단원을 처음 본다면 진단 문항인 문제 1 · 2 · 4 · 6 · 7부터 풀어 보세요. 막히는 문항에 적힌 레슨만 골라 읽어도 됩니다.

문제 1

(진단 · 모듈과 타입 내보내기) menu.ts에 export interface Menu⁠만 들어 있을 때, main.ts에서 type 없이 import { Menu }로 가져오면 검사와 실행 때 각각 어떻게 될까요?

  • ① 타입 검사와 Node 실행 모두 문제없이 통과한다
  • ② 타입 검사 때는 TS1484로 막히고, Node로 실행하면 menu.ts에서 Menu가 지워져 SyntaxError로 멈춘다
  • ③ 타입 검사 때는 통과하고, 실행 때 빈 객체 {}가 넘어온다
힌트

menu.ts의 interface Menu가 타입 지우기 단계 뒤에 남는지 생각해 보세요.

정답

정답: ②

interface Menu는 타입 지우기 단계에서 사라지므로 실행 시점의 menu.ts에는 Menu가 없습니다(타입이 실행 때 남는다는 생각). 따라서 import type { Menu }로 가져와야 하며, type을 빼면 검사기는 TS1484로 막고 Node는 SyntaxError로 멈춥니다.

다시 볼 레슨: 모듈과 타입 내보내기

문제 2

(진단 · 선언 파일과 @types) 과목 기본 tsconfig.json("types": [])에서 다음 코드를 타입 검사하면 어떤 결과가 나올까요?

console.log(process.version);
  • ① Node 내장 기능이므로 검사 오류 0으로 통과한다
  • ② process의 선언을 찾지 못해 TS2591 오류가 난다 (@types/node 설치와 "types": ["node"] 설정 필요)
  • ③ 문법 오류(TS1294)가 난다
힌트

"types": [] 설정일 때 검사기가 외부 선언 패키지를 자동으로 읽는지 떠올려 보세요.

정답

정답: ②

검사 결과 오류 1개
main.ts(1,13): error TS2591: Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.

TypeScript 7.0과 과목 tsconfig.json은 "types": []가 기본이므로, @types/node를 설치하고 "types": ["node"]를 적어야 Node 전용 전역 이름인 process의 타입을 읽습니다.

다시 볼 레슨: 선언 파일과 @types

문제 3

(선언 파일과 @types) 다음 코드를 타입 검사할 때와 Node로 실행할 때의 결과로 맞는 것은?

declare const bonus: number;
console.log(bonus + 100);
  • ① 타입 검사는 통과하지만, 실행 때는 bonus 값이 실제로 없어 ReferenceError로 멈춘다
  • ② 타입 검사와 실행 모두 통과하여 100이 출력된다
  • ③ 1번 줄에서 타입 검사 오류가 난다
힌트

declare 줄이 타입 지우기 뒤에 무엇으로 바뀌는지 생각해 보세요.

정답

정답: ①

실행 결과
main.ts:2console.log(bonus + 100);            ^ ReferenceError: bonus is not defined    at main.ts:2:13

declare는 검사기에게 "실행 환경에 이 값이 있다"고 믿게만 할 뿐(검사 규칙 6), 실제 실행 값을 만들어 주지 않습니다(타입이 실행 때 남는다는 생각).

다시 볼 레슨: 선언 파일과 @types

문제 4

(진단 · DOM 요소의 타입) 다음 브라우저 코드를 타입 검사하면 3번 줄에서 왜 막힐까요?

const input = document
  .querySelector('#qty');
console.log(input.value);
  • ① input이 null일 수 있다는 오류(TS18047) 하나만 난다
  • ② input이 null일 수 있고(TS18047), Element에는 value 속성이 없다(TS2339)는 두 오류가 함께 난다
  • ③ 오류가 나지 않는다
힌트

document.querySelector⁠가 돌려주는 타입(Element | null)의 두 갈래를 살펴보세요.

정답

정답: ②

검사 결과 오류 2개
main.ts(3,13): error TS18047: 'input' is possibly 'null'.main.ts(3,19): error TS2339: Property 'value' does not exist on type 'Element'.

document.querySelector⁠의 반환 타입은 Element | null이므로, 요소를 못 찾았을 때의 null 가능성과 일반 Element에 value가 없다는 점 두 가지를 instanceof HTMLInputElement⁠로 함께 좁혀야 합니다.

다시 볼 레슨: DOM 요소의 타입

문제 5

(DOM 요소의 타입) 다음 코드에서 9번 줄이 타입 검사에서 막히는 까닭은 무엇일까요?

const input = document
  .querySelector('#qty');

if (
  input instanceof
  HTMLInputElement
) {
  const total =
    input.value * 4000;
  console.log(total);
}
  • ① input이 여전히 null일 수 있기 때문이다
  • ② HTMLInputElement의 value 타입은 언제나 string이라 산술 곱셈(*) 왼쪽 자리에 바로 쓸 수 없기 때문이다 — TS2362
  • ③ HTMLInputElement에는 value 속성이 없기 때문이다
힌트

HTMLInputElement로 좁힌 뒤 input.value의 타입이 number인지 string인지 떠올려 보세요.

정답

정답: ②

검사 결과 오류 1개
main.ts(9,5): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.

input.value는 언제나 string이므로 수 계산에 쓰려면 Number(input.value) * 4000⁠으로 변환해야 합니다.

다시 볼 레슨: DOM 요소의 타입

문제 6

(진단 · tsconfig.json 읽기) 과목 tsconfig.json의 옵션과 그 역할이 잘못 짝지어진 것은?

  • ① "noEmit": true — tsc가 .js 파일을 만들지 않고 타입 검사만 하게 한다
  • ② "erasableSyntaxOnly": true — enum이나 매개변수 속성처럼 타입만 지워서는 실행할 수 없는 문법을 TS1294로 막는다
  • ③ "verbatimModuleSyntax": true — 모든 파일을 자동으로 .js로 변환해 저장한다
힌트

verbatimModuleSyntax가 import type과 어떤 관련이 있는지 떠올려 보세요.

정답

정답: ③

"verbatimModuleSyntax": true⁠는 타입을 가져올 때 반드시 import type을 쓰도록 강제해(TS1484), 지울 줄과 남길 줄을 명확히 구분하게 하는 옵션입니다.

다시 볼 레슨: tsconfig.json 읽기

문제 7

(진단 · strict가 켜는 것들) 과목 기본 설정("strict": true)에서 다음 코드를 검사하고 실행한 결과로 맞는 것은?

function half(n) {
  return n / 2;
}
console.log(half(10));
  • ① 검사 때는 noImplicitAny 때문에 TS7006으로 막히지만, Node 실행 결과는 5가 찍힌다
  • ② 검사 때도 막히고 Node 실행도 멈춘다
  • ③ "strict": true라도 n / 2를 보고 n을 number로 추론해 검사를 통과한다
힌트

매개변수 n에 타입 표기가 없을 때 검사기와 Node가 각각 어떻게 동작하는지 비교해 보세요.

정답

정답: ①

검사 결과 오류 1개
main.ts(1,15): error TS7006: Parameter 'n' implicitly has an 'any' type.
실행 결과
5

"strict": true가 켜는 noImplicitAny는 타입을 안 적은 매개변수 n을 TS7006으로 막습니다. 하지만 옵션은 검사만 바꿀 뿐 Node 실행 결과(5)는 바꾸지 않습니다(타입이 실행 때 남는다는 생각).

다시 볼 레슨: strict가 켜는 것들

문제 8

(strict가 켜는 것들) tsconfig.json에 noUncheckedIndexedAccess⁠를 true로 추가로 켰을 때 달라지는 점은 무엇일까요?

  • ① 배열 const a = ["커피", "라테"]⁠에서 꺼낸 a[2]의 지금 타입이 string에서 string | undefined로 바뀌어, 확인 없이 a[2].length를 읽으면 TS18048로 막힌다
  • ② 배열에 push를 부를 수 없게 된다
  • ③ Node가 배열 범위를 벗어날 때 실행을 자동으로 멈춘다
힌트

배열 인덱스로 꺼낸 값의 타입에 무엇이 추가되는지 생각해 보세요.

정답

정답: ①

noUncheckedIndexedAccess⁠를 켜면 배열이나 인덱스 시그니처에서 인덱스로 꺼낸 값의 타입에 | undefined가 더해져, undefined 거르기 없이 쓰는 코드를 TS18048로 잡아 줍니다.

다시 볼 레슨: strict가 켜는 것들

문제 9

(복습 · interface를 지키는 클래스) 다음 코드를 타입 검사하면 어떻게 될까요?

interface Priced {
  calc(qty: number): number;
}

class Menu implements Priced {
  calc(qty) {
    return qty * 4500;
  }
}
  • ① Priced 인터페이스에 qty: number가 적혀 있으므로 통과한다
  • ② 6번 줄 calc(qty)에서 매개변수 타입을 안 적어 TS7006으로 막힌다
  • ③ Menu에 생성자가 없어서 막힌다
힌트

implements가 클래스 메서드의 매개변수 타입까지 추론해 주는지 떠올려 보세요.

정답

정답: ②

검사 결과 오류 1개
main.ts(6,8): error TS7006: Parameter 'qty' implicitly has an 'any' type.

implements는 클래스가 인터페이스의 모양을 지켰는지 대조만 할 뿐, 메서드 매개변수 타입을 인터페이스에서 가져와 추론해 주지 않습니다(implements가 매개변수 타입을 추론한다는 생각).

다시 볼 레슨: interface를 지키는 클래스

문제 10

(복습 · Promise와 async의 타입) 다음 코드에서 5번 줄이 타입 검사에서 막히는 까닭은 무엇일까요?

let note: string | null = '쿠폰';

if (note !== null) {
  setTimeout(() => {
    console.log(note.length);
  }, 10);
  note = null;
}
  • ① let으로 선언한 변수는 if 조건문 안에서도 처음부터 좁혀지지 않고 선언 타입 그대로 남기 때문이다
  • ② 콜백은 언제 불릴지 몰라서, 콜백을 만든 뒤 다시 대입되는 let 이름은 콜백 안에서 선언 타입으로 돌아가기 때문이다
  • ③ 검사기가 10ms 뒤 실행 시점을 미리 따라가 보고 그때 note의 값이 이미 null로 바뀐 것을 확인했기 때문이다
힌트

setTimeout에 넘긴 콜백이 언제 실행되는지 생각해 보세요.

힌트 둘

콜백을 만든 4번 줄 뒤(7번 줄)에 note를 다시 대입하는 줄이 있는지 함께 살펴보세요.

정답

정답: ② — 콜백을 만든 뒤 다시 대입되는 let 이름은 콜백 안에서 좁힘이 풀린다

검사 결과 오류 1개
main.ts(5,17): error TS18047: 'note' is possibly 'null'.

콜백은 언제 불릴지 몰라서, 콜백을 만든 뒤 7번 줄에서 다시 대입되는 note는 콜백 안에서 좁힘을 풀고 선언 타입 string | null로 봅니다(검사 규칙 5). 7번 줄에 글자를 넣어도 같습니다. const saved = note;에 옮겨 담으면 해결됩니다.

  • ① let 변수도 if (note !== null) 바로 아래에서는 string으로 좁혀집니다. 콜백 안에서 풀릴 뿐입니다.
  • ③ 검사기는 타이머 시간(10ms)이나 실행 순서를 따라가지 않고, 콜백 뒤에 재대입 줄이 있는지만 봅니다(검사기가 실행을 따라간다는 생각).
  • 흔한 오답 하나는 3번 줄의 좁힘이 콜백 안까지 그대로 유지된다고 보는 것입니다(좁힘이 끝까지 간다는 생각).

다시 볼 레슨: Promise와 async의 타입

문제 11 (만들어 보기)

(DOM 요소의 타입) #coupon 요소를 찾아 instanceof HTMLInputElement⁠로 좁힌 뒤, 입력 칸의 .value 문자열을 출력하는 브라우저 코드를 검사 오류 0으로 완성해 보세요.

(가) 뼈대 채우기 — ___ 자리에 알맞은 클래스 이름과 속성을 채워 보세요.

// const el = document
//   .querySelector('#coupon');
// if (
//   el instanceof
//   ___
// ) {
//   console.log(el.___);
// }

(나) 백지에서 짜기 — #memo 요소를 찾아 HTMLInputElement로 좁힌 뒤 el.value를 출력하는 코드를 작성해 보세요(검사 오류 0).

힌트

<input> 태그에 대응하는 DOM 클래스 이름은 HTMLInputElement입니다.

정답
const el = document
  .querySelector('#memo');

if (
  el instanceof
  HTMLInputElement
) {
  console.log(el.value);
}
검사 결과 오류 없음

el instanceof HTMLInputElement⁠로 좁히면 null이 걸러지고 el.value가 string으로 정해져 검사 오류 0으로 통과합니다.

다시 볼 레슨: DOM 요소의 타입

문제 12 (만들어 보기)

(strict가 켜는 것들) 문자열 배열 words: string[]과 인덱스 idx: number를 받아, 그 칸의 문자열이 있으면 글자 수(length)를 돌려주고 없으면(undefined) 0을 돌려주는 함수 lenAt을 작성해 보세요(noUncheckedIndexedAccess⁠를 켜도 통과하도록 undefined를 거릅니다).

(가) 뼈대 채우기 — ___ 자리에 알맞은 타입과 조건을 채워 보세요.

// function lenAt(
//   words: ___,
//   idx: ___,
// ): number {
//   const w = words[idx];
//   if (w !== ___) {
//     return w.length;
//   }
//   return 0;
// }

(나) 백지에서 짜기 — lenAt(["커피", "녹차"], 1)⁠과 lenAt(["커피", "녹차"], 5)⁠의 결과를 차례로 출력해 보세요(2와 0이 찍혀야 합니다).

힌트

인덱스로 꺼낸 값 w가 undefined가 아닐 때만 w.length를 돌려줍니다.

정답
function lenAt(
  words: string[],
  idx: number,
): number {
  const w = words[idx];
  if (w !== undefined) {
    return w.length;
  }
  return 0;
}

const list = ['커피', '녹차'];
console.log(lenAt(list, 1));
console.log(lenAt(list, 5));
실행 결과
2
0

if (w !== undefined)로 먼저 확인하면 noUncheckedIndexedAccess⁠가 켜진 환경에서도 검사 오류 0이고, 범위를 벗어난 인덱스 5에서도 실행 중 TypeError 없이 안전하게 0을 돌려줍니다.

다시 볼 레슨: strict가 켜는 것들

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

타입스크립트 목차로