topbg
Typescript

typescript type

2023.05.26

타입스크립트에서 지원하는 type 종류

1. 원시 타입

다른 타입의 메소드를 사용하면 에러가 발생한다.

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;

num1 = "123"; // type error
num1.toUpperCase(); // string methord error

string

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

str = 1; // type error
str1.toFixed(); // number method error

bollean

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

null

let null1: null = null;

undefined

let unde1: undefined = undefined;

2. 리터럴 타입

해당 value를 타입으로 지정하고, 만일 다른 value를 사용하면 에러가 발생한다.

let numA: 10 = 10;
numA = 12; // number 10 만 허용

let strA: "hello" = "hello";
strA = "h"; // string "hello" 만 허용

let boolA: true = true;
boolA = false; // boolean true만 허용

3. 배열, 튜플

배열

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

let stringArr: string[] = ["hi", "name", "age"];

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

// 배열 요소 타입이 여러가지인 경우
let multiArr: (number | string)[] = [1, "hello"];

// 다차원 배열의 타입 정의
let doubleArr: number[][] = [
  [1, 2, 3],
  [4, 5],
];

튜플

  • 타입 스크립트에만 사용 하는 배열 타입
  • 길이와 타입이 고정된 배열
let tup1: [number, number] = [1, 2]; // 배열 index가 2이고 타입은 number
tup1 = [1, 2, 3]; // index가 갯수가 2이상이므로 에러 발생
tup1 = ["1", "2"]; // type이 string여서 에러 발생

여러 타입을 가진 튜플

let tup2: [number, string, boolean] = [1, "2", true];
tup2 = [1]; // index가 하나인 요소를 선언하여 에러 발생
tup2 = ["2", 1, true]; // type 순서가 잘못되어 에러 발생

주의해야 할 튜플

튜플을 js로 변환하면 배열로 변경된다.

그렇기 때문에 배열 메서드를 사용하면 튜플의 기능이 없어지므로 사용에 주의해야 한다.

let tup2: [number, string, boolean] = [1, "2", true];
tup2.push(1);
console.log(tup2); // [ 1, '2', true, 1 ]

튜플을 사용하는 이유?

  • 배열에 다른 요소를 추가할 경우 지정한 타입으로 선언하지 않으면, 자바스크립트에서는 에러를 발생시키지 않아 문제가 발생한다.

  • 타입스크립트의 튜플을 사용하여 선언하면 이러한 문제점을 해결할 수 있다.

const users: [string, number][] = [
  ["user1", 1],
  ["user2", 2],
  ["user3", 3],
  ["user4", 4],
  [5, "h"], // number와 string인 요소를 추가혀면 오류를 발생 시킨다.
];

4. 객체

객체 리터럴 타입

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

Typescript는 구조적 타입 시스템

객체 타입을 property기준으로 타입을 결정한다. (Property Based Type System)

선택적 property 타입

해당 property에 물음표를 넣게 되면 선택적 property가 된다.

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

user = {
  name: "user2",
};
// 해당 id는 있어도 되고 없어도 에러가 발생하지 않는다.

readonly

값이 수정될 수 없도록 읽기 전용으로 변경

let config: {
  readonly apiKey: string;
} = { apiKey: "My Api Key" };

config.apiKey = "modify Api Key"; // 읽기 전용인데 값이 변경 되어 에러 발생시킴

5. type alias and Index Signature

type alias

타입을 별도로 지정하여 정의 할 수 있다.

type type명 = 타입

// type alias 적용 전
let user1: { id: number; name: string; age: number; bio: string; location: string } = {
  id: 1,
  name: "user1",
  age: 18,
  bio: "hello",
  location: "seoul",
};

let user2: { id: number; name: string; age: number; bio: string; location: string } = {
  id: 1,
  name: "user2",
  age: 18,
  bio: "hello",
  location: "seoul",
};
// type alias 적용 후
type User = {
  id: number;
  name: string;
  age: number;
  bio: string;
  location: string;
};

let user11: User = {
  id: 1,
  name: "user1",
  age: 18,
  bio: "hello",
  location: "seoul",
};

let user22: User = {
  id: 1,
  name: "user2",
  age: 18,
  bio: "hello",
  location: "seoul",
};

type alias의 스코프

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

let user11: User = {
  id: 1,
  name: "user1",
  age: 18,
  bio: "hello",
  location: "seoul",
};
// global scope의 User type

function func() {
  type User = { id: number; name: string };
  let funcUser: User = {
    id: 1,
    name: "sss",
  };
  // 해당 타입은 함수 내의 스코프에서만 사용 가능하다.
}

Index Signature(인덱스 시그니처)

  • key와 value의 타입의 규칙에 따라 유연하게 타입을 선언하는 문법
  • key와 value의 타입만 일치하면 key와 value의 갯수에 상관없이 추가하거나 삭제해도 에러가 발생하지 않는다.
type CountryCodes = {
  Korea: string;
  UnitedState: string;
  UnitedKingdom: string;
};
// key type이 string이고 value도 string이 규칙적으로 선언

// index signature
type CountryCode = {
  [key: string]: string;
};

let countrycodes: CountryCode = {
  Korea: "ko",
  UnitedState: "us",
  UnitedKingdom: "uk",
    ... 프로퍼티를 여러개 추가해도 에러가 발생하지 않는다.
};

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

let countryNumberCodes: CountryNumberCodes = {
  Korea: 410,
  UnitedState: 840,
  UnitedKingdom: 826,
  ... 프로퍼티를 여러개 추가해도 에러가 발생하지 않는다.
};

빈 객체로 선언해도 에러가 발생하지 않는다. 아무런 property가 없기 때문에 타입 규칙을 위반하지 않는다.

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

만일 객체에 key와 value를 명시하려면 type에 key와 타입을 선언한다.

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

let countryNumberCode: CountryNumberCode = {
  Korea: 410,
};

index signature value는 number이고, 추가한 property인 Korea의 value가 string이면 에러가 발생한다.

타입을 number로 일치 시키면 에러가 발생하지 않는다.

type CountryNumberAndStringCode = {
  [key: string]: number;
  Korea: string; // 에러 발생
  Korea: number;
};
let countryNumberAndStringCode: CountryNumberAndStringCode = {
  Korea: "ko", // 에러 발생
  Korea: 410,
};

6. enum

  • enum은 열거형 타입, Typescript에만 있는 타입
  • 여러가지 값들에 각각 이름을 부여해 열거하고 사용하는 타입
  • 열거형은 주로 상수 값들을 그룹화하고 가독성을 높이며 오류를 방지 하기 위해 사용한다.

enum에 값을 입력하지 않으면 자동으로 0부터 값을 채워 +1씩 증가 된다.

// eum 선언하기
enum Role {
  ADMIN,
  USER,
  GUEST,
}

const user1 = {
  name: "master",
  role: Role.ADMIN, // 0이 자동으로 삽입
};
const user2 = {
  name: "user1",
  role: Role.USER, // 1이 자동으로 삽입
};
const user3 = {
  name: "user2",
  role: Role.GUEST, // 2이 자동으로 삽입
};

console.log(user1, user2, user3);

/*
{ name: 'master', role: 0 } 
{ name: 'user1', role: 1 } 
{ name: 'user2', role: 2 }
*/
  • 만일 enum 멤버에 값을 직접 할당 가능하다. 그리고 나머지 enum에 값을 할당하지 않으면 자동으로 직접 할당한 값을 기준으로 +1씩 증가 된다.
enum Role {
  ADMIN = 10,
  USER, // 11
  GUEST, // 12
}

const user1 = {
  name: "master",
  role: Role.ADMIN, // 10 할당
};
const user2 = {
  name: "user1",
  role: Role.USER, // 11 할당(자동)
};
const user3 = {
  name: "user2",
  role: Role.GUEST, // 12 할당(자동)
};

문자열 열거형

enum 멤버에 숫자이외에 문자열도 할당 할 수 있다.

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

enum Role {
  ADMIN,
  USER,
  GUEST,
}

const user1 = { name: "master", role: Role.ADMIN, language: Language.korean };
const user2 = { name: "user1", role: Role.USER, language: Language.english };
const user3 = { name: "user2", role: Role.GUEST, language: Language.korean };

console.log(user1, user2, user3);

/*
{ name: 'master', role: 0, language: 'ko' } 
{ name: 'user1', role: 1, language: 'en' }
 { name: 'user2', role: 2, language: 'ko' }
*/

7. Any, Unknown

any와 unknown는 특정 변수가 현재 시점에서 알수 없거나 확인할 수 없을때 사용하는 타입스크립트의 타입이다.

any

  • 모든 타입을 허용한다. (타입검사를 하지 않는다.)
  • 다른 타입의 값을 재할당해도 에러가 발생하지 않는다.
  • 타입검사를 하지 않아 그에 따른 부작용이 발생할 수 있기 때문에 any 타입 사용을 지양해야 한다.
let num = 1;
num = "hello"; // ❌ type error

any로 타입을 선언하면, 타입검사를 하지 않아 에러가 발생하지 않는다.

let num = 1;
num = "hello";
console.log(num); // hello
let value: any = 10;
value = "hello";

let num: number = 1;

num = value;
console.log(number1);

Unknown

  • any와 비슷하지만 안전한 타입이다.
  • 변수에 Unknown 타입으로 지정하면, 모든 타입의 값을 할당 받을 수 있다.
  • 하지만 Unknown 타입을 다른 변수에 재할당하거나, 연산 및 메서드에 사용하면 에러가 발생한다.
let unknownVar: unknown;

unknownVar = ""; // string 타입 할당
unknownVar = () => {}; // function 할당
unknownVar = 1; // number 타입 할당

에러나는 조건

  • unknown으로 선언한 변수를 다른 변수에 재할당 할 수 없다.
let num: number = 10;
let unknownVar: unknown;

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

num = unknownVar; // ❌ error
  • unknown으로 선언한 변수는 연산을 하거나 메서드를 사용하면 에러가 발생한다.
let unknownVar: unknown;
unknownVar = 1;

console.log(unknownVar.toString()); // ❌ error
console.log(unknownVar * 2); // ❌ error

만일 unknown 타입의 변수 값을 해당하는 명시적인 타입을 가지기 위해서는 조건문을 이용하면 해결할 수 있다.

function printName(person: unknown) {
  if (typeof person === "string") {
    console.log(person.toUpperCase()); // OK: 'person'이 'string' 타입일 때만 사용 가능
  } else {
    console.log("Unknown name");
  }
}

printName("Alice"); // ALICE
printName(123); // Unknown name

if (typeof unknownVar === "number") {
  // 이 조건이 참이된다면 unknownVar는 number 타입으로 볼 수 있음
  unknownVar * 2;
}
  • any 타입은 타입 검사를 비활성화하고 모든 작업을 허용하기 때문에 타입 안전성이 떨어질 수 있다.
  • 반면, unknown 타입은 타입 안전성을 유지하면서 타입이 알려지지 않은 값들을 처리하는 유연성을 제공하기때문에 코드를 작성할 때는 any 타입보다는 타입 안전성을 유지할 수 있는 unknown 타입을 사용하는 것이 나을 수 있다.

8. void, never

void

  • void 타입은 아무것도 없음을 나타내는 타입이다.
  • 리턴값이 없는 함수에서 사용한다.
function func(): void {
  console.log("hello");
}

하지만 자바스크립트의 undefined, null이 값이 없는 타입이지만, 함수에서 리턴 타입으로 undefined로 지정하면 함수는 undefined 타입으로 값을 리턴해야 한다.

function func(): undefined {
  console.log("Hello"); // ❌ error
  // return undefined; // undefined 리턴값이 없으면 에러 발생
}

function func(): null {
  return null;
}

함수가 아닌 변수에 void를 선언하면 변수는 undefined과 null 타입에 할당 할 수 있다.

let value: void;

value = "hello"; // ❌ error
value = 1; // ❌ error

valiue = undefined;
// "strictNullChecks: false" 일 경우
value = null;

never

  • 함수가 어떤 값도 반환하지 않거나 절대 끝나지 않는다는 것을 나타 냄
  • 함수의 무한루프는 종료하지 않아 리턴값을 반환할 수 없을 때나 throw를 통한 구문이 있을때 never를 사용한다.
function throwError(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {
    console.log("Infinite loop");
  }
}

let result: never = throwError("Something went wrong"); // 함수가 예외를 throw하므로 never 타입
let loop: never = infiniteLoop(); // 함수가 끝나지 않으므로 never 타입

변수에 never 타입을 정의하면 어떠한 타입도 변수에 할당 할 수 없다.

let anyVar: any;
let a: never;
a = 1; //
a = null; //
a = undefined; //
a = anyVar; // any 타입을 never 타입에 할당 할 수 없다. ❌

referance

이해한 것을 정리하다보니
잘못된 부분이 있을 수도 있습니다.
댓글로 잘못된 부분을 알려주세요.