본문 바로가기

온라인 강의(유데미, 인프런 등)/한 입 크기로 잘라먹는 타입스크립트(인프런)

[한 입 크기로 잘라먹는 타입 스크립트] 타입스크립트 기본

반응형

1. 기본 타입 

기본 타입(내장 타입) = 타입스크립트가 자체적으로 제공하는 타입

각각의 기본 타입들은 서로 부모 자식 관계를 이루며 계층을 형성
 

2. 원시 타입

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

원시 타입이 아닌 배열이나 객체 같은 비 원시 타입들은 동시에 여러 개의 값들을 저장할 수 있는 반면, number, string, boolean 등의 원시 타입은 단 하나의 값만 저장 가능하다.

1) number 타입

자바스크립트에서 숫자를 의미하는 모든 값을 포함하는 타입. 단순 정수 뿐만 아니라 소수, 음수, Infinity, NaN등의 특수한 숫자들도 포함한다.

변수의 이름 뒤에 콜론(:)과 함께 변수의 타입을 정의하는 문법 = ‘타입 주석’ or ‘타입 어노테이션’
// 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;

2) string 타입

문자열을 의미하는 타입. 단순 쌍따옴표 문자열 뿐만 아니라 작은 따옴표, 백틱, 템플릿 리터럴로 만든 모든 문자열을 포함한다.

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

3) boolean 타입

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

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

4) null 타입

// null
let null1: null = null;

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

자바스크립트에서는 다음과 같이 아직 값이 정해지지 않은 상태에서는 변수에 null 을 임시로 넣어두곤 했다.

let numA = null;

그러나 만약 타입스크립트에서는 이 numA가 만약 number 타입의 변수일 경우 다음과 같이 오류가 발생한다.

let numA: number = null;  // ❌

null은 number 타입에 포함되는 값이 아니기 때문에 오류가 발생하는 것이다. 이럴 때에는 tsconfig.json의 strcitNullChecks(엄격한 null 검사) 옵션을 false로 설정하면 된다.

5) undefined 타입

// undefined 타입
let unde1: undefined = undefined;

6) 리터럴 타입

타입스크립트에는 string, number 처럼 범용적으로 많은 값을 포함하는 타입 뿐만 아니라 딱 하나의 값만 포함하는 타입도 존재한다.

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

 

3. 배열과 튜플

1) 배열 타입

배열을 저장하는 변수의 이름 뒤에 타입 주석의 시작을 의미하는 콜론(:)을 작성한 다음 배열요소타입[] 형식으로 배열 타입을 정의한다.

let strArr: string[] = ["hello", "im", "winterlood"];

Array<배열요소타입> 형태로도 배열의 타입을 정의할 수 있다. 참고로 이렇게 꺽쇠와 함께 타입을 작성하는 문법을 타입스크립트에서는 ‘제네릭’ 이라고 부른다.

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

다양한 타입의 배열 요소를 갖는 배열 타입은 다음과 같이 소괄호와 바를 이용하여 정의한다. 바(|)를 이용해 여러 타입중 하나를 만족하는 타입을 정의하는 문법을 유니온(Union) 타입 이라고 부른다.

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

다차원 배열 타입[]를 연달아 작성하면 된다.

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

2) 튜플 타입

튜플은 자바스크립트에는 없는 타입스크립트의 특수한 타입으로 길이와 타입이 고정된 배열을 의미한다.

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

튜플은 결국 배열이므로 배열 메서드인 push나 pop을 이용해 고정된 길이를 무시하고 요소를 추가하거나 삭제할 수 있다. 따라서 튜플을 사용할 때 배열 메서드를 이용해 요소를 추가하거나 삭제하는 등의 연산을 할 때에는 각별히 주의해야 한다.

# 튜플을 왜 쓰는걸까?

다음과 같이 회원 정보를 2차원 배열로 저장하는 상황을 가정해 보자.

const users = [
  ["이정환", 1],
  ["이아무개", 2],
  ["김아무개", 3],
  ["박아무개", 4],
];

각 배열의 0번 인덱스에는 회원의 이름, 1번 인덱스에는 회원의 아이디를 저장해 두었는데 만약 눈치 없는 동료 중 한명이 다음과 같이 순서를 잘 못 배치해 요소를 추가할 경우 문제가 될 수 있다.

const users = [
  ["이정환", 1],
  ["이아무개", 2],
  ["김아무개", 3],
  ["박아무개", 4],
  [5, "조아무개"], // <- 새로 추가함
];

자바스크립트에서는 이런 문제를 확인할 방법이 없다. 그러나 타입스크립트에서는 튜플을 사용하면 위와 같은 실수를 빨리 바로잡을 수 있다.

const users: [string, number][] = [
  ["이정환", 1],
  ["이아무개", 2],
  ["김아무개", 3],
  ["박아무개", 4],
  [5, "조아무개"], // 오류 발생
];

 

4. 객체

1) 객체 리터럴 타입

객체 리터럴 타입은 중괄호를 열고 객체가 갖는 프로퍼티를 직접 나열해 만드는 타입이다.

타입스크립트는 기존의 정적 타입 시스템을 따르는 언어인 C나 Java와는 달리, 객체의 타입을 정의할 때 프로퍼티를 기준으로 객체의 구조를 정의하듯이 타입을 정의한다는 점 입니다.

타입스크립트의 이런 특징을 구조적 타입 시스템이라고 부른다. 객체의 구조를 결정하는 것은 프로퍼티이다. 따라서 타입스크립트는 이 객체에 어떤 프로퍼티들이 있어야 하는지 정의하는 방식으로 객체의 타입을 정의한다. 마치 name과 id가 있는 객체는 '유저 타입'이야 라고 보는 것과 같다.

let user: {
  id: number;
  name: string;
} = {
  id: 1,
  name: "이정환",
};

user.id;

2) 선택적 프로퍼티

특정 프로퍼티를 상황에 따라 생략하도록 만들고 싶다면 해당 프로퍼티를 선택적 프로퍼티로 만들어줘야 한다.

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

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

3) 읽기 전용 프로퍼티

특정 프로퍼티를 읽기 전용으로 만들고 싶다면 다음과 같이 프로퍼티의 이름 앞에 readonly 키워드를 붙이면 된다.

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

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

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

 

5. 타입 별칭과 인덱스 시그니쳐

1) 타입 별칭

타입 별칭을 이용하면 다음과 같이 변수를 선언하듯 타입을 별도로 정의할 수 있다.

type 타입_이름 = 타입 형태로 타입을 정의한다.

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

참고로 변수 선언과 유사하게 동일한 스코프에 동일한 이름의 타입 별칭을 선언하는 것은 불가능하다. 하지만 스코프가 다르다면 상관 없다.

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

function test() {
  type User = string;
}

2) 인덱스 시그니쳐

인덱스 시그니쳐는 객체 타입을 유연하게 정의할 수 있도록 돕는 특수한 문법이다.

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

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

[key : string] : string 은 인덱스 시그니쳐 문법으로 이 객체 타입에는 key가 string 타입이고 value가 string 타입인 모든 프로퍼티를 포함된다 라는 의미이다. 굳이 일일이 모든 국가의 코드 타입들을 직접 명시할 필요가 없다.

이때 반드시 포함해야 하는 프로퍼티가 있다면 다음과 같이 직접 명시해도 된다.

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

주의할 점은 인덱스 시그니쳐를 사용하면서 동시에 추가적인 프로퍼티를 또 정의할 때에는 인덱스 시그니쳐의 value 타입과 직접 추가한 프로퍼티의 value 타입이 호환되거나 일치해야 한다. 따라서 다음과 같이 서로 호환되지 않는 타입으로 설정하면 오류가 발생한다.

type CountryNumberCodes = {
  [key: string]: number;
  Korea: string; // 오류!
};

 

6. 열거형(Enum) 타입

열거형 타입은 자바스크립트에는 존재하지 않고 오직 타입스크립트에서만 사용할 수 있는 특별한 타입으로, 여러개의 값을 나열하는 용도로 사용한다.
// 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
};

자동 할당되는 값은 기본적으로 0부터 시작한다. 만약 이 값을 변경하고 싶다면 다음과 같이 시작하는 위치에 값을 직접 할당해주면 된다. 그럼 자동으로 그 아래의 멤버들은 1씩 증가된 값으로 할당된다.

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

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

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

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

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

enum의 멤버에는 숫자 말고도 문자열 값도 할당할 수 있다.

enum Role {
  ADMIN,
  USER,
  GUEST,
}

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

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

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 = {

 

7. any와 unknown

1) any

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

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

anyVar = true;
anyVar = {};

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

any 타입은 어떠한 타입 검사도 받지 않기 때문에 아무 타입의 값이나 범용적으로 담아 사용할 수 있고, 다양한 타입의 메서드도 마음대로 호출해서 사용해도 문제가 되지 않는다.

또 any 타입의 값은 어떤 타입으로 정의된 변수던 문제 없이 다 할당할 수 있다. 따라서 다음과 같이 number 타입의 변수 num에 any 타입의 값 anyVar를 할당해도 문제가 발생하지 않는다.

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

let num: number = 10;
num = anyVar;

하지만 위의 코드를 컴파일하거나 실행해보면 toUpperCase 부분에서 런타임 오류가 발생한다. any 타입은 타입 검사를 받지 않는 타입이므로 모든 타입스크립트의 문법과 규칙으로부터 자유롭지만 그만큼 위험한 타입이다. 정말 어쩔 수 없는 경우를 제외하고는 any 타입 사용을 지양해야 한다.

2) unknown

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 타입은 변수의 타입으로 정의되면 모든 값을 할당받을 수 있게 되지만, 그 어떤 타입의 변수에도 할당할 수 없고 모든 연산에 참가할 수 없다. 오직 값을 저장하는 행위밖에 할 수 없다.

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

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

참고로 타입스크립트에서는 위 코드처럼 조건문을 이용해 특정 값이 특정 타입임을 보장할 수 있게 되면 해당 값의 타입이 자동으로 바뀌고 이를 타입 좁히기라고 한다.

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

 

8. void와 never

1) void

void 타입은 아무런 값도 없음을 의미하는 타입이다. return문 자체가 없는 함수의 반환값 타입을 정의해야 할 때 사용한다.

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

물론 변수의 타입으로도 void 타입을 지정할 수 있다. 그러나 void 타입이 undefined 타입을 포함하는 타입이기 때문에 void 타입의 변수에는 undefined 이외의 다른 타입의 값은 담을 수 없다. 

2) never

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

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

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

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

 

https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8/dashboard

 

한 입 크기로 잘라먹는 타입스크립트 - 인프런 | 강의

문법을 넘어 동작 원리와 개념 이해까지 배워도 배워도 헷갈리는 타입스크립트 이제 제대로 배워보세요! 여러분을 타입스크립트 마법사🧙🏻‍♀️로 만들어드립니다., - 강의 소개 | 인프런

www.inflearn.com

 

반응형