TypeScript

강정우·2023년 2월 6일
0

TypeScript

목록 보기
1/22
post-thumbnail

TypeScript란 무엇인가

  • JavaScript의 superset 언어이다.

  • 왜 type일까 정접 타입 (statically Typed)의 특징을 갖는다 하여 타입 스크립트이다.
    반대로 JS는 dynamic typed laguage이다.

  • 이게 무슨 뜻이냐? 바로 함수 선언 시점에는 자료형을 특정짓지 않는다는 것이다.
    즉, 타입이 고정되어 있지도 않고 이 함수에서 사용할 타입을 미리 정해두지도 않은 상태에서 전달된 매개 변수를 받아 코드를 실행해보는 것이다.

  • 물론 편리할 수 있겠지만 그만큼 헛점이 많을 수 있고 거대한 프로젝트에서는 어디서 에러가 났는지도 알 수 없게 되어버리면서 최근 TS가 각광받고 있는 것이다.

  • type을 지정해 줌으로써 선언한 타입의 인자를 설정한 타입의 매개 변수에 할당할 수 없다고 알려주기도 한다.
    이를 통해 비슷한 종류의 오류와 의도치 않은 방식의 함수 사용을 잡아낼 수 있다

  • TS의 또다른 특징은 브라우저에서 실행되지 않는다는 것이다.
    그래서 TS=>JS로 컴파일해야한다. 이때 타입 표기는 모두 삭제된다.
    그리고 또 컴파일 단계에서 우리가 미처 발견하지 못한 문제점을 찾아 알려준다.

  • npm tsc 로 TS=>JS 컴파일을 실행한다.
    하지만 config 파일 없이 실행하면 오류가 나는데 config파일을 구성해줘야 TS에게 컴파일할 파일을 알려줄 수 있기 때문이다.

  • npm tsc 파일명 의 명령어로 특정 TS파일만 컴파일하여 JS파일로 받아볼 수 있다.
    이때 IDE에서 에러를 내뿜더라도 컴파일을 해준다.

기본 문법

원시 타입

let age : number = 27;
let useName :string = "강정우"
let haveLunch: boolean = false;
  • 이때 특징은 type 표기가 "소문자" 라는 것인데 만약 대문자로 바꿀경우 이는 JS의 Number 객체를 가리키게 되니 주의하자
    여기서 우리가 표현하고 싶은 것은 primitive한 원시타입의 일반적인 숫자, 문자타입이라면 소문자로 작성해주어야한다.

  • null과 undefined 타입도 있지만 무언가를 null로 지정하는 경우는 잘 없다.
    예를들어 ‘hobbies’라는 변수를 만들고 null로 지정할 수는 있겠지만 이후에 다른 값을 할당하려고 하면 오류가 발생한다. 따라서 let 변수명 : null 이런 식으로는 잘 사용하지 않는다.

참조 타입

  • 배열과 객체에 대하여 알아보자

배열

let hobbies: string[] = ["Workout", "Gaming", "Shopping"];
  • 원시 타입 뒤에 [] 만 붙여주면 된다 단, 이렇게 선언 시 아래 자바 코드와 같이 String밖에 담을 수 없다.
String[] beer = {"Kloud", "Cass", "Asahi", "Guinness", "Heineken"};
  • 그렇다면 배열어 여러 자료형을 담고 싶다면 어떻게 하면 될까? 우선 any 로 선언할 수 있다.
let person
person = {
  name:"alex",
  age:27
}

  • 또한 타입스크립트는 특별한 구성이 없어도 잘 동작하여 위 처럼 따로 지정하지 않으면 자동적으로 any라는 특별한 타입을 부여한다.
  • 단, 타입은 예비적으로 사용되는 타입이므로 타입스크립트를 사용하는 주요 목적과 반대되므로 사용하지 않는 게 좋다.

객체, 객체 배열

    let person: {
        name: string;
        age: number;
    };
    person = {
        name:"alex",
        age:27
    }
    person = {
        isEmployee:false			// <= 에러
    }
    let people:{
        name: string;
        age: number;
    }[]
  • 객체를 설정하고 싶다면 각 필드값에 타입을 선언하면 된다.
  • 그리고 만약 위와 다른 필드값이나 데이터 타입이 들어오면 에러를 내뿜는다.
  • 또한 이 객체를 여러개를 배열로 만들고 싶다면 단순히 [] 를 붙여주면 된다.

type inference (타입 추론)

  • 기본적으로 타입스크립트는 가능한 많은 타입을 유추하려고 한다.
    무슨말이냐 명시적인 타입 표기가 없어도 어떤 타입을 어디에 사용해야 할지 알아내려고 한다.
    알기쉽게 예제를 봐보자
let course = "hello";
course=5			// <= 에러
  • 위코드를 보면 2번째 줄에 밑줄이 생기는데 이런 식으로 변수를 만들고 바로 초기화하면 타입스크립트는 할당된 값의 자료형을 보고 변수에 저장된 값이 문자열이라는 걸 알아낸 후 해당 값의 자료형을 변수의 타입으로 여기고 사용하는 것이다.
  • 이는 TS가 타입을 추론하여 우리가 작성할 코드가 줄어든다는 것이다. 이런식으로 type inference를 이용하여 코드를 작성하는 것이 권장되고 있다.

유니온 타입

  • 앞서 우리는 한가지의 타입만을 지정하여왔다. 하지만 여러가지 타입을 동시에 지정하고싶다면 어떻게 할까?
let course: string | string[] | number | boolean[] = "hello";
  • 앞서 우리가 객체타입에서 지정한 것 처럼 여러가지의 타입들을 | 파이프 문자를 이용하여 이어 붙여서 지정할 수 있다.
  • 이처럼 유니온 타입은 TS로 하여금 값과 타입을 좀더 유연하게 정의할 수 있다.

타입 Alias

  • 그동안에 배운것을 보면 타입을 정의하느라 매우 많은 양의 코드를 쳐야할 것 같다.
  • 그래서 이를 해결하고자 타입 알리아스가 나왔는데
  • 우리가 직접 기본(Base) 타입을 만들어 거기에 복잡한 타입을 정의해 두고 그 타입 별칭을 사용하여 가져다 쓰는 것이다.
type Person = {
  name: string;
  age: number;
};

let person: Person;

person = {
  name: "alex",
  age: 27
};

let people: Person[];
  • ‘type’ 예약어를 사용하면 일반적인 자바스크립트에는 없는 키워드이지만 타입스크립트에는 추가되어 있다.
    이제 type 키워드 뒤에 원하는 이름을 붙이면, 그게 새로운 타입의 이름된다.

  • 그래서 이 TS파일을 컴파일하면 반환되는 JS파일에서는 해당 코드를 찾아볼 순 없을 것이다.

  • 또한 설정한 타입 뒤에 [] 예약어를 사용하여 배열로 만든다음 사용할 수도 있다.

함수와 타입

  • 함수를 사용할 때도 타입을 지정한는 위치가 따로 있다.

  • 자세히보면 우리가 function add() :number 라고 따로 지정하지 않았는데 설정이 되어있는 것을 볼 수 있다. 이는 TS가 반환타입에 의하여 inference했다고 볼 수 있다.
  • 하지만 당연히 explicit하게 설정할수도 있다.
const add = (a:number, b:number):string|number => {
  return "a + b";
}
  • 또 하지만 꼭 지정해야 할 이유가 없다면 타입스크립트가 알아서 타입을 추론하니까 지정하지 않는 게 좋다.
  • 따라서 여기서 염두해두어야하는 것은 반환에도 타입이 있고 지정할 수 있다는 것을 기억해야한다.
  • 또 중요한 것은 함수명을 지을 때 JS에 지정되어있는 예약어를 함수명으로 중복되게 짓는다면
    에러를 내뿜으니 이것또한 기억하고 있어야한다.

void

  • 진짜 오랜만에 보는 java의 void이다.

  • 만약 반환값을 따로 지정해주지 않았다면 void로 지정이 되는데 null, undefined와 비슷한 개념을 갖지만 void는 항상 함수와 결합하여 사용된다는 특징이 있다.

  • void는 절대 반환값이 없는다. 만약 void를 반환하는 함수에 반환값을 받아서 작업하려면 undefined로 받아서 작업해야 오류도 안뜨고 작업을 할 수 있다.

제네릭 (generics)

문제점

  • 앞서 배운 내용을 종합하여 아래과 같은 예제 코드를 짰는데 타입을 정확히 지키기려고 사용하는 TS에서 any 타입을 사용하자마자 총제적 난국인 상황이 벌어진다.
const insertAtBeginning = (array:any[], value:any) => {
  const newArray = [value, ...array];
  return newArray;
}
const testArray=[1,2,3]
const updatedArray = insertAtBeginning(testArray, -1);
updatedArray[0].split("");
  • "updatedArray"는 분명 숫자로만 이루어져있어서 split 메서드는 사용할 수 없다.
    하지만 컴파일러에서 에러를 보여주지도 않고 이상이 없어보인다.
    여기서 문제점이 생기는 것이다.

즉, any를 사용하면 함수를 호출한 다음 타입스크립트로부터 어떤 지원도 받을 수 없게 된다
그래서 이런 문제점을 해결하기위해 generics라는 기능이 존재한다.

<T>

  • 별칭 generic type placeholder
  • 선언 시점이 아니라 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법이다.
  • 한번의 선언으로 다양한 타입에 '재사용'이 가능하다는 장점이 있다.
const insertAtBeginning = <T>(array:T[], value:T) => {
	const newArray = [value, ...array];
    return newArray;
}
const testArray=[1,2,3]
const updatedArray = insertAtBeginning(testArray, -1);
updatedArray[0].split("");
  • 어떤 식별자를 넣어도 상관은 없지만 통상 type의 이니셜을 따와서 <T>라고 작성한다.
  • 이 제너릭 기호를 넣어줌으로써 배열과 새로들어올 값들이 모두 같은 타입을 띄고있다고 TS에게 알려주는 것이다.
  • 그리하여 첫 변수의 값을 토대로 타입을 inference하는 것이다.

  • 이처럼 제네릭을 사용하면 유연성과 타입의 안정성 측면에서 모두 이점을 취할 수 있다.

class

  • class를 type으로도 지정할 수 있다.
  • 그래서 이것을 응용하여 <T extends Lengthwise>처럼 Lengthwise를 extends해서 사용하면 부분적으로 length property가 있는 타입의 경우 타입에 의한 에러 상괎없이 사용할 수 있다.
const 함수 = <T extends {toString:Function}>(변수:객체<T>) => {
  객체{
    필드키1 = 필드벨류1.toString()
  }
}
  • 2개 이상의 제러릭을 사용할 수도 있다.
const 함수 = <T,U>(변수1:T, 변수2:U):[T,U] => {
  return [v,u]
}
  • 또한 3항연산자로 타입지정이 가능하다.
profile
智(지)! 德(덕)! 體(체)!

0개의 댓글