본문 바로가기
└ TypeScript

한 입 크기로 잘라먹는 타입스크립트 복습용 정리(2) 기본 타입

by 디디 ( DD ) 2023. 7. 13.

 

 

기본 타입(내장 타입)?

: 타입스크립트가 자체적으로 제공하는 타입

 

타입 계층도

 

 

 

 

 

원시타입(Primitive Type)

 

: 하나의 값만 저장하는 타입.

 

- number 타입 : 양수, 음수, 소수, Infinity(양의 무한대), -Infinity(음의 무한대), NaN 포함.

- string 타입 : " "(큰 따옴표), ' '(작은 따옴표), ` `(백틱, 템플릿 리터럴)

- boolean 타입: true / false

- null 타입

- undefined

 

- 리터럴 타입: 딱 하나의 값만 포함하는 타입

 

// 리터럴 타입 예시

let numA: 10 = 10;

// 이렇게 설정하면 이제 numA에는 10 이외의 값을 저장할 수 없게 된다.

 

 

 

★ 타입 주석 (type annotation)

let 이름:string = 'kim';
let num1: number = 123;
이런 식으로 콜론과 함께 변수의 타입을 정의하는 방식 (기본 방식임)

(+) let 이름:string | number = 'kim'; (유니온 타입(union type) - |를 이용하여 다양한 타입이 들어올 수 있게 할 수도 있다.)


 

 

개발을 하다 보면 임시로 null 값을 사용하는 경우가 있는데, 이때 사용할 수 있는 옵션이 있다.

 

// strcitNullChecks(엄격한 null 검사) 옵션을 false로 설정

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

 

strictNullChecks 옵션strict 옵션의 하위 옵션이기 때문에, strict 옵션이 켜져 있으면 기본적으로 strictNullChecks 옵션도 켜지고, strict 옵션이 꺼져 있으면 strictNullChecks 옵션도 꺼진다. 

 

 

 

 

 

배열과 튜플

 

 

★ 배열의 경우에는
let numArr: number[] = [1, 2, 3];
이런 식으로 타입을 지정. 또는,
let numArr: Array<number> = [1, 2, 3];
이런 식으로도 쓸 수 있음. (제네릭 문법)

 

다양한 타입 요소를 갖는 배열을 만들려면,
let multiArr: (number | string)[] = [1, "hello"];
이런 식으로 배열 요소의 타입이 stirng이거나 number라고 정의할 수 있다. (유니온 타입)

[TIP] 변수에 마우스 커서를 올려보면, 타입 지정을 어떻게 해야 할지 힌트를 얻을 수 있다.

 

다차원 배열 타입을 정의하고 싶다면,
let doubleArr : number[][] = [
  [1, 2, 3],
  [4, 5],
];
이런 식으로 []를 연달아 작성하면 됨.
즉, 위 예시에서는 number 타입의 배열을 저장하는 배열을 의미.

 

 

>> 튜플 타입

     : 길이와 타입이 고정된 배열 (JS에는 없는 TS의 특수한 타입)

배열의 인덱스별로 정해진 타입이 들어가야 할 때 유용하게 사용할 수 있다.

 

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

type Member = [number, boolean]; 이런 식으로 쓰면(타입 별칭),
let john:Member = [20, true]; 무조건 첫째는 number, 둘째는 boolean 타입이 와야 한다. 

 

※ 튜플 타입도 결국 배열이므로 배열 메서드인 push나 pop등을 사용할 수 있다. 이 경우 길이가 달라지지만, 오류가 나지 않는다. 그러므로 튜플 타입에 배열 메서드를 쓸 때는 각별히 주의해야 한다.

 

 

 

 

 

객체

 

 

★ 객체의 경우,
let user: { id: number, name: string; } = {
  id: 1,
  name: "홍길동"
};
이런 식으로 각 프로퍼티의 타입을 지정해 주어야 한다. (객체 리터럴 타입)

→ 타입스크립트는 "구조적 타입 시스템(Property Based Type System)"을 가지고 있다. 위와 같이 구조를 기준으로 타입을 정의한다. (cf. C언어나 자바는 "명목적 타입 시스템" 즉, 이름을 기준으로 타입을 지정한다.)

 

선택적 프로퍼티라면?
let user: { id: number, name?: string; } = {
  id: 1,
  name: "홍길동"
};
이렇게 ?를 추가하면, 이 값은 옵션. 즉, 있어도 되고 없어도 된다.

 

읽기 전용 프로퍼티를 만들 수도 있다.
let config: { readonly apiKey: string; } = {
  apiKey: "MY API KEY",
}
config.apiKey = "lalala" // 오류 발생

 

 

 


 

 

 

타입 별칭

 

→ 타입 정의를 변수처럼 하게 해주는 문법.

타입 지정하는 부분이 너무 길어질 때, 중복 코드를 제거할 때 유용하게 사용할 수 있다.

 

type 작명 = 타입
→ 작명할 땐 대문자로 시작

 

// 예시 1

// 타입 별칭
type Name = string | number;

// 사용
let 이름 :Name = 'kim';

 

// 예시 2

// 타입 별칭
type User = {
  id: number;
  name: string;
  nickname: string;
  birth: string;
  bio: string;
  location: string;
};

// 사용
let user: User = {
  id: 1,
  name: "홍길동",
  nickname: "뿅뿅",
  birth: "1999.09.09",
  bio: "안녕하세요",
  location: "부천시",
};

let user2: User = {
  id: 2,
  name: "최칠복",
  nickname: "seven",
  birth: "1990.01.01",
  bio: "반갑습니다",
  location: "서울시",
};

 

(참고1) 변수 선언과 마찬가지로, 동일한 스코프에 동일한 이름의 타입 별칭을 선언할 수는 없다. 스코프가 다르다면 가능.

(참고2) 타입 변수는 컴파일 과정에서 사라지게 된다.

 

 

 

 

 

인덱스 시그니처

 

→ 객체 타입을 좀더 유연하게 정의하도록 도와주는 문법

 

type CountryCodes = {
  [key : string] : string
};
// 이 객체 타입에는 key가 string 타입이고, value도 string 타입인 모든 프로퍼티를 포함할 수 있다는 뜻
// 사용 예시

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

※ 인덱스 시그니처 타입은 규칙을 위반하지만 않으면 모든 객체를 허용하므로 빈 객체의 경우에도 오류가 나지 않는다. 이 점을 유의해서 사용할 것. 

 

// 반드시 포함해야 하는 프로퍼티가 있다면,
type CountryNumberCodes = {
  [key: string]: number;
  Korea: number;
};
// 이런 식으로도 쓸 수 있다.
// 이때 주의할 점은 인덱스 시그니쳐의 value 타입과 직접 추가한 프로퍼티의 value 타입이
// 호환되거나 일치해야 한다는 것이다. 
type CountryNumberCodes = {
  [key: string]: number;
  Korea: string; // 오류!
};

 

 

 


 

 

 

열거형 타입 (Enum ; Enumerable Type)

 

: 여러가지 값들에 각각 이름을 부여해 열거해두고 사용하는 타입 (JS에는 없음.)

 

// 사용 예시

enum Role {
  ADMIN = 0,
  USER = 1,
  GUEST = 2,
}

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

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

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

// 개발을 하다 보면, 회원의 역할을 숫자로 지정하는 경우가 많다. 
// 문자열로 지정하면 임의적인 부분이 많아 관리하기가 까다롭기 때문이다.
// 하지만 숫자로 지정하는 경우에도 어떤 숫자가 어떤 역할이었는지 헷갈릴 수 있다.
// 그럴 때 이렇게 enum 타입을 사용하면, 혼동 가능성을 줄일 수 있다. 
// 결과적으로 user1.role 에는 0이 user2.role에는 1이 user3.role에는 2가 할당되게 된다.

 

// 참고로 enum 멤버에 숫자 값을 직접 할당하지 않아도 
// 0 부터 1씩 늘어나는 값으로 자동 할당된다.

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

// 기본적으로 시작되는 숫자를 0이 아닌 다른 숫자로 바꾸고 싶다면,
// 아래와 같이 시작 위치에 직접 할당해주면 된다.

enum Role {
  ADMIN = 10, // 10 할당 
  USER,       // 11 할당(자동)
  GUEST,      // 12 할당(자동)
}

// 또는 중간부터 할당할 수도 있다.

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

→ 이렇게 멤버의 값이 모두 숫자인 enum을 숫자형 enum 또는 숫자 열거형 타입이라고 부른다.

 

 

>> 문자열 열거형

 

// 예시

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

const user1 = {
  name: "이정환",
  role: Role.ADMIN, // 0
  language: Language.korean, // "ko"
};

const user2 = {
  name: "홍길동",
  role: Role.USER, // 1
  language: Language.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 = {

 

 

 


 

 

 

Any 타입

 

: 특정 변수의 타입을 확실히 모를 때 사용할 수 있는 타입. 타입스크립트에서만 제공되는 특별한 타입으로, 타입 검사를 받지 않는다. (치트키!)

 

※ any 타입을 많이 사용하면 타입스크립트를 쓰는 의미가 없기 때문에 최대한 사용하지 않는 것이 권장된다. 

    → 런타임 에러를 발생시킬 수 있음.

 

 

 

 

 

Unknown 타입

 

: unknown 타입은 any 타입과 비슷하지만 보다 안전한 타입이라고 할 수 있다. 왜냐하면 any 타입과 다르게 unknown 타입의 값은 어떤 타입의 변수에도 저장할 수 없고, 어떤 메서드도 사용할 수 없기 때문이다. 즉, (어떤 타입이든 상관없이) 오직 값을 저장하는 행위만 할 수 있는 타입이다. 

 

(만약 unknown 타입의 값을 활용하고 싶다면, 아래와 같이 '타입 좁히기'를 이용해 특정 값이 특정 타입임을 보장해 주어야 한다.)

let unknownVar: unknown;

unknownVar * 2 // 오류 !

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

 

 

※ 그러므로 특정 변수가 당장 어떤 값을 받게 될지 모르는 상황이라면, any 타입보단 unknown 타입을 이용하는 것이 더 안전한 선택이다. 

 

 

 


 

 

 

Void 타입

 

: 아무런 값도 없음을 의미하는 타입. (참고로 void; 공허)

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

 

function func1(): string {
  return "hello";
}

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

 

※ 타입스크립트 버전 5.1 이후 아무것도 반환하지 않는 함수의 반환값 타입으로 undefined를 명시해도 문제가 발생하지 않도록 수정됨. 

 

 

 

 

 

Never 타입

 

: 존재하지 않는, 불가능한 타입. 보통 함수가 어떠한 값도 반환할 수 없는 상황일 때 사용한다.

 

// 무한 루프를 도는 경우

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

// 의도적으로 오류를 발생시키는 경우

function func4(): never {
  throw new Error();
}

 

 

 

댓글