[TS] (3) 기초 : 기본 타입

41752 단어 typetypescripttype

저번 벨로그에서 타입 선언과 타입 추론에 대해서 알아보았다
[TS] (2) 기초 : 타입 선언과 타입 추론


이제는 타입을 적을 위치에 들어갈 기본적인 타입들에 대해서 공부해보자!!

// 여기 들어갈 타입?
someVariable: ?????;

1. 자바스크립트와 동일한 타입

1) 원시 타입(Number,String,Boolean,Null, Undefined)

  • 타입 선언 : 소문자로 작성(number, string, boolean, null, undefined)

  • 타입 추론 : 변수 선언'만' 할 때(let 키워드, 기본값이 없는 매개변수)를 제외하고는 할당된 값의 타입이 '원시 타입'일 경우에 타입을 선언할 필요는 없습니다.
// 예시
let unde: undefined;
let e:null;
const obj = {
    key: "hello", // 프로퍼티의 값의 타입으로 추론됨
};
const fun4 = (a=1, b:string)=> { // 기본 값이 있고 그 값이 원시 타입인 경우 타입 선언 생략 가능
    console.log(a)
    console.log(b)
}
fun4(2,"hello")

2) 객체 타입(참조 타입)

  • 특징
    - 원시 타입이 아닌 모든 타입이 객체 타입(참조 타입)이며, 객체, 배열, 함수도 이에 해당한다.
    - 여러 타입들로 객체 타입이 선언될 수 있다.
  • 타입 선언 : 중괄호로 선언하며, key:type 쌍으로 객체 내부 프로퍼티의 타입을 정한다.
    - 각 쌍은 콤마(,)가 아니라 세미콜론(;)으로 구분한다.
    - 중첩이 가능하다(Nested Objects).

  • 타입 추론 : 각 프로퍼티의 타입이 모두 추론 가능하면 객체 타입의 선언 자체룰 생략할 수 있지만, 명시적인 선언이 하나라도 필요하다면 모든 타입에 대해 작성해주어야 한다.
const objDeep = {
    key1: "sdfsdf",
    key2: {
        name: "kim",
        job: "developer",
    },
    isMale: true,
};

// 객체(타입 선언 필요함) : arr 대응하는 프로퍼티의 타입이 tuple(길이 고정, 각 인덱스의 타입 정해짐)이었으면 좋겠다. 이 때는 타입을 모두 선언해주어야 함.
// 타입 선언을 하지 않으면 arr 프로퍼티의 타입이 유니온 타입(추후 설명)으로 결정됨(x)
const typeNeeded: {
    key1: string;
    key2: {
        name: string;
        job: string;
    };
    arr: [number, boolean, string];
} = {
    key1: "sdfsdf",
    key2: {
        name: "kim",
        job: "developer",
    },
    arr: [2, true, "hello"],
};

우리가 원하는 arr의 타입이 tuple(길이가 고정, 각 인덱스 타입 고정인 배열) 이라면 선언 필요(이후 설명)

  • 타입 선언했을 때 arr(tuple 타입)
  • 타입을 선언하지 않았을 때 arr(union 타입)

2. 타입스크립트에 추가된 타입

알아야 할 것 : 배열 관련 타입은 원래 자바스크립트에서는 객체 타입에 포함되었으나, 타입스크립트에서는 명시적으로 다른 타입으로써 선언 가능하다. (물론 자바스크립트로 컴파일되면 객체 타입이다 ㅎㅎ)

1) 배열 타입(참조 타입)

  • 타입 특징
    - 길이가 고정되지 않았으며, 배열 항목에 들어갈 수 있는 타입을 결정한다.
    - 객체 타입과 마찬가지로, 여러 타입들로 배열 타입이 구성될 수 있다.
    - 항목의 타입에는 어떤 타입이던 다 들어갈 수 있기 때문에 타입이 확장된 TS에서는 더욱 다채로운 배열 타입을 선언할 수 있다.

  • 타입 선언 : '포함된(될) 항목 타입[]'
// 배열 타입 선언하기(추론 없이)
const arr1: string[] = ["Hello","Bye"]; // string으로만 이루어진 배열 타입
const arr2: (string | number)[] = ["Hello",12]; // string이나 number 타입만 가능(union 타입 포함됨. 이후 다룰 것!)
let arr3: string[];
  • 타입 추론 : 변수 선언'만' 할 때(let 키워드, 기본값이 없는 매개변수)를 제외하고는 할당된 값의 타입이 '배열 타입'일 경우에 타입을 선언할 필요는 없다. 위 코드 3개의 배열와 각각 비교해보자.
const arr1 = ["Hello","Bye"]; // 사실 위처럼 타입 선언할 필요 없다. 아래 이미지 참고

const arr2 = ["Hello", 12]; // string이나 number 타입 항목이 들어간 배열을 원한다면 
// 여기도 마찬가지로 선언할 필요 없다. 아래 이미지 참고

let arr2: string[]; // 변수 선언만 할 때는 타입 선언을 해줘야 함

2) tuple 타입 : 길이가 고정된 배열

  • 타입 특징
    - 배열 내 항목에 들어갈 타입이 각 인덱스마다 정해져 있고, 배열의 길이가 고정됨
    - 여러 타입들로 tuple 타입이 구성될 수 있다.

  • 타입 선언 : [타입,타입,타입,...]
// Good
const arr4: [number, string, () => number] = [
    12,
    "Hi",
    () => {
        return 3;
    },
]; // 배열의 길이가 3이고, 배열 내 항목의 타입이 인덱스 순서대로 number, string, number를 return하는 함수(이후 설명 예정)

  • 타입 추론(거의 불가) : 할당된 값에 의한 타입 추론에 의지하면, 유니온(union) 타입이 되므로 웬만하면 명시적인 선언이 필요할 것이다.
// 위 코드에서 타입 선언을 제외하면 그저 number 혹은 string 혹은 function 타입을 가지는 유니온 타입의 배열이 되어버린다.
// Bad
const arr4 = [
    12,
    "Hi",
    () => {
        return 3;
    },
]; 
// 아래 이미지 참고!

  • 주의!!! : 길이 고정을 벗어나는 예외는 있다 -> 길이에 영향을 주는 자바스크립트 array 빌트인 메서드(push, pop 등등)

    이런 메서드를 안쓰는 것도 좋으나, 아예 이런 걸 막고 싶다면 좀 더 복잡한 타입 선언이 필요할 듯 하다.
    아직 나는 초보라서 다음에 알아보자. ㅠㅠ
    [stackoverflow] tuple에 적용되는 메서드에 대한 질문


3) any 타입 : 타입 검사를 하지 않는 타입

  • 타입 특징
    - 가장 자바스크립트처럼 사용할 수 있는 타입이며, 잘못 사용하면 타입스크립트를 사용하는 의미가 희석될 수 있다.
    - 모든 타입을 할당받을 수 있다.
    - 타입 검사가 필요한 모든 과정을 지나친다. 웬만하면 unknown 타입을 사용하자.

  • 타입 선언 : any

let anyType: any; // any 타입
let b: any[] = [] // any[] 타입
console.log(anyType.push("Hello")); // 컴파일 에러가 발생하지 않음. 런타임 에러 예상됨..
  • 타입 추론 : 변수를 선언할 때 아무 값도 할당하지 않으면(let 키워드 혹은 기본값 없는 매개변수 등) any 타입으로 추론된다.
let a; // any 타입 추론
// 'a' 변수는 암시적으로 'any' 형식이지만, 사용량에서 더 나은 형식을 유추할 수 있습니다.
let b = [] // any[] 타입 추론

4) unknown 타입 : 어떤 타입이든 허용하지만 타입 체크

  • 타입 특징
    - any와 유사하지만 타입 검사를 거친다. 즉 어떤 타입이든 할당할 수 있지만 할당한 변수를 사용할 때 타입 체크를 해줘야 할 의무가 있다.

    - 모든 타입의 최상위에 있다. any 타입 포함

    - 타입 선언 : unknown
    - 타입 추론 : 불가
    - 타입 체크 예시 : 아래 참조
let unknownType: unknown;
unknownType = 12;
let unknownArray: unknown = ["dfd"];
console.log(unknownType.push("Hello")); // 에러 발생

let unknownType: unknown;
unknownType = 12;
let unknownArray: unknown = ["dfd"];
if (Array.isArray(unknownType)) {
    console.log(unknownType.push("Hello")); // 에러 없음!
}

5) union 타입 : 특정 타입만 허용하고 싶을 때

  • 타입 특징 : 두 개 이상의 타입을 할당할 수 있다. 'or'으로 이해하면 편하다.

  • 타입 선언 : 타입 | 타입 | 타입 ...

let stringOrNumber: string | number; 
  • 타입 추론 : 할당되는 값이 여러 타입을 항목으로 가진 배열 혹은 논리합 연산자(||)로 이루어진 표현식일 때 추론이 가능하다.
let c = "sdf" || 12 // c: string | number 추론
c = true // 'boolean' 형식은 'string | number' 형식에 할당할 수 없습니다.
const arr5 = [12, "sdf"] // arr5: (number | string)[] 추론 <- 위에서 많이 봤죠?

6) (문자열) 리터럴 타입 : 유니크한 특정 상수만 허용하고 싶을 때

  • 타입 특징
    - 문자열 리터럴 자체를 타입으로 지정하여, 해당 변수가 그 타입의 값만 가질 수 있도록 제한하는 유니크한 타입이다.
    - 보통 union 타입과 같이 사용하여 특정 상수만 허용하고 싶을 떄 선언한다.

  • 타입 선언: 문자열 리터럴

let d: "frontend"| "backend";
d = "I love you" // '"I love you"' 형식은 '"frontend" | "backend"' 형식에 할당할 수 없습니다.
  • 타입 추론: 불가

    IDE(나의 경우 vscode)의 지원 : (큰 / 작은) 따옴표 입력 시


7) enum 타입 : 같은 종류의 열거형 상수들을 가독성 있게

  • 타입 특징
    - 같은 ‘종류’를 나타내는 여러 개의 숫자 혹은 문자열을 다뤄야 하는데, 각각 적당한 이름을 붙여서 코드의 가독성을 높이고 싶다면 enum을 사용한다. (사실 나는 enum 타입이 제일 어색하다..)
    - 객체 타입이지만, 초기화 후 객체 내 프로퍼티를 바꿀 수 없다.
    - 프로퍼티가 숫자 열거형(0부터)으로 자동 결정되며, 필요에 따라 선언할 때 이를 초기화하여 변경할 수 있다.
    - tree-shaking(사용되지 않는 코드 덜어내기)이 안되어 사용을 권하지 않는다고도 하니 주의하자. 아래 링크 참고!

    더 자세한 설명은 다른 분이 올려주신 좋은 글이 있다.
    TypeScript enum을 사용하는 이유
    이론
    Handbook - Enums - TypeScript
    enum과 tree-shaking 관한 글
    TypeScript enum을 사용하지 않는 게 좋은 이유를 Tree-shaking 관점에서 소개합니다.

  • 타입 선언 : enum 타입명 { EXAMPLE, HI, ... } (관습상 대문자로 선언합니다)
// 숫자 열거형 : 초기화 안함(기본 0부터)
enum WebDeveloper {
    FRONTEND,
    BACKEND,
}
console.log(WebDeveloper.FRONTEND) // 0
console.log(WebDeveloper.BACKEND) // 1

// 숫자 열거형 : 초기화 (초기화한 다음 인덱스부터 차례로 1씩 증가. 전 인덱스에는 영향 없음)
enum Browser {
    CHROME,
    SAFARI,
    WHALE = 10,
    BRAVE,
}
console.log(Browser.CHROME) // 0
console.log(Browser.SAFARI) // 1
console.log(Browser.WHALE) // 10
console.log(Browser.BRAVE) // 11

// 문자열 열거형 : 초기화 (문자열 초기화된 이후 항목들은 초기화해야 함). 유의미한 정보 전달
enum Browser {
    CHROME = "ch",
    SAFARI = "sa",
    WHALE = "wh",
    BRAVE = "br",
}
console.log(Browser.CHROME) // "ch"
console.log(Browser.SAFARI) // "sa"
console.log(Browser.WHALE) // "wh"
console.log(Browser.BRAVE) // "br"

// 이종 열거형 : 여러 개의 타입으로 초기화(굳이 사용할 필요 없음)
enum Browser {
    CHROME = "ch",
    SAFARI = 10,
    WHALE,
    BRAVE ,
}
console.log(Browser.CHROME); // "ch"
console.log(Browser.SAFARI); // 10
console.log(Browser.WHALE); // 11
console.log(Browser.BRAVE); // 12
  • 타입 추론 : 불가

8) (함수의 return)void 타입 : 함수가 return 하지 않음

  • 타입 특징
    - 함수의 return 값의 타입을 선언할 때 활용되는 타입 중 하나이다.
    - 함수가 return하지 않을 때(return문이 없을 때) return 값의 타입을 void로 선언한다.
    - void 타입의 함수의 return 값을 출력하면 undefined가 출력된다.(void > undefined, null)
    - 변수에도 선언할 수 있지만 void 타입은 null이나 undefined만을 할당할 수 있으므로 그다지 유용하지 않다.
    - undefined와는 다름 : return문이 있음(함수에서는 드물게 쓰임)
// 함수의 return값 타입을 undefined로 하려면
const undefinedFunc = (): undefined => {
	return;
}
  • 타입 선언 : void
// void
const voidFunc = (): void => {
    console.log("Hello");
};
console.log(voidFunc) // undefined
  • 타입 추론 : 함수 선언 시 함수의 return문이 없을 경우 void 타입으로 추론된다. 위 코드에서 ": void"를 삭제하더라도 추론이 잘 된다.
// void
const voidFunc = () => {
    console.log("Hello");
};
console.log(voidFunc) // undefined


9) (함수의 return) never 타입 : 절대로 발생하지 않는 값의 타입

  • 타입 특징
    - 함수의 return 값의 타입을 선언할 때 활용되는 타입 중 하나이다.
    - 모든 타입의 assign 될 수 있다. 즉 최하위 타입!

    - 절대로 발생하지 않는 값의 타입일 경우 never 타입으로 선언한다.
    - '절대로 발생하지 않는 값'이란 뭘까? : 강의에 따르면 함수가 실행되는 순간 script가 cancel/crush/destroy되는 경우(에러, 무한루프 등)를 말한다.
  • 타입 선언 : never
// never
const neverFunc = (): never => {
    throw { message: "에러입니다", errorCode: 500 }; // error 발생
};
const infiniteFunc = (): never => {
    while (true) {} // 무한 루프
};
const returnErrorFunc = (): never => {
    return neverFunc(); // never값을 return하는 함수
};
  • 타입 추론 : 절대로 발생하지 않는 값의 경우나 never 타입을 return하는 경우에 타입이 추론된다. 위 코드의 return 타입 선언은 사실 필요가 없다!
// never 타입 추론
const neverFunc = () => {
    throw { message: "에러입니다", errorCode: 500 };
};
const infiniteFunc = () => {
    while (true) {}
};
const returnErrorFunc = () => {
    return neverFunc();
};




10) 함수 타입 : 함수의 형태를 정하기

  • 타입 특징
    - 함수 자체를 타입으로 볼 수 있다.
    - 함수 내부의 형태 (매개변수의 수와 타입, return 값의 타입)를 선언한다.
  • 타입 선언 : (매개변수: 타입, 매개변수: 매개변수, ....) => 타입
let getFunc: () => number; 
// 매개변수는 없고, return 값이 number 타입인 함수 타입
let getFunc2: (a: number, b: number) => string; 
// 2개의 number 타입 매개변수를 가지고, return 값이 string 타입인 함수 타입
  • 타입 추론(함수 선언 시) : 매개변수(유무, 개수, 타입)는 추론 불가. return 타입은 코드 문맥에 따라 추론이 가능하다.
let funcType2 = (a: number, b: string) => {
    return a.toString() + b;
}; 
// return 타입은 선언하지 않았으나 return 값이 string인 것이 명확하므로 string 타입으로 추론된다.


3. 다음에 공부할 것

TypeScript Compiler에 대해서...

좋은 웹페이지 즐겨찾기