타입스크립트 타입

정민교·2023년 10월 5일
0

typescript

목록 보기
2/17

📒기본 타입

타입스크립트가 자체적으로 제공하는 타입들을 타입스크립트의 기본 타입이라고 부릅니다. 다른 말로 내장 타입이라고도 합니다.

이 각각의 기본 타입들은 서로 부모 자식 관계를 이루고 있습니다.

✔️원시 타입의 변수

📌number

// 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;

자바스크립트에서의 숫자 타입입니다. 정수, 음수 뿐만 아니라 소수, Infinity ,-Infinity, NaN까지 포함됩니다.


📌string

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

string은 문자열 타입입니다. 쌍 따옴표, 작은 따옴표, 백틱, 템플릿 리터럴의 모든 문자열을 포함합니다.


📌boolean

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

true, false를 포함하는 참, 거짓을 나타내는 타입입니다.


📌undefined

// undefined
let unde1: undefined = undefined;

📌null

// null
let null1: null = null;

undefinednull은 자바스크립트의 원시 타입 그대로를 의미합니다.

자바스크립트에서 아직 값이 정해지지 않은 변수에 임시로 null을 할당하여 사용하는 경우가 있습니다.

let numA: number = null;  // ❌

하지만 타입스크립트에서는 위와 같이 number 타입의 변수에 null을 할당할 수 없습니다.

이럴 때에는 strictNullChecks 옵션을 false로 설정해주면 됩니다.

📌symbol

const sym1: symbol = Symbol('sym');
// typeof sym
let sym2: symbol = Symbol('sym');
// symbol

symbolconst로 선언할 때와 let으로 선언할 때의 타입 추론이 다릅니다.

타입스크립트에서는 const로 선언한 변수의 타입이 symbol일 경우 unique symbol라고 합니다.

그리고 unique symbol끼리는 비교가 불가능 합니다.

const symbol1 = Symbol.for('sym');
let symbol2 = Symbol.for('sym');

📌bigint

const big = 100000000n;
//const big: 100000000n
let big = 100000000n;
// let big: bigint

✔️리터럴 타입의 변수

하나의 리터럴 값만 할당가능하도록 하는 리터럴 타입의 변수도 선언할 수 있습니다.

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

위와 같이 변수를 타이핑하면 말 그대로 이 리터럴 값만 할당할 수 있는 리터럴 타입 변수가 됩니다.

let으로 선언하지 않고 const로 선언하면 굳이 리터럴 타입 변수로 변수를 타이핑 하지 않아도 리터럴 타입의 변수로 알아서 타입스크립트가 추론합니다.

✔️any 타입의 변수

any 타입은 타입 검사를 받지 않는 타입입니다.

any 타입으로 변수를 타이핑하는 경우는 권장하는 방법이 아닙니다. 변수를 any타입으로 타이핑하면 타입스크립트를 사용하는 이점을 포기하게 되는 것이기 때문입니다.

let anyVar = 10;
anyVar = "hello"; // 오류 발생!

위와 같이 anyVar 변수를 타이핑 하지 않으면, 10을 할당했기 때문에 타입스크립트가 anyVar의 타입을 number 타입으로 추론합니다.

타입스크립트는 초기화 된 값의 타입을 기준으로 변수의 타입을 추론하기 때문입니다.

따라서 이 다음에 anyVar 변수에 string 타입의 값인 "hello" 를 할당하면 오류가 발생하게 됩니다.

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

anyVar = true;
anyVar = {};

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

anyVar 변수를 any 타입으로 타이핑 하게되면 아무 타입의 값이나 변수에 할당할 수 있게됩니다.

toUpperCase()toFixed() 같은 string, number 타입의 메서드를 호출해도 문제가 발생하지 않습니다.

하지만 이 코드를 ts-node로 실행하거나 컴파일을 하게되면 런타임 오류가 발생하게 됩니다.

✔️unknown 타입의 변수

any타입과 비슷하지만 보다 안전합니다.

unknown 타입 변수에는 어떤 타입의 값이든 저장할 수 있습니다.

let unknownVar: unknown;

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

하지만 unknown 타입 변수의 값을 다른 타입 변수에 할당할 수 없습니다.

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

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

num = unknownVar; // 오류 !

또한 unknown 타입 변수는 어떤 연산에도 참여할 수 없고, 어떤 메서드도 사용할 수 없습니다.

let unknownVar: unknown;
(...)

unknownVar * 2 // 오류!

✔️void 타입

void 타입은 아무런 값도 없음을 의미하는 타입입니다.

let a: void;
a = undefined;

void 타입의 변수에는 undefined 타입 값을 저장할 수 있지만 그 외의 값은 저장할 수 없습니다.

아무 값도 반환하지 않은 함수의 반환값 타입을 정의할 때도 사용합니다.

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

✔️never 타입

never 타입은 불가능을 의미하는 타입입니다.

함수가 어떠한 값도 반환할 수 없거나, 오류를 던지는 경우 반환값을 never 타입으로 정의할 수 있습니다.

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

✔️배열과 튜플

📌배열 타입

배열은 자바스크립트에서의 배열과 다르지 않습니다.

let numArr: number[] = [1, 2, 3]

위와 같이 배열 타입의 변수를 선언하고 배열을 할당합니다.

let boolArr: Array<boolean> = [true, false, true];

위와 같은 방법으로도 boolean 배열 타입 변수를 선언할 수 있습니다.

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

이렇게 다양한 타입의 값을 갖는 배열 타입 변수도 선언할 수 있습니다.

📌튜플 타입

튜플은 길이와 타입이 고정된 배열을 의미합니다.

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

위와 같이 튜플 타입의 변수를 선언할 수 있습니다.

튜플도 결국 배열이기 때문에 push, pop 같은 메서드 사용이 가능합니다.

✔️객체 타입

타입스크립트에서 객체 타입의 변수를 선언할 때 object 타입으로 선언할 수 있습니다.

let user: object = {
  id: 1,
  name: "정민교",
};

user.id;

하지만 이렇게 선언하게 되면 타입스크립트는 오류를 발생시킵니다.

업로드중..

object 타입에 id 프로퍼티가 존재하지 않는다는 에러입니다.

타입스크립트의 object 타입은 이 타입이 object 라는 것 외에는 어떠한 정보도 포함하지 않습니다.

따라서 타입스크립트에서 객체 타입의 변수를 정의하기 위해서는 객체 리터럴 타입으로 정의하는 것이 좋습니다.

📌객체 리터럴 타입

let user: {
  id: number;
  name: string;
} = {
  id: 1,
  name: "정민교",
};

user.id;

이렇게 객체 리터럴 문법과 비슷한 방식으로 타입을 정의한다고 하여 객체 리터럴 타입이라고 합니다.

이렇게 변수의 타입을 객체 리터럴 타입 방식으로 정의하면 정상적으로 user.id의 값을 참조할 수 있습니다.

📌선택적 프로퍼티 정의

객체 타입에서 특정 프로퍼티가 있어도 되고 없어도 되도록 객체 타입을 정의할 수 있습니다.

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

user = {
  name: "홍길동",
};

위와 같이 타입 정의의 프로퍼티 뒤에 ?를 붙이면 선택적(Optional) 프로퍼티가 됩니다.

선택적 프로퍼티는 말 그대로 있어도 되고 없어도 되지만 있다면 위에서 보이듯이 반드시 number 타입이어야 합니다.

📌읽기 전용 프로퍼티

읽기 전용 프로퍼티로 정의할 수도 있습니다.

특정 프로퍼티 앞에 readonly 키워드를 붙이면 읽기 전용 프로퍼티가 됩니다.

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

user.name = "dskfd"; // 오류 발생

위와 같이 값을 할당한 이후에는 변경이 불가능합니다.

✔️열거형 타입

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

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

const user1 = {
  name: "정민교",
  role: Role.ADMIN, // 0
};

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

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

위와 같은 방식으로 enum 타입을 정의할 수 있습니다.

직접 enum 멤버에 값을 할당할 수 있지만, 할당하지 않으면 순서대로 숫자값이 할당됩니다.

var Role;
(function (Role) {
    Role[Role["ADMIN"] = 0] = "ADMIN";
    Role[Role["USER"] = 1] = "USER";
    Role[Role["GUEST"] = 2] = "GUEST";
})(Role || (Role = {}));

enum 타입은 컴파일하면 객체가 됩니다.

✔️타입 별칭

타입스크립트에서는 타입 별칭을 이용해서 별도의 타입을 정의할 수 있습니다.

아래와 같이 User 라는 타입을 우리가 직접 정의하여 사용할 수 있습니다.

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

정의한 타입 별칭을 이용하여 변수의 타입을 정의할 수 있습니다.

let user2: User = {
  id: 2,
  name: "홍길동",
  nickname: "winterlood",
  birth: "1997.01.07",
  bio: "안녕하세요",
  location: "부천시",
};

같은 스코프 안에서 동일한 이름의 타입 별칭을 선언할 수 없습니다.

다른 스코프에서는 가능합니다.

// 불가능
type User = {
  id: number;
  name: string;
  nickname: string;
  birth: string;
  bio: string;
  location: string;
};

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

function test() {
  type User = string;
}

✔️인덱스 시그니처

인덱스 시그니처는 객체 타입을 유연하게 정의할 수 있도록 도와주는 문법입니다.

type CountryCodes = {
  Korea: string;
  UnitedState: string;
  UnitedKingdom: string;
};

let countryCodes: CountryCodes = {
  Korea: "ko",
  UnitedState: "us",
  UnitedKingdom: "uk",
};

국가마다 영어 코드를 저장해야 해서 위와 같은 타입을 만들었습니다.

그런데 이렇게 타입을 정의하면 국가가 추가될 때마다 계속 프로퍼티를 추가해주어야 합니다.

이럴 때 인덱스 시그니처를 이용하면 유연하게 객체 타입을 정의할 수 있습니다.

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

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

ContryCodes 타입은 key가 string 타입이고, 값이 string 타입을 가지는 모든 프로퍼티를 포함하는 타입임을 정의한 것입니다.

국가 코드를 숫자로 보관하는 객체가 하나 더 필요하면 타입을 하나 더 정의하면 됩니다.

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

반드시 포함해야하는 프로퍼티가 있다면 직접 명시할 수도 있습니다.

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

하지만 추가적인 프로퍼티를 직접 명시할 때는 인덱스 시그니처의 value 타입과 직접 추가한 프로퍼티의 value 타입이 호환되어야 합니다.

type CountryNumberCodes = {
  [key: string]: number;
  Korea: string; // 오류!
};
profile
백엔드 개발자

0개의 댓글