본문 바로가기

기본 타입

number와 bigint, string, boolean, 배열과 튜플, 열거형, any를 예제로 익히고, 값이 없거나 끝나지 않는 흐름을 나타내는 void와 null, undefined, never의 자리를 구분합니다.

타입은 어떤 값이 들어올 수 있고, 그 값으로 어떤 연산을 할 수 있는지를 정합니다. 문자열에는 toUpperCase를 쓸 수 있지만 숫자에는 쓸 수 없고, 숫자 목록과 좌표 한 쌍은 둘 다 배열처럼 보여도 지켜야 할 규칙이 다릅니다. 타입스크립트의 기본 타입은 이런 값의 종류를 나타내는 가장 작은 단위입니다.

이 글은 숫자와 문자열, 불리언 같은 원시 타입에서 시작해 배열과 튜플, 열거형, any를 차례로 보고, 값이 없음을 나타내는 타입은 다음 글로 이어질 만큼만 짧게 소개합니다. 아래 그림은 이 타입들을 값의 역할에 따라 나눈 것으로, 상속 관계나 배우는 순서가 아니라 타입을 고르는 관점입니다.

원시 값, 배열과 튜플, 선택지, 결측, 함수 흐름, 불확실성을 다른 관점으로 구분합니다. 이 분류는 상속 관계나 실행 순서가 아닙니다.

TYPE / RUNTIME / CONTRACT

기본 타입은 값의 역할로 분류한다

한 값의 종류, 모음의 구조, 선택지, 결측과 함수 흐름은 서로 다른 질문입니다. 아래 분류는 상속 관계나 실행 순서가 아니라 타입을 고르는 관점입니다.

VALUE / PRIMITIVE

숫자 · 문자열 · 참과 거짓

number는 정수와 부동소수점을 함께 다룹니다. string은 텍스트, boolean은 true/false입니다. bigint는 별도의 원시 타입이며 이 절의 코드에는 등장하지 않습니다.

COLLECTION / POSITION

배열의 반복과 튜플의 위치 계약

number[]와 Array<number>는 같은 요소 계약입니다. [string,number]는 위치마다 타입을 정합니다. 가변 튜플의 push를 막으려면 readonly 튜플을 고려합니다.

CHOICE / RUNTIME

선택지 이름과 런타임 값을 구분

Direction 숫자 enum은 Up=0과 역방향 매핑을 보여줍니다. 문자열 enum에는 같은 역방향 매핑이 없습니다. literal union은 다음 절에서 다룰 정적 선택지입니다.

ABSENCE / STRICT NULL CHECKS

없을 수 있는 값을 명시한다

strictNullChecks=true에서 string|null, number|undefined처럼 결측을 표현합니다. null과 undefined를 같은 의미로 처리할지는 실제 데이터 계약에서 정합니다.

FUNCTION / CONTROL FLOW

void는 결과 계약, never는 정상 반환 불가

본문 warnUser는 정상 종료하고 결과는 void입니다. 예외 또는 무한 루프 함수는 never입니다. 모든 유니온 경우를 제거한 나머지에도 never가 쓰입니다.

CHECKING / EXTERNAL VALUE

any 허용과 unknown 검증을 구분

본문 any 값은 마지막에 false여서 toFixed에서 실패합니다. unknown은 다음 절에서 사용 전 좁히기를 요구합니다. 타입 이름만으로 실제 값의 검증이 끝나지 않습니다.

원시 값, 배열과 튜플, 선택지, 결측, 함수 흐름, 불확실성을 다른 관점으로 구분합니다. 이 분류는 상속 관계나 실행 순서가 아닙니다.

예제는 1장에서 만든 ts-first 프로젝트에 basic-types.ts라는 새 파일을 만들고 차례로 적어 확인합니다. tsconfig.json의 include를 ["basic-types.ts"]로 바꾸면 이 파일만 검사하며, tsc -p .으로 출력한 뒤 node dist/basic-types.js로 실행합니다. 블록마다 변수 이름이 겹치지 않게 지었으므로 한 파일에 이어 붙여도 됩니다.

숫자와 문자열, 불리언

타입스크립트의 number는 자바스크립트의 숫자와 같아서, 정수와 소수를 따로 나누지 않고 모두 64비트 부동소수점 수로 나타냅니다. 이 형식이 수를 저장하는 방식과 정확히 나타낼 수 있는 정수의 범위는 웹 기초 교재의 변수, 데이터 타입, 연산자에서 다룹니다. 10진수뿐 아니라 16진수와 2진수, 8진수 리터럴도 모두 number입니다.

const decimalValue: number = 6;
const hexValue: number = 0xf00d;
const binaryValue: number = 0b1010;
const octalValue: number = 0o744;
const floatValue: number = 10.5;

console.log(decimalValue, hexValue, binaryValue, octalValue, floatValue);
6 61453 10 484 10.5

console.log는 여러 값을 공백으로 이어 출력하고, 진법이 다른 리터럴은 모두 10진수로 바뀌어 보입니다. 0xf00d는 15×4096+13이어서 61453이고, 0b1010은 10, 0o744는 7×64+4×8+4이어서 484입니다.

부동소수점 수는 2의 53제곱보다 큰 정수를 모두 정확히 나타내지는 못하며, 2의 53제곱에 1을 더한 9007199254740993부터 표현할 수 없는 정수가 생깁니다. 그보다 큰 정수를 정확히 계산해야 하면 숫자 뒤에 n을 붙이는 bigint 타입을 씁니다.

const unsafeNumber: number = 9007199254740993;
const exactBig: bigint = 9007199254740993n;

console.log(unsafeNumber);
console.log(exactBig + 1n);
9007199254740992
9007199254740994n

number에 넣은 9007199254740993은 표현할 수 있는 가까운 값인 9007199254740992로 바뀌어 저장되지만, bigint는 값을 정확히 유지합니다. bigint와 number는 섞어 계산할 수 없어서 exactBig + 1은 Operator '+' cannot be applied to types 'bigint' and '1'.로 진단되므로, 1n처럼 같은 타입끼리 계산합니다. 진단에 number 대신 '1'이 보이는 것은 컴파일러가 숫자 1을 그 값 하나만 뜻하는 타입으로 보여 주기 때문입니다. bigint 리터럴은 target이 ES2020 이상일 때 쓸 수 있고, 지금 프로젝트의 es2022는 이 조건을 만족합니다.

string은 텍스트를 나타냅니다. 작은따옴표와 큰따옴표, 백틱(backtick)으로 만들 수 있고, 백틱으로 만든 템플릿 리터럴(template literal)은 여러 줄을 그대로 담고 ${} 안에 표현식을 넣을 수 있습니다.

const courseName: string = "TypeScript for Beginners";
const instructor: string = 'Kim Coding';
const introduction: string = `Hello, my name is ${instructor}.
This course is about ${courseName}.`;

console.log(introduction);
Hello, my name is Kim Coding.
This course is about TypeScript for Beginners.

boolean은 true와 false 두 값만 가지며, 조건문의 분기를 정할 때 주로 씁니다. 이 예제는 isCompleted가 거짓이므로 else 쪽의 문장을 출력합니다.

const isCompleted: boolean = false;

if (isCompleted) {
  console.log("작업이 완료되었습니다.");
} else {
  console.log("작업이 진행 중입니다.");
}
작업이 진행 중입니다.

세 타입 모두 다른 종류의 값을 넣으면 1장에서 본 TS2322가 나옵니다. 예를 들어 const notANumber: number = "hello";는 Type 'string' is not assignable to type 'number'.를, const isValid: boolean = 1;은 Type 'number' is not assignable to type 'boolean'.을 보고합니다. 타입 이름은 소문자 number, string, boolean으로 쓰며, 대문자로 시작하는 Number, String, Boolean은 래퍼 객체를 가리키는 다른 타입이므로 거의 쓸 일이 없습니다.

배열

배열(array)은 같은 종류의 값을 순서대로 여러 개 담습니다. 요소의 타입 뒤에 []를 붙여 number[]처럼 쓰거나, 제네릭 문법으로 Array<number>처럼 쓰며 두 표기는 같은 타입입니다. 보통은 더 짧은 number[]를 많이 쓰고, 제네릭은 제네릭 함수에서 자세히 다룹니다.

const scores: number[] = [1, 2, 3, 4, 5];
const fruits: string[] = ["apple", "banana", "cherry"];
const moreScores: Array<number> = [10, 20, 30];

console.log(scores[0], fruits.length, moreScores[1]);
1 3 20

scores는 숫자만 담는 배열이므로, scores.push("six");처럼 문자열을 넣으려 하면 Argument of type 'string' is not assignable to parameter of type 'number'.라는 TS2345가 나옵니다. 인덱스는 0부터 시작하므로 scores[0]은 첫 요소 1이고, moreScores[1]은 두 번째 요소 20입니다.

다만 number[]는 요소의 종류만 정할 뿐, 배열의 길이나 특정 위치에 값이 있는지는 보장하지 않습니다. 없는 위치를 읽으면 자바스크립트는 오류 대신 undefined를 돌려줍니다.

const prices: number[] = [1200, 1800];
const firstPrice = prices[0];

if (firstPrice !== undefined) {
  console.log(firstPrice.toFixed(0));
}
console.log(prices[2]);
1200
undefined

기본 설정에서 prices[0]의 타입은 그냥 number여서, 확인하지 않고 toFixed를 불러도 검사를 통과합니다. tsconfig.json에 noUncheckedIndexedAccess를 켜면 인덱스로 읽은 값의 타입이 number | undefined가 되어, 위처럼 undefined가 아닌지 확인한 뒤에만 숫자의 메서드를 쓸 수 있습니다. 이 옵션은 strict에 포함되지 않으므로 따로 켜야 하며, 자세한 내용은 공식 noUncheckedIndexedAccess 설명에 있습니다.

튜플

튜플(tuple)은 요소의 개수와 위치마다의 타입을 미리 정한 배열입니다. [string, number]는 첫 요소가 문자열이고 두 번째 요소가 숫자인 두 칸짜리 배열이라는 뜻이어서, 이름과 나이처럼 위치에 따라 의미가 정해진 값에 씁니다.

const userInfo: [string, number] = ["John Doe", 30];
const rgbColor: [number, number, number] = [255, 0, 0];

console.log(userInfo[0], userInfo[1], rgbColor.length);
John Doe 30 3

순서를 바꾸거나 요소를 빠뜨리면 진단이 나옵니다. 다음 두 줄을 추가하면, 첫 줄은 두 위치에서 각각 타입이 맞지 않고, 둘째 줄은 요소가 하나 모자랍니다.

const swappedInfo: [string, number] = [30, "John Doe"];
const missingInfo: [string, number] = ["Jane Doe"];
error TS2322: Type 'number' is not assignable to type 'string'.
error TS2322: Type 'string' is not assignable to type 'number'.
error TS2322: Type '[string]' is not assignable to type '[string, number]'.
  Source has 1 element(s) but target requires 2.

튜플의 길이는 타입 검사에서만 지켜진다는 점을 알아 두어야 합니다. userInfo.push(40)은 요소 타입 string | number에 맞으므로 통과해서 실행 중의 길이가 3이 되지만, 타입은 여전히 두 칸짜리라 userInfo[2]를 읽으면 Tuple type '[string, number]' of length '2' has no element at index '2'.라는 TS2493이 나옵니다. userInfo.push(true)는 불리언이 string | number에 맞지 않아 TS2345로 진단됩니다.

길이가 바뀌면 안 되는 튜플은 앞에 readonly를 붙여 readonly [number, number]처럼 선언합니다. 그러면 push 같은 변경 메서드가 타입에서 사라지고, 그 타입의 변수 point에 point[0] = 10처럼 요소를 바꾸는 대입도 진단됩니다. readonly는 타입 검사에서만 쓰기를 막을 뿐, 실행 중의 배열을 얼려 두는 기능은 아닙니다.

열거형

열거형(enum)은 서로 관련된 상수에 이름을 붙여 하나로 묶습니다. 값을 정하지 않으면 첫 멤버가 0이 되고 1씩 커지며, 숫자 열거형은 값에서 이름을 거꾸로 찾는 역방향 매핑(reverse mapping)도 만듭니다.

enum Direction {
  Up,
  Down,
  Left,
  Right,
}

enum StatusCode {
  Success = 200,
  NotFound = 404,
}

enum HttpStatus {
  OK = "OK",
  Created = "CREATED",
}

console.log(Direction.Up, Direction.Left, Direction[0]);
console.log(StatusCode.Success, StatusCode.NotFound);
console.log(HttpStatus.OK, HttpStatus.Created);
0 2 Up
200 404
OK CREATED

StatusCode처럼 멤버마다 숫자를 정할 수도 있고, HttpStatus처럼 문자열을 정할 수도 있습니다. 문자열 열거형은 멤버마다 값을 반드시 적어야 하며, 역방향 매핑을 만들지 않습니다.

열거형은 다른 타입 표기와 달리, 타입을 지운 뒤에도 실행되는 객체를 남깁니다. Direction을 컴파일하면 다음과 같은 자바스크립트가 만들어지며, Direction[Direction["Up"] = 0] = "Up"이 이름에서 값으로, 값에서 이름으로 가는 두 방향의 속성을 한 번에 만듭니다.

var Direction;
(function (Direction) {
    Direction[Direction["Up"] = 0] = "Up";
    Direction[Direction["Down"] = 1] = "Down";
    Direction[Direction["Left"] = 2] = "Left";
    Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));

이렇게 새 코드를 만들어야 하는 문법이어서, 1장에서 본 Node.js의 타입 제거 실행은 열거형이 있는 파일을 ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX 오류로 거부합니다. 타입을 지우는 것만으로 실행할 수 있는 문법만 허용하려면 erasableSyntaxOnly 옵션을 켜며, 이때 tsc는 열거형에 This syntax is not allowed when 'erasableSyntaxOnly' is enabled.를 보고합니다. 그런 프로젝트에서는 열거형 대신 유니온과 인터섹션 타입에서 다루는 문자열 리터럴의 유니온을 씁니다.

더 깊이: 열거형 타입이 걸어 온 길과 const enum

처음의 열거형은 같은 타입을 가진 숫자 상수의 묶음에 가까웠고, TypeScript 2.0에서 멤버마다 자기 타입을 갖는 열거형 리터럴 타입(enum literal type)이 들어오면서 열거형이 멤버 타입의 유니온이 되었습니다. 다만 Math.random()처럼 계산해야 값을 아는 멤버가 있으면 예전 방식으로 돌아갔는데, TypeScript 5.0부터는 그런 멤버에도 고유한 타입을 만들어 모든 열거형을 유니온 열거형으로 다룹니다. 같은 5.0에서 멤버의 값이 아닌 리터럴을 열거형 타입에 넣는 것도 오류가 되었습니다. 예를 들어 멤버 값이 0, 2, 4뿐인 열거형 타입의 변수에 1을 넣으면, 5.0 이전에는 통과했지만 지금은 진단됩니다.

열거형 앞에 const를 붙인 const enum은 출력에 객체를 남기지 않고, 멤버를 쓴 자리에 값을 직접 써 넣습니다(inline). 객체를 만들고 속성을 읽는 비용은 없어지지만, 공식 Handbook은 이 기능의 함정을 따로 정리해 둡니다. 함정은 주로 선언 파일(.d.ts)에 든 const enum에서 생깁니다. 값을 써 넣으려면 선언 파일의 정보가 필요하므로 파일을 하나씩 변환하는 isolatedModules와 함께 쓸 수 없고, 라이브러리의 const enum은 컴파일할 때 설치된 버전의 값이 코드에 박혀 실행할 때의 다른 버전과 어긋날 수 있습니다. 그래서 Handbook은 const enum을 아예 쓰지 않거나, 공개하는 라이브러리라면 preserveConstEnums로 일반 열거형과 같은 출력을 남긴 뒤 빌드 단계에서 선언 파일의 const를 지워, 앰비언트 const enum을 배포하지 않는 방법을 권합니다. 타입스크립트 프로젝트 자신도 뒤의 방법을 씁니다.

Handbook의 열거형 설명은 요즘의 타입스크립트에서는 as const를 붙인 객체로 열거형을 대신할 수 있다고도 적습니다. 그 이유로 드는 것은 코드가 지금의 자바스크립트에 맞춰진 상태로 남는다는 점이며, 나중에 자바스크립트에 열거형이 들어오면 그때 그 문법으로 옮기면 된다고 덧붙입니다. 객체는 표준 자바스크립트이고 as const는 지우기만 하면 되는 표기이므로, 앞에서 본 타입 제거 실행이나 erasableSyntaxOnly와도 충돌하지 않습니다.

검사를 끄는 any

any는 타입 검사를 끄는 타입입니다. 어떤 값이든 넣을 수 있고, 그 값의 어떤 속성이나 메서드를 써도 컴파일러가 확인하지 않습니다. 기존 자바스크립트와 연결하는 동안 잠시 쓸 수는 있지만, 타입스크립트를 쓰는 이점을 그 자리에서 포기하는 것과 같습니다.

any-demo.ts
let flexible: any = 4;
flexible = "hello";
flexible = false;

console.log(typeof flexible);
flexible.toFixed(2);
console.log("이 줄은 실행되지 않습니다.");
boolean

이 파일은 검사를 통과하지만, 실행하면 boolean을 출력한 뒤 TypeError: flexible.toFixed is not a function으로 시작하는 오류를 내고 멈춥니다. 마지막에 넣은 값이 false여서 toFixed라는 메서드가 없기 때문입니다. 실행 중에 멈추는 예제이므로 basic-types.ts가 아닌 별도의 파일에 두고 include에 더해 확인하며, 어떤 값이 올지 모를 때는 다음 글의 unknown과 검증을 먼저 고려합니다.

값이 없거나 끝나지 않는 흐름

함수가 돌려줄 값이 없을 때는 반환 타입을 void로 씁니다. null과 undefined는 각각 같은 이름의 타입을 가지며, TypeScript 6.0에서 기본으로 켜지는 strict 설정에는 strictNullChecks가 포함되어 있어 두 값을 다른 타입에 함부로 넣지 못하게 합니다.

function logNotice(message: string): void {
  console.log(message);
}

const noValue: undefined = undefined;
const emptyValue: null = null;
logNotice(`${noValue} ${emptyValue}`);
undefined null

이 설정에서 const missingCount: number = null;은 Type 'null' is not assignable to type 'number'.로 진단됩니다. 값이 없을 수 있는 자리라면 number | null처럼 그 가능성을 타입에 직접 적어야 하며, 이 할당 관계는 다음 글에서 자세히 봅니다.

never는 절대 생길 수 없는 값의 타입입니다. 항상 예외를 던지는 함수의 반환 타입이나, 모든 경우를 처리하고 남은 경우가 없다는 표시에 씁니다.

function describeWidget(widget: string | number): string {
  if (typeof widget === "string") {
    return `문자열 위젯 ${widget.toUpperCase()}`;
  } else if (typeof widget === "number") {
    return `숫자 위젯 ${widget.toFixed(2)}`;
  }
  const unreachable: never = widget;
  return unreachable;
}

console.log(describeWidget("box"));
console.log(describeWidget(3));
문자열 위젯 BOX
숫자 위젯 3.00

두 if를 지나면 widget에 남는 타입이 없으므로 never인 변수에 넣을 수 있습니다. 나중에 매개변수를 string | number | boolean으로 넓히고 분기를 추가하지 않으면, 그 줄에서 Type 'boolean' is not assignable to type 'never'.가 나와 처리하지 않은 경우를 알려 줍니다.

불확실한 외부 값에는 unknown과 검증을 사용합니다. 알려진 값은 형태·순서·결측·함수 흐름에 맞는 계약으로 표현하며 void와 never의 의미를 구분합니다.

TYPE / RUNTIME / CONTRACT

값의 형태와 불확실성에서 타입을 선택한다

외부에서 들어온 값인지 먼저 확인하고, 이미 파악한 값은 반복·위치·선택지·결측·함수 결과의 의미에 맞게 표현합니다. 최소 타입은 의미를 충분히 담는 계약을 뜻합니다.

값의 계약에 따른 타입 선택같은3노드2간선 선택분기이다. 값의계약을모르면unknown과런타임검증, 알면값의형태·순서·결측·함수흐름에맞는타입을고른다. 두출력은차례로실행하는단계가아니다.값의 계약을알고 있는가?아니오예unknown · 검증아직 모르는 외부 값알려진 값의 형태순서 · 결측 · 반환 의미
같은 타입 선택 분기 · 모바일같은3노드2간선 선택분기이다. 값의계약을모르면unknown과런타임검증, 알면값의형태·순서·결측·함수흐름에맞는타입을고른다. 두출력은차례로실행하는단계가아니다.값의 계약을아는가?아니오예unknown · 검증아직 모르는 외부 값알려진 값의 형태순서 · 결측 · 반환 의미

UNKNOWN / FIRST

외부 입력은 사용 전에 확인한다

unknown은 값을 받을 수 있지만 바로 속성을 읽거나 호출할 수 없습니다. 실제 타입과 필드를 확인한 뒤 그 범위에서 사용합니다.

COLLECTION / CHOICE

반복은 배열, 위치는 튜플

순서마다 의미가 다르면 tuple, 같은 요소를 반복하면 array를 고릅니다. 닫힌 상태 후보에는 literal union이나 enum의 런타임 요구를 비교합니다.

ABSENCE / FUNCTION

결측 · 반환값 · 정상 반환 불가를 구분

null/undefined는 없는 값, void는 함수 결과의 계약, never는 정상적으로 돌아오지 않는 흐름입니다. 결측과 반환 불가능성을 같은 종류로 묶지 않습니다.

LESSON / FOLLOWING TOPICS

관찰한 값이 타입 이름보다 먼저다

본문 number/string/boolean/array/tuple/enum 예제가 출발점입니다. unknown과 literal union의 상세 사용은 다음 절에서 다룹니다. any는 검사를 완화하는 선택이므로 적용 범위를 좁힙니다.

불확실한 외부 값에는 unknown과 검증을 사용합니다. 알려진 값은 형태·순서·결측·함수 흐름에 맞는 계약으로 표현하며 void와 never의 의미를 구분합니다.

실제로 타입을 고를 때는 위 그림처럼 값이 어디에서 오는지부터 봅니다. 외부에서 들어와 모양을 모르는 값은 unknown으로 받아 검증하고, 이미 아는 값은 한 값인지 모음인지, 위치에 의미가 있는지, 비어 있을 수 있는지, 함수가 값을 돌려주는지에 맞추어 가장 작은 타입을 고릅니다.

정리

  • number는 정수와 소수를 모두 담는 64비트 부동소수점 수이고, 2의 53제곱보다 큰 정수를 정확히 다루려면 bigint를 씁니다.
  • 타입 이름은 소문자 number, string, boolean으로 쓰며, 다른 종류의 값을 넣으면 TS2322가 나옵니다.
  • 배열 타입은 요소의 종류만 정하고 길이와 위치의 존재는 보장하지 않으며, noUncheckedIndexedAccess를 켜면 인덱스로 읽은 값에 undefined가 포함됩니다.
  • 튜플은 위치마다 타입을 정하지만 길이는 타입 검사에서만 지켜지므로, 바뀌면 안 되는 튜플에는 readonly를 붙입니다.
  • 열거형은 실행되는 객체를 만들어 내므로 Node.js의 타입 제거 실행이나 erasableSyntaxOnly와 함께 쓸 수 없습니다.
  • any는 검사를 끄므로 실행 중의 오류를 막지 못하고, void와 null, undefined, never는 값이 없거나 끝나지 않는 흐름을 나타냅니다.

연습 문제

1. 짧아진 배열

위의 prices를 [900]으로 바꾸면 두 출력은 무엇이 되나요? 이어서 console.log(prices[2].toFixed(0));를 추가하면, noUncheckedIndexedAccess를 껐을 때와 켰을 때 각각 어떻게 되나요?

해설 보기

첫 출력은 900, 둘째 출력은 undefined입니다. 옵션을 끈 기본 설정에서는 prices[2]의 타입이 number라 검사를 통과하지만, 실행하면 undefined에서 toFixed를 읽으려다 TypeError: Cannot read properties of undefined (reading 'toFixed')로 멈춥니다. 옵션을 켜면 타입이 number | undefined가 되어 Object is possibly 'undefined'.라는 TS2532가 나오므로, 실행 전에 이 실수를 알 수 있습니다.

2. 배열과 튜플 고르기

개수가 정해지지 않은 상품 가격 목록과, 바뀌지 않을 화면 좌표 (40, 80)을 각각 선언해 보세요. 좌표는 첫 칸이 x, 둘째 칸이 y이고, 세 번째 값을 추가하지 못하게 해야 합니다.

해설 보기
const productPrices: number[] = [1200, 1800];
const screenPoint: readonly [number, number] = [40, 80];
console.log(screenPoint[0], screenPoint[1]);

출력은 40 80입니다. 가격 목록은 개수가 바뀔 수 있으므로 배열을, 좌표는 두 위치의 의미가 서로 다르므로 튜플을 고릅니다. readonly를 붙였으므로 screenPoint.push(100)은 Property 'push' does not exist on type 'readonly [number, number]'.로 진단되며, const는 변수에 다른 배열을 다시 넣는 것만 막는다는 점에서 readonly와 다릅니다.

3. 역방향 매핑

위의 세 열거형에서 Direction[Direction.Right]와 StatusCode[404]의 값은 무엇인가요? HttpStatus에서 값 "CREATED"로 멤버의 이름을 같은 방법으로 찾을 수 있나요?

해설 보기

Direction.Right는 3이고 Direction[3]은 역방향 매핑으로 "Right"입니다. StatusCode도 숫자 열거형이므로 StatusCode[404]는 "NotFound"입니다. 문자열 열거형은 역방향 매핑을 만들지 않아 HttpStatus 객체에는 OK와 Created 두 속성만 있으므로, 값으로 이름을 찾으려면 Object.keys로 속성을 훑는 코드를 따로 써야 합니다.

다음 글 특수 타입에서는 모양을 모르는 값을 안전하게 받는 unknown과 원시 값이 아닌 값을 나타내는 object, 그리고 void와 never, null, undefined의 할당 관계를 자세히 봅니다.