어제 오늘 내일

[TypeScript] 3. 기본 타입 총정리 본문

IT/TypeScript

[TypeScript] 3. 기본 타입 총정리

hi.anna 2026. 8. 9. 12:41

 

이 글의 목표: TypeScript의 기본 타입을 예제로 익히고, 특히 헷갈리는 any / unknown / never의 차이를 명확히 합니다.

원시 타입: string, number, boolean

가장 기본이 되는 세 가지입니다.

const name: string = "김철수";
const age: number = 30;         // 정수, 실수 구분 없이 모두 number
const isAdmin: boolean = true;

// 타입에 맞지 않는 값은 즉시 에러
// const wrong: number = "열살"; // ❌ 'string'을 'number'에 할당할 수 없음

참고로 number는 정수/실수를 구분하지 않고, 큰 정수를 위한 bigint, 심볼을 위한 symbol도 원시 타입에 포함됩니다.

배열과 튜플

// 배열: 같은 타입의 원소들. 두 가지 표기법 모두 동일하다
const scores: number[] = [90, 85, 100];
const names: Array<string> = ["a", "b"]; // 제네릭 표기, 결과는 동일

// 튜플: 길이와 각 위치의 타입이 고정된 배열
let point: [number, number] = [10, 20];
// point = [10, 20, 30]; // ❌ 원소 3개는 불가

// 튜플은 위치마다 의미가 다를 때 유용하다
const person: [string, number] = ["철수", 30]; // [이름, 나이]
const [pName, pAge] = person; // 구조 분해로 꺼내 쓴다

객체 타입

객체는 각 속성의 타입을 명시합니다.

// 인라인으로 객체 구조를 기술
const user: { id: number; name: string; email?: string } = {
  id: 1,
  name: "철수",
  // email은 ? 가 붙어 선택적(optional)이므로 없어도 된다
};

// 매번 이렇게 쓰면 번거롭다 → type 별칭으로 재사용 (5편에서 자세히)
type User = { id: number; name: string; email?: string };
const user2: User = { id: 2, name: "영희" };

리터럴 타입

특정 값 자체를 타입으로 쓸 수 있습니다.

// direction은 이 세 문자열 중 하나만 가질 수 있다
let direction: "up" | "down" | "left" | "right";
direction = "up";    // ✅
// direction = "top"; // ❌ 허용되지 않은 값

// 상수 열거처럼 활용하면 오타를 원천 차단할 수 있다
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
function request(method: HttpMethod, url: string) { /* ... */ }
request("GET", "/users");   // ✅
// request("GET ", "/users"); // ❌ 뒤에 공백이 있어 에러 → 오타 즉시 발견

null과 undefined

strict 모드에서는 이 둘이 별도 타입으로 취급되어, 실수로 값이 비는 상황을 막아줍니다.

let nickname: string | null = null; // "문자열이거나 null" 임을 명시
nickname = "철수"; // 이 시점 이후 nickname은 string으로 좁혀진다

// 함수 매개변수처럼 "값이 아직 정해지지 않은" 경우를 보자
function printLength(nickname: string | null) {
  // strict 모드에서는 아래가 에러다 (nickname이 null일 수 있으므로)
  // console.log(nickname.length); // ❌ 'nickname'이(가) 'null'일 수 있습니다.

  // 좁히기(narrowing)로 안전하게 처리 (7편에서 자세히)
  if (nickname !== null) {
    console.log(nickname.length); // ✅ 이 블록 안에서 nickname은 string
  }
}

참고: let nickname = "철수"처럼 문자열을 바로 대입한 직후에는, TypeScript가 흐름을 분석해 nickname을 string으로 좁혀줍니다. 그래서 그 지점에서는 .length 접근이 에러가 아닙니다. 에러는 위 함수 매개변수처럼 "그 값이 여전히 null일 수 있는" 경우에 발생합니다.

any: 타입 검사 포기 선언

any는 "이 값은 검사하지 말라"는 뜻입니다. 어떤 연산도 허용되어 사실상 JavaScript로 돌아갑니다.

let data: any = 10;
data = "문자열";      // OK
data = { x: 1 };      // OK
data.foo.bar.baz;     // OK (실행 시 터지지만 컴파일러는 침묵)

any는 타입 안전성을 통째로 버립니다. 꼭 필요한 경우가 아니면 피하세요.

unknown: 안전한 any

unknown도 "무엇이든 담을 수 있다"는 점은 같지만, 쓰기 전에 타입을 확인하도록 강제합니다.

let value: unknown = fetchSomething();

// value.length;      // ❌ unknown은 바로 사용할 수 없다
// value.toFixed(2);  // ❌

// 반드시 타입을 좁힌 뒤에 사용해야 한다
if (typeof value === "string") {
  console.log(value.length); // ✅ 여기서는 string으로 확정됨
}
if (typeof value === "number") {
  console.log(value.toFixed(2)); // ✅ 여기서는 number
}

외부 데이터(API 응답, JSON.parse 결과 등)처럼 타입을 신뢰할 수 없는 값에는 any 대신 unknown을 쓰세요.

never: 절대 일어나지 않는 값

never는 "값이 존재할 수 없음"을 나타냅니다.

// 항상 예외를 던지는 함수는 정상적으로 반환하지 못하므로 never
function fail(message: string): never {
  throw new Error(message);
}

// 무한 루프 함수도 never
function loop(): never {
  while (true) {}
}

never의 실전 활용은 "빠짐없이 처리했는지 검사"입니다.

type Shape = "circle" | "square";

function area(shape: Shape): number {
  if (shape === "circle") return 3.14;
  if (shape === "square") return 4;
  // 위에서 모든 경우를 처리했다면 여기 shape는 never 타입이 된다
  const exhaustive: never = shape;
  return exhaustive;
}
// 나중에 Shape에 "triangle"을 추가하면, 위 never 할당에서 에러가 나
// "처리 안 된 경우가 있다"고 알려준다. (13편에서 재등장)

정리

타입 의미
string number boolean 원시 타입
T[] / Array<T> 배열
[T, U] 튜플 (길이·순서 고정)
"a" | "b" 리터럴 유니온
any 검사 포기 (지양)
unknown 안전한 any (확인 후 사용)
never 값이 존재할 수 없음

any는 피하고 unknown을 쓰는 습관이 TypeScript를 제대로 쓰는 첫걸음입니다. 다음 글에서는 타입을 언제 직접 쓰고 언제 추론에 맡길지 다룹니다.

반응형
Comments