TypeScript(타입스크립트) 기본 타입

·2026년 1월 20일

type-script

목록 보기
2/9
post-thumbnail

TypeScript(타입스크립트) 설치 및 개요

타입스크립트 기본

기본타입 (Basic Types)

  • 타입들이 서로 부모-자식 관계를 이루며 계층을 형성
  • any, void, never, unknown 은 타입스크립트에서만 제공

원시타입

원시 타입(Primitive Type)은 동시에 한개의 값만 저장할 수 있는 타입

배열이나 객체 같은 비 원시타입들은 동시에 여러개의 값들을 저장할 수 있음 / 반면 원시타입은 숫자면 숫자, 문자열이면 문자열 딱 하나의 값만 저장할 수 있음

number 타입

let num1: number = 123;
let num2: number = -123;
let num3: number = 0.123;
let num4: number = -0.123;
let num5: number = Infinity;
let num6: number = -Infinity;
let num7: number = NaN;

이때 number 타입으로 정의한 변수에는 number 타입을 제외한 값을 할당할 수 없다.

number 타입의 값이 사용할 수 없는 toUpperCase 등의 메서드는 사용할 수 없다.

string 타입

let str1: string = "hello";
let str2: string = 'hello';
let str3: string = `hello`;
let str4: string = `hello ${str1}

boolean 타입

// boolean
let bool1 : boolean = true;
let bool2 : boolean = false;

boolean 타입은 참과 거짓만을 저장하는 타입입니다. true 또는 false만 이 타입에 해당됩니다.

null 타입 / undefined 타입

let null1: null = null;
let unde1: undefined = undefined;

null 값을 다른 타입의 변수에 할당하기

let numA = null;

위와 같이 아직 값이 미정인 변수에 임의로 null을 할당하는 경우가 있는데, 타입스크립트에서는 numA의 타입이 number라고 지정되어 있으면 에러가 난다.

let numA: number = null;  // ❌

하지만 만약 이렇게 쓰고 싶다면 추후 나오는 유니온 타입이나 tsconfig.json의 strcitNullChecks(엄격한 null 검사) 옵션을 false로 설정하면 된다.

{
  "compilerOptions": {
    "strictNullChecks": false,
  },
}

하지만 strictNullChecks은 strict 옵션의 하위 옵션이기때문에,

strict 옵션이 true이면 자동으로 true로 설정되며 반대로 strict 옵션이 false이면 자동으로 false로 설정된다. 만약 위 코드처럼 우리가 직접 설정하면 설정된 옵션에 따른다.

리터럴 타입

타입스크립트에는 string, number 처럼 범용적으로 많은 값을 포함하는 타입 뿐만 아니라 딱 하나의 값만 포함하는 타입도 존재한다. 따라서 다음과 같이 변수의 타입을 숫자 10으로 설정하는 것 또한 가능하다.

let numA: 10 = 10;
let strA: "hello" = "hello";
let boolA: true = true;
let boolB: false = false;

any 타입

타입스크립트에서만 제공되는 특별한 타입으로 타입 검사를 받지 않는 특수한 치트키 타입이다.

만약 number의 값을 할당받았다가 string을 할당받아야하는 케이스에서는 any라는 치트키를 쓸 수 있다.

let anyVar: any = 10;
anyVar = "hello";

anyVar = true;
anyVar = {};

anyVar.toUpperCase();
anyVar.toFixed();
anyVar.a;

이렇듯 any 타입은 어떠한 타입 검사도 받지 않기 때문에 아무 타입의 값이나 범용적으로 담아 사용할 수 있고 또 다양한 타입의 메서드도 마음대로 호출해서 사용해도 문제가 되지 않는다.
또 any 타입의 값은 어떤 타입으로 정의된 변수던 문제 없이 다 할당할 수 있다. 따라서 다음과 같이 number 타입의 변수 num에 any 타입의 값 anyVar를 할당해도 문제가 발생하지 않는다.

하지만 그렇기때문에 최대한 사용하면 안된다!

위에 코드에서는 런타임에러가 나지만 타입스크립트의 컴파일과정에서는 에러를 찾을 수 없기때문이다.

unknown 타입

unknown 타입은 any 타입과 비슷하지만 보다 안전한 타입이다. unknown 타입의 변수는 어떤 타입의 값이든 다 저장할 수 있다.

let unknownVar: unknown;

unknownVar = "";
unknownVar = 1;
unknownVar = () => {};

하지만 any와 다르게 unknown 타입의 값은 어떤 타입의 변수에도 저장할 수 없다. 또한 어떠한 연산에도 참여할 수 없다.

let num: number = 10;
(...)

let unknownVar: unknown;
unknownVar = "";
unknownVar = 1;
unknownVar = () => {};

num = unknownVar; // ❌
unknownVar * 2 // ❌

정리하자면 unknown 타입은 독특하게도 변수의 타입으로 정의되면 모든 값을 할당받을 수 있게 되지만,

반대로 unknown 타입의 값은 그 어떤 타입의 변수에도 할당할 수 없고, 모든 연산에 참가할 수 없게 된다. 쉽게 정리하면 오직 값을 저장하는 행위밖에 할 수 없게 된다.

만약 위와 같이 unknown 타입의 값을 number 타입의 값처럼 취급하고 곱셈 연산을 수행하게 하고 싶다면 다음과 같이 조건문을 이용해 이 값이 number 타입의 값임을 보장해줘야 한다.

if (typeof unknownVar === "number") {
	// 이 조건이 참이된다면 unknownVar는 number 타입으로 볼 수 있음
  unknownVar * 2;
}

따라서 특정 변수가 당장 어떤 값을 받게 될 지 모른다면 any 타입으로 정의하는 것 보단 unknown 타입을 이용하는게 훨씬 안전한 선택이다.

void 타입

아무런 값도 없음을 의미하는 타입

보통은 아무런 값도 반환하지 않는 함수의 반환값 타입을 정의할때 사용한다.

function func2(): void {
  console.log("hello");
}

변수의 타입으로도 void를 지정할 수 있지만, void 타입의 변수에는 undefined 이외의 다른 타입의 값은 담을 수 없다.

let a: void;
a = undefined;

never 타입

불가능을 의미하는 타입

함수가 어떠한 값도 반환할 수 없는 상황일 때 해당 함수의 반환값 타입을 정의할 때 사용된다.

function func3(): never {
  while (true) {}
}

함수 func3는 무한 루프를 돌기 때문에 아무런 값도 반환할 수 없다. 엄밀히 말하면 이 함수는 영원히 종료될 수 없기 때문에 뭔가를 반환한다는 것 자체가 '불가능' 하다.
이렇게 불가능 한 값의 타입을 정의할 때 never 타입을 사용한다.\

변수의 타입을 never로 정의하면 any를 포함해 그 어떠한 타입의 값도 이 변수에 담을 수 없게 된다.

let anyVar: any;

let a: never;
a = 1; // ❌
a = null; // ❌
a = undefined; // ❌
a = anyVar; // ❌



배열 타입

타입스크립트에서는 다음과 같이 배열 타입을 정의한다.

let numArr: number[] = [1, 2, 3]
let strArr: string[] = ["hello", "im", "winterlood"];
let boolArr: Array<boolean> = [true, false, true];

처럼 꺽쇠와 함께 타입을 작성하는 문법을 제네릭 이라고 한다.

number[] 와 Array 두 형식 모두 모양만 다를뿐 기능은 동일하다.

다양한 타입 요소를 갖는 배열 타입 정의

let multiArr: (number | string)[] = [1, "hello"];

소괄호바 | 를 이용해 배열 요소가 둘 중 하나의 타입에 해당하도록 타입을 정의한다.

number | string는 요소가 number이거나 string 타입이여야한다.

다차원 배열 타입 정의

let doubleArr : number[][] = [
  [1, 2, 3], 
  [4, 5],
];

다음과 같이 []를 연달아 작성해 다차원 배열 타입도 간단하게 정의 가능하다.

튜플

튜플 타입을 이용하면 고정된 길이를 갖는 배열 타입을 정의할 수 있다.

길이가 2로 고정된 number 타입 요소를 갖는 튜플 타입은 아래와 같이 정의한다.

let tup1: [number, number] = [1, 2];

다양한 타입을 갖는 튜플 타입도 정의할 수 있다.

let tup2: [number, string, boolean] = [1, "hello", true];

하지만 튜플은 결국 배열이고, 배열 메서드인 push나 pop을 이용해 고정된 길이를 무시하고 요소를 추가하거나 삭제할 수 있다.

따라서 튜플을 사용할때는 최대한 배열 메서드를 이용해 요소를 추가하거나 삭제하는 등의 연산을 할때에는 주의해야한다. 이럴때는 readonly를 함께 사용하면 수정 메서드 사용을 원천적으로 막을 수 있다.

let tup: readonly [number, string] = [1, "hello"];
tup.push(2); // ❌

객체

객체의 기본 타입 정의는 object 이다.

let user: object = {
  id: 1,
  name: "챙",
};

하지만 object는 단순 값이 객체임을 표현하는 것 외에는 아무런 정보도 제공하지 않기 때문에 내부의 user.id 처럼 접근하려는 순간 타입에러가 발생한다. 이렇게 객체의 키까지 타입으로 만들고 싶으면 객체 리터럴 타입을 사용해야한다.

객체 리터럴 타입

let user: {
  id: number;
  name: string;
} = {
  id: 1,
  name: "챙",
};

user.id;

객체 리터럴과 비슷한 문법으로 객체 타입을 정의한 타입을 객체 리터럴 타입이라고 부른다.

변수의 타입을 객체 리터럴 타입으로 정의하면 타입내에 정의되어 있는 프로퍼티(user.id)에도 접근할 수 있다.

따라서 객체의 타입을 정의할 때에는 앞으로 object 보다는 객체 리터럴 타입을 사용하는게 좋다.

여기서 알 수 있는 사실은 타입스크립트는 기존의 정적 타입 시스템을 따르는 언어인 C나 Java와는 달리 객체의 타입을 정의할 때 프로퍼티를 기준으로 객체의 구조를 정의하 듯이 타입을 정의한다는 점이다.
타입스크립트의 이런 특징을 구조적 타입 시스템이라고 부른다.

객체의 구조를 결정하는 것은 프로퍼티이고, 타입스크립트는 이 객체에 어떤 프로퍼티들이 있어야 하는지 정의하는 방식으로 객체의 타입을 정의한다.

interface Person {
  name: string;
  age: number;
}

interface Dog {
  name: string;
  age: number;
}

let man: Person = { name: "철수", age: 30 };
let myDog: Dog = { name: "바둑이", age: 3 };

// Person 자리에 Dog를 넣으려 하면?
man = myDog; // 성공! (에러 없음)

위의 코드를 보면 Person 이나 Dog와 같은 타입의 이름은 중요하지 않고 구조를 기준으로만 판단한다.

그래서 서로 다른 이름의 타입이라도, 내부 구조(프로퍼티)가 겹치면 서로 호환해서 사용할 수 있다.

특수한 프로퍼티 정의

선택적 프로퍼티(Optional Property)

있어도 되고 없어도 되는 상황에서 사용한다.

let user: {
  id?: number; // 선택적 프로퍼티가 된 id
  name: string;
} = {
  id: 1,
  name: "",
};

user = {
  name: "김개발",
};

id 프로퍼티 뒤에 붙은 물음표의 의미는 이 프로퍼티는 이제 생략 가능한 선택적(Optional) 프로퍼티라는 의미이다.

읽기전용 프로퍼티(Readonly Property)

프로퍼티의 이름 앞에 readonly 키워드를 붙이면 된다.

let user: {
  id?: number;
  readonly name: string; // name은 이제 Readonly 프로퍼티가 되었음
} = {
  id: 1,
  name: "챙",
};

user.name = "김개발"; // ❌

name 프로퍼티는 이제 읽기 전용 프로퍼티가 되었기 때문에 마지막 라인처럼 프로퍼티의 값을 수정하려고 하면 오류가 발생하게 된다. 이를 통해 의도치 않은 프로퍼티의 수정을 방지할 수 있다.

타입 별칭(Type Alias)

변수를 선언하듯 타입을 별도로 정의할 수 있다.

type User = {
  id: number;
  name: string;
  nickname: string;
  birth: string;
  bio: string;
  location: string;
};

변수처럼 동일한 스코프에 동일한 이름의 타입 별칭을 선언하는 건 불가하다. 하지만 스코프가 다르면 가능하다.

Interface vs Type Alias

둘 다 객체의 타입을 정의할 수 있다는 점은 비슷하다.

하지만 type은 객체뿐만 아니라 모든 타입(원시값, 유니온 등)에 별칭을 붙일 수 있는 반면, interface는 객체의 구조를 정의하는 데 특화되어 있다는 차이가 있다.

인덱스 시그니처 (Index Signature)

객체 타입을 유연하게 정의할 수 있도록 돕는 특수한 문법이다.

type CountryCodes = {
  [key: string]: string;
};

let countryCodes: CountryCodes = {
  Korea: "ko",
  UnitedState: "us",
  UnitedKingdom: "uk",
  // (... 약 100개의 국가)
  Brazil : 'bz'
};

[key : string] : string 은 인덱스 시그니쳐 문법으로 이 객체 타입에는 key가 string 타입이고 value가 string 타입인 모든 프로퍼티를 포함된다 라는 의미이다.

따라서 Korea : “ko”나 Brazil : “bz” 처럼 key와 value가 모두 string 타입인 이런 프로퍼티들 이 타입에 자동으로 모두 포함되고, 굳이 일일이 이런 타입들을 직접 명시할 필요가 없다.

type CountryNumberCodes = {
  [key: string]: number;
  Korea: string; // ❌
};

하지만 주의할 점은 인덱스 시그니처를 사용하면서 동시에 추가적인 프로퍼티를 정의할때에는 인덱스 시그니처의 value와 추가한 value의 타입이 호환되거나 일치해야한다.

열거형(Enum) 타입

여러개의 값을 나열하는 용도로 사용

// enum 타입
// 여러가지 값들에 각각 이름을 부여해 열거해두고 사용하는 타입

enum Role {
  ADMIN = 0, // 0 할당(자동)
  USER = 1,  // 1 할당(자동)
  GUEST = 2, // 2 할당(자동)
}

const user1 = {
  name: "이정환",
  role: Role.ADMIN, //관리자
};

const user2 = {
  name: "홍길동",
  role: Role.USER, // 회원
};

const user3 = {
  name: "아무개",
  role: Role.GUEST, // 게스트
};

user1.role 에는 0이 user2.role에는 1이 user3.role에는 2가 할당된다.
이렇듯 유저의 권한과 같은 여러개의 멤버를 갖는 값을 숫자로 표기할 때 enum을 이용해 보다 안전하고 직관적으로 관리할 수 있습니다.

참고로 다음과 같이 enum 멤버에 숫자 값을 직접 할당하지 않아도 0 부터 1씩 늘어나는 값으로 자동으로 할당된다. 추가로 문자열도 할당 가능하다.

enum Language {
  korean = "ko",
  english = "en",
}

Enum은 컴파일 결과 객체가 된다.

enum은 컴파일될 때 다른 타입들 처럼 사라지지 않고 자바스크립트 객체로 변환된다. 따라서 우리가 위에서 했던 것 처럼 값으로 사용할 수 있다.

var Role;
(function (Role) {
    Role[Role["ADMIN"] = 0] = "ADMIN";
    Role[Role["USER"] = 1] = "USER";
    Role[Role["GUEST"] = 2] = "GUEST";
})(Role || (Role = {}));
var Language;
(function (Language) {
    Language["korean"] = "ko";
    Language["english"] = "en";
    Language["japanese"] = "jp";
})(Language || (Language = {}));
const user1 = {}

  • 한 입 크기로 잘라먹는 타입스크립트 강의를 보고 참고했습니다.
profile
하고싶은거 짱많은 주니어 프론트엔드 개발자

0개의 댓글