
~ 2024.7.1
typescript는 microsoft에서 개발 및 유지보수하는 오픈소스 언어
스케일이 큰 애플리케이션 개발을 위해 설계된 언어
js에 정적타이핑 + 선택적 타입 annotation이 추가된 형태( annotation은 변수, 함수, 객체 등의 데이터 타입을 명시적으로 지정하는 것을 말함 )
본래 typescript 개발팀은 더 나은 개발도구를 위해 코드에디터(vscode의 전신)개발을 시작했으나, 그 과정에서 디버깅에만 너무 많은 시간이 들어가게 되고 그것이 주로 type관련 오류라는것을 발견함.
디버거에만 의존하는 대신 컴파일러와 같은 도구가 코드 실행 전에 이러한 문제를 찾는것에 도움이 될것이라는 생각으로 typescript가 개발됨.
typescript는 javascript의 super set(상위집합)
으로 모든 자바스크립트 코드는 typescript에서 동작하며 구문적으로 유효하다.
크로스 플랫폼 : typescript는 js가 실행되는 모든 플랫폼에서 실행된다.
객체 지향 언어 : typescript는 클래스, 인터페이스, 모듈과 같은 강력한 객체 지향 프로그래밍 기능을 제공한다.
정적 타입 검사 : typescript는 정적 타이핑을 사용하며, 이것은 타입 주석(type annotation)을 통해 수행된다. 이것을 통해 컴파일 시에 타입 검사가 가능하고, 스크립트를 매번 실행하지 않아도 코드 작성 중에 오류를 찾을 수 있다.
chatGPT : 정적 타이핑(static typing)이란 변수와 함수의 타입을 코드 작성 시점에서 명시하고, 이러한 타입 정보를 바탕으로 컴파일 시에 타입 검사를 수행하는 것을 의미합니다.
타입 추론 : 변수가 타입 없이 선언되면, 그 값에 기반하여 타입을 추론하는 기능을 제공한다. (그래서 명시적으로 작성하지 않았어도, ts가 추론한 타입과 맞지 않은 값을 할당하려고 하면 오류가 발생한다.)
선택적 정적 타이핑 : ts의 정적 타입은 선택적이다. (타입 검사를 필요로 하는 곳에만 넣어도 된다.)
DOM 조작 : js처럼 ts도 DOM을 조작하는데 사용 할 수 있다.

컴파일 : 코드가 실행 되어 기계언어로 변환하는 컴파일 과정에서 오류를 찾을 수 있기 때문에, 코드가 실행 되기 전에 오류를 찾을 수 있다.
기본적으로 인터프리터
JavaScript는 기본적으로 인터프리터 방식으로 동작합니다. 이는 JavaScript 엔진이 코드를 한 줄씩 읽고, 해석하고, 즉시 실행한다는 것을 의미합니다. 이 방식은 코드가 실행되기 전에 전체 프로그램을 컴파일할 필요가 없기 때문에 빠른 시작 시간과 높은 유연성을 제공합니다.JIT 컴파일
현대 JavaScript 엔진 (예: Google의 V8, Mozilla의 SpiderMonkey, Microsoft의 Chakra)에서는 인터프리터 방식만으로는 부족한 성능을 보완하기 위해 JIT 컴파일을 사용합니다.(인터프리터는 한줄씩 코드를 읽고 실행하는 과정에서 매번 기계어로 변환하기 때문에 실행이 오래걸린다.) JIT 컴파일은 코드가 실행되는 동안, 특히 자주 실행되는 코드 부분(핫스팟)을 감지하고, 이를 네이티브 기계어로 컴파일하여 실행 성능을 향상시키는 기술입니다.
동적 타입 언어는 변수 타입이 런타임에 결정된다. 개발이 빠르고 유연하지만 프로젝트 규모가 커지면 오류를 잡기 힘들어진다. 실행하기 전까지는 오류를 잡을 수 없기 때문이다.
자바스크립트는 처음에 웹페이지에 간단한 기능을 넣기 위해 만들어진 스크립트 언어였다. 그러나 좀 더 인터랙티브한 웹사이트의 필요성이 생기면서 자바스크립트 생태계가 커지기 시작하고 프론트의 요구 사항이 많아졌다. 그러면서 동적타입이라는 것이 문제가 생겼다. 작은 프로젝트라면 괜찮지만 규모가 매우 크거나 장기 프로젝트라면? 오류를 잡기 점점 힘들어진다. 그래서 타입스크립트의 필요성이 높아진거다.
새로운 언어를 만들기에는 자바스크립트의 생태계가 이미 커졌고, 브라우저에서 모두 자바스크립트를 사용하고 있기 때문에 새로운 언어를 만들기보다 자바스크립트의 오류를 잡는 방식으로 발전했다.
그래서 타입스크립트를 이용해서 실행전에 컴파일을 한번 한다. 그래서 실행이 되기 전에 = 런타임 에러를 사전에 방지 할 수 있다. 또 타입이나 데이터 형식을 표시하면 협업하는 개발자에게 의도를 전달하기 좋다. 러닝커브가 있고 초기 설정이나 컴파일 시간이 추가되고, 코드가 길어진다는 단점이 있지만 그것들을 이길만큼 장점이 분명하다. 러닝커브도 초반에는 거의 없다고한다. 타입스크립트 중급 이상쯤? 이 되면 그때부터 좀 알아야 하는게 생기지만 그것도 어렵지 않고, 초반에는 매우 쉬우니 그냥 우선 사용하면서 익히는걸 권장한다.
자바스크립트에서 타입스크립트로 변환시키는 것은 점진적 마이그레이션이 가능하다. 모든 자바스크립트 코드는 타입스크립트 = 슈퍼셋이라고 했다. 그래서 천천히 일부분씩 변경해도 문제가 없기 때문에 기존 프로젝트에 적용하는데도 문제가 없다.
라이브세선 질문 1) 파이선도 동적타입언어인데, 머신러닝, ai등 많은 분야에서 사용되고있다. 그래서 파이선은 동적인데 안정적이어서 많이 쓰나요? 하고 물어봤더니 그건 아니라고 하셨다. 결국에 정적 타입 체킹을 도입했다고 한다.
질문 2) 컴파일이 추가되면 브라우저에서 실행 시간이 늘어나나요? 라고 물어봤는데, 정확하게 말해서 실행전에 컴파일 되는거기때문에 실행시간이랑은 상관이 없다고 한다.
ts를 js로 바꾸는걸 컴파일이라고 하는데 정확히는 트랜스파일링이다. ts로 파일을 짜면 그걸 신문법 js로 바꾸고 그걸 다시 바벨이 구문법 js로 바꾼다. 각종 웹브라우저에서 모두 호환되게 하기 위함이다.
또 리액트 같은거 사용해서 프로젝트 만들면 js파일이 되게 많고 그걸 브라우저에서 접속해서 누군가 다운받으면 bundle.js해서 받는다. 이렇게 파일을 하나로 만들어주는게 번들링이다.
하고자하는 말이 뭐냐면 컴파일에 바벨도있고, 웹팩도 있는데 그 안에 tsc가 하나 더 껴서 컴파일 시간이 늘어날 수 있지만 유의미하게 느려지지는 않는다.
node.js : ts의 컴파일러 tsc는 node환경에서 동작하기 때문에 node.js가 설치되어 있어야 한다.
typescript : https://www.typescriptlang.org/ko/download/ 들어가보면, 글로벌로설치 or 프로젝트마다 설치할 수 있는 두개의 방법을 알려준다.
tsc -v : 설치 후 터미널에서 버전을 확인해서 버전이 명시되면 설치가 잘 되었다는 뜻이다.

ts를 설치하고, index.ts파일을 하나 만들고 함수도 만들었다. 위 코드는 parameter의 type이 반드시 string이어야 한다고 type annotation으로 명시하고 있다.
터미널로 돌아와서 tsc index.ts를 실행하면 ts코드를 js코드로 변환하는 컴파일 과정이 실행된다. (브라우저나 node.js에서는 결국에 javascript만 실행 될 수 있기 때문에 ts를 js로 변환시키는 과정이 필요하다. tsc는 TypeScript Compiler를 뜻한다. )
실행 결과 파일 명이 같은 js파일을 만든 것을 확인 할 수 있다.
해당 파일을 실행해보면 함수가 잘 동작하는걸 볼 수 있다.
ts파일은 실행되지 않는다.
추가로 몇개의 예제를 살펴보자. 타입스크립트의 정적 타입 체킹에 대한 예제이다.
js의 경우 처음에 숫자를 할당하고 문자를 할당해도 문제가 없지만, ts의 경우 string을 할당했다고 오류가 난다.
js의 경우 값을 가지지 않는 null에 마침표연산자를 사용해도 오류가 나지 않지만, ts의 경우 오류를 발생시킨다.
js의 경우 프로퍼티에 오류가 나도 오류를 발생시키지 않지만, ts의 경우 바로 오류를 표시한다.
js의 경우 파라미터를 하나 덜 넣어도 오류가 나지 않지만, ts의 경우 오류가 난다. 또한 어떤 오류가 발생했는지를 확인 할 수도 있다. ( 위의 예제들도 마찬가지 )
이렇게 함수명의 괄호 옆에 타입을 지정하면 해당 함수가 반환할 값의 타입을 명시 할 수 있다. return이 없는 함수는 undefined를 반환하게 되고, js는 이것을 오류가 없지만, ts는 string을 반환해야 하는데 undefined를 반환하고 있기 때문에 오류가 난다.
빈 값이라도 리턴하게 하면 오류가 발생하지 않는다.
클래스도 값을 하나 쓰지 않아도 오류가 나지 않지만, ts는 바로 난다. 타입도 선언해야하고, 사용도 다 해야한다.
일치연산자가 아니라 동등연산자를 사용하면 js는 자동형변환을 해서 두값을 비교하지만, ts는 안봐준다.
js는 배열에 문자열만 넣다가 숫자를 넣어도 문제 없다. ts는 암시적으로 자기가 이 배열은 string이 들어온다고 타입을 지정했는데 숫자가 들어왔다고 오류를 발생시킨다.
디스트럭처링 할 때, js는 값이 하나가 없어도 오류가 안나지만, ts는 오류를 발생시킨다. 일단 type을 명시하라는 오류이며
타입을 명시해도 오류가 난다.
배열의 길이가 3이기 때문에 index가 3인 요소에 접근 할 수 없다는 메세지이다.
타입스크립트는 자바스크립트에 타입을 더한거라고들 한다. 즉 타입스크립트는 타입 시스템을 기본으로 동작한다.
데이터의 종류를 구분하는 분류 체계를 뜻한다. 데이터가 메모리에 어떻게 저장되고, 어떻게 처리되어야 하는지에 대한 규칙을 정의해서 **프로그램의 안정성과 신뢰성을 높이는 것이 가장 큰 목적이다.
-> 쉽게 말해서, 값이 어떤 데이터 타입이어야 하는지를 모두 지정해서 오류가 발생할 확률을 낮추겠다는 소리다.
핵심개념
타입 안정성 type safety : 프로그램이 예상치 못한 방식으로 데이터를 해석하는 오류를 방지.
타입 추론 type inference : 명시적으로 타입을 지정핮 ㅣ않아도 컴파일러가 자동으로 데이터 타입을 결정하여, 코드의 간결성을 높이면서도 타입의 안정성을 유지 할 수 있음.
정적 vs 동적 타입 static vs dynamic
-> 정적 타입 : 컴파일 시에 타입이 결정되며, 런타임에는 변경되지 않아서 코드가 실행 되기 전에 타입 오류를 잡을 수 있고 런타임에 오류가 발생하지 않아 안전성이 높음 = ts
-> 동적 타입 : 런타임에 타입이 결정됨. 유연하지만 실행 되어야 오류를 찾을 수 있음 = js
제네릭 generics : 타입 매개변수(ex. <T>)를 사용하여 다양한 타입에 대해 동작하는 코드도 만들 수 있음.
무슨소리냐면 위와 같음. identify 함수는 인자로 어떤 값을 받게 될지모르니까, 함수를 호출하는 시점에 그것을 정하겠다 라는 뜻이다.
결론적으로 위와 같은 타입시스템을 사용해서 타입스크립트를 작성하면, 컴파일을 통해서 자바스크립트를 생성하고 코드를 실행한다.
무슨 뜻이냐면, 컴파일 되어 생성된 js파일에는 type annotation이 존재하지 않는다.위처럼 컴파일을 tsc를 사용해서 직접 컴파일 하지 않고 tyscript의 공식 홈페이지에서 제공하는 playground기능을 사용해도 살펴 볼 수 있다.
왼쪽이 ts코드이고 오른쪽이 컴파일된 js코드이다. (interface는 객체의 구조를 정의하는 방법이다. 객체가 가져야 할 속성 및 메서드를 명시적으로 선언 한다.)
위와 같이 코드가 변환되는 것은 앞에서 말한 1,2,3의 과정을 거쳐서 이뤄진다.
앞에서 ts는 컴파일되어서 js파일을 만든다고 했다.
이때 어떻게 컴파일 될것인지 설정값을 줄 수 있고, 그것은 tsconfing.json파일을 통해서 이뤄진다.

왼쪽이 생성된 설정 파일인데, 터미널에서 생성됐다고 알려주는 값들만 주석처리가 안되어있고 나머지는 다 주석처리 되어있다.
다 없애고 나면 이렇게 남는데, 타입스크립트 프로젝트를 시작하기에 알맞은 옵션만 기본적으로 활성화 되어 있다고 보면 된다. 하나씩 살펴보자target : 자바스크립트가 호환되는 es버전 설정
module : 모듈 코드의 종류를 지정. 위는 기본적으로 commonjs가 지정되어 있는 모습. (require/export 사용)

바로 아래의 설정덕에 ts에서도 import를 사용 할 수 있음. 모듈을 es2015로 바꾸고, tsc로 컴파일해서 index.js를 보면 여전히 import를 사용하고 있음.
하지만 commonjs로 다시 바꾸고 tsx하면 위와 같이 컴파일됨.
esModuleInterop : commonjs를 좀 더 쉽게 가져오기 / 내보내기를 할 수 있도록 함. (import / export도 사용 가능)
forceConsistentCasingInFileNames : 파일명에서 대소문자를 구분
strict : 엄격 파일 검사
skipLibCheck : 타입 정의 파일, 타입 선언 파일이라고 불리는 .d.ts파일에 대한 검사를 생략할거냐는 의미. (node_moduels도 포함) 그래서 전체 컴파일 시간을 줄이지만, 만약 그 파일에 오류가 있으면 감지하지 못하게될 위험이 있음.
outDir :
컴파일된 js파일이 어디에 저장될지를 지정 할 수 있음.
tsc를 하면 dist폴더에 생성된 모습
include :
반대로 include는 컴파일 할 소스파일을 어디서 가져올거냐고 지정할 수 있음. 위와같이 하면 src폴더 내의 모든 폴더와 파일을 컴파일 한다는 뜻.
src폴더 내의 파일이 dist폴더로 컴파일 된 모습
noUncheckedIndexedAccess :
기본값은 false, true가 되면 객체나 배열의 인덱스로 요소에 접근 할 떄 좀 더 엄격한 타입 검사를 진행한다는 의미.
꺼져있으면 오류가 발생하지 않지만,
켜져있으면 오류를 발생시킴 -> 사용하는게 좋다. ( strict와 noUncheckedIndexedAccess는 웬만하면 true로 사용해라 )
lib :
기본으로 사용할 라이브러리 지정. 타입스크립트는 기본적으로 dom이나 es6와 같은 표준 내장 객체와 그에 따른 함수의 타입 선언을 모두 포함하지만, 명시적으로 위처럼 ES5라고 하면 ES5만 인지하게 됨.
그래서 es6의 문법인 endsWith를 사용하면 인식하지 못함.(마지막 글자가 내가 지정한 글자와 일치하느냐) 만약에 DOM도 추가하고 싶으면 ES5뒤에 DOM도 그렇고 일일이 다 추가해줘야 함으로, 다른 라이브러리들은 사용할 수 없게됨. 정말 필요할 때만 써라
타입스크립트의 문법으로 변수, 함수, 객체 등에 명시적으로 타입을 지정하는 방법을 말하는 타입주석은 기본적으로 string, number, boolean, null, any등이 있다.
string에 Annotation을 다는 방법은 위와 같다. 문자열이기 때문에 문자열 관련 메서드를 다 사용 할 수 있고, 문자가 아닌것을 할당하려하면 오류가 난다.

숫자형도 마찬가지다. 숫자가 아닌것을 할당하려하면 오류가 나고, 숫자 메서드를 사용 할 수있다. 자바스크립트에서 숫자를 표현하는 부동소수점 숫자를 모두 사용 할 수 있다(3.4e-5는 지수표현) 숫자끼리 하는 연산도 가능하며, infinity와 nan도 숫자로 취급한다.
true나 false를 값으로 가지고 조건문이나 논리연산자에도 사용 할 수 있다.
null도 타입이다. 첫줄은 유니언타입이라고 해서, 여러 데이터 타입을 가질 수 있는 경우를 말한다. user는 문자나 null을 받을 수 있고 null로 초기화됐다.
그래서 login, logout에 의해서 문자나 null로 모두 할당 될 수 있다. null은 이렇게 유니언타입에서 종종 사용된다.
주의할점은 undefined는 할당 된 적이 없다 <-> null은 값을 일부러 할당하지 않았음 이라는 뜻으로 두 타입은 다르니 상황에 맞게 쓰고 호환되지 않는다는 것을 기억해야한다.

any는 어떠한 타입의 값도 할당이 가능하고, 어떤 메소드도 호출이 가능하다는 의미이다. 하지만 이것을 사용하면 타입의 안정성이 감소하고 타입스크립트를 사용하는 의미가 퇴색되기 때문에 쓰지 않는것을 추천한다.
배열은 위 두가지 방법으로 나타낼 수 있다. 타입스크립트에서 배열은 배열의 모든 요소가 동일한 타입을 갖도록 명시하고, 배열 내부 요소의 타입 안정성을 제공한다. 다른 타입의 값을 넣으려고 하면 정적타입체킹이 동작해서 오류가 난다.
객체의 각 속성과 해당 속성의 타입을 명시적으로 정의해야한다. 없는 값에 접근할 수 없다. 위와 같이 객체 리터럴로 객체를 표기할 때는 값을 줘서 초기화를 하고 나서 값에 접근해야 오류가 나지 않는다.
정의하지 않은 값을 추가할 수도 없지만, 정의한 값이 부족해서도 안된다.
처음에 한 것처럼 타입을 명시하지 않고도 객체를 만들 수 있다. 이럴 경우에 타입추론으로 ts가 타입을 추론하여 만들어낸다.
생성한 객체명에 마우스를 올려보면 타입추론으로 타입이 지정되어 있는걸 볼 수 있다. 마찬가지로 타입에 어긋나지도, 없는값을 넣을수도, 하나가 부족해서도 안된다.
users객체의 age key에 ?가있다. ?를 붙이면 optional property가 된다. 말그대로 해당 키값은 옵션이 된다는 뜻으로 객체를 생성할 때 해당 값을 넣지 않아도 오류가 나지 않는다.
key 값 앞에 readonly를 붙이면 값을 수정 할 수 없게 된다. 주로 환경 설정 객체를 만들 때 사용한다.
이 앞에서 한 객체 리터럴 방식은 편리하지만 재사용성이 낮다.
type alias 타입별칭을 이용하면 비슷한 객체를 여러개 만들 수 있다. 당연히 값이 부족해서도 안되고 더할수도 없다. 미리 생성한 alias와 똑같이 만들어야 한다.
user1의 type은 UserType이 된다. UserType[]=[]은 UserType만을 요소로 갖는 배열이며 초기값은 빈배열이라는 뜻이다. optional이나 readonly도 type alias에만 추가하면 이것에 의해 만들어진 객체들도 모두 설정을 따르게 된다.
중첩객체의 경우에도 그냥 위처럼 표기하면 된다.
함수의 파라미터와 반환값에 타입을 지정한다. 예상치 못한 인자가 전달되거나 타입의 값이 반환되는것을 방지한다.
자바스크립트에서는 위와 같은 방식으로 함수를 만든다.
함수를 만들고 함수명 위에 마우스를 올리면 보이는 정보를 시그니쳐라고한다. 타입을 지정해주지 않으면 시그니쳐만으로 해당 함수의 목적을 정확히 유추하기가 힘들다. add 함수가 숫자를 더하는건지, 문자를 더하는건지 확실하지 않기 때문이다. 위 함수는 타입을 별도로 지정하지 않아서 ts가 any type을 지정했고, 그래서 숫자든 문자든 어떤것도 인자로 받을 수 있다.
이처럼 모두 number로 지정하면 숫자형만 arg로 전달 할 수 있게 된다.
시그니쳐도 변경되어서 함수의 의도를 파악하기에 명료하다.
결과를 변수에 담으면, add함수는 number를 return하기 때문에 result의 type도 number로 추론된다.
파라미터와 매치되는 갯수의 아규먼트를 전달해야한다고 알려주기도 하고, 당연히 더 많은 값을 전달해도 오류가 난다.
디폴트 값을 주면 아규먼트로 전달하지 않아도 오류가 나지 않는다.
옵셔널 파라미터로 설정할 수 있고, 값이 오지 않을 경우에 어떻게 할지 조건을 줄 수도 있다. 위 코드는 quantity가 존재하지 않으면 1을 반환한다.
contextual typing은 함수가 인자로 사용되는 경우 = 콜백함수인 경우에, 개별요소의 타입에 대해서 ts가 타입을 추론한다는 뜻이다.
numbers배열 안의 item들은 number로 type이 지정되어있다.
그래서 해당 타입에 맞는 메서드들을 추천해주고, 사용할 수 있다. 이것도 ts의 장점이다. 문자열타입에 숫자형메서드를 쓰거나 하는 사고를 방지 할 수 있다.
맨첨에도 잠깐 나왔지만, 소괄호 오른쪽에 type을 작성하면 해당 함수가 반환할 값에 대한 type을 지정 할 수 있다.

함수가 어떤 값을 리턴하는게 아니라, api 요청이 될 수도 있고 위처럼 콘솔에 찍는 간단한 함수일수도 있다. 이런 경우에 함수는 자체적으로 undefined를 반환하게 된다. 그럴 경우 의도적으로 값을 리턴하지 않는다는 것을 표기하기 위해서 void와 never를 사용한다.
never은 사용하는 경우가 많지 않은데, 끝없이 돌아가야 하는 함수이거나 위의 예시처럼 에러처리를 하는 경우에 사용한다. void보다 never가 좀 더 강력한 표현인데 일반적인 애플리케이션을 개발하는 경우에는 잘 사용하지 않는다.
유니언은 변수나 함수가 여러 타입 중 하나를 가질 수 있도록 하는 타입이다. 서로 다른 타입의 값을 하나의 변수에 할당 할 수 있으며 |를 사용하여 나타낸다.
기초적인 사용법은 위와 같다. userId는 string이나 number type의 값을 모두 할당받을 수 있다.
함수의 파라미터에도 사용 할 수 있는데
이 경우 type narrowing 타입 좁히기를 해줘야한다. 무슨말이냐면 위 함수에서 value는 number or string이다. 그런데 리턴에서 사용한 .toUpperCase()는 문자열타입에서만 사용가능한 메서드임으로 value가 숫자가 되면 오류가 발생할 수 있다.
그래서 이렇게 각 경우에 대한 처리를 모두 해줘야한다.

배열에 사용하는 경우 위 두개의 동작방식은 다르다. string | number[]은 배열이 문자형만 가지거나 혹은 숫자형만 가지는 배열이거나 라는 뜻(string[] or number[])이고, (string | number)[]처럼 소괄호로 묶으면, 하나의 배열안에 요소가 숫자 or 문자형 모두 type으로 가질 수 있다는 뜻이다.
소괄호를 빼면 오류가 난다.
리터럴은 타입과 값을 한번에 지정하는 것을 말한다.
id는 문자열1을 값이자 타입으로 가진다. 그래서 문자형이라도 다른 값을 id에 할당하려하면 오류가 난다. 이것이 가장 기초적인 리터럴 타입의 사용 방법이고, 리터럴은 보통 유니언과 함께 사용된다. 실무에서 많이 사용하는 방법이다.
파라미터인 option의 리터럴을 on or off로 지정했다. 시그니쳐를 보면 type도 on or off로 확인된다. option은 해당 두 리터럴만 값이자 타입으로 가질 수 있다.
포함되지 않는 리터럴을 사용하려하자 오류가 난다.
객체를 만들 때 type alias로 만드는 방법에 대해서 공부했는데, 사실 객체만 만들 수 있는 방법은 아니다.
type alias는 특정 타입을 직접 만들고 거기에 이름을 붙여서 사용하기 편하게 하는것이 본질이다. 
즉 type alias로 만들면 그것이 하나의 타입이되고 어떤 값의 타입으로 지정할 수 있다. 또 지금 공부하고 있는 주제인 union과 함께 사용 할 수 있다.
사용법은 위와같이 type 키워드를 사용하는거다. 위 코드는 type alias + union + 리터럴을 함께 사용한 모습이다.
setSize 함수의 파라미터의 타입을 Size로 지정했다. size는 Size에 지정된 타입만을 가질 수 있다.
위와 같은 방식은 switch문과 함께 자주 사용된다.
혹은 이렇게 특정 통신 코드나 환경 변수와 같은 고정된 값들 중 하나를 선택해야 할 때도 사용한다.
객체의 구조 / 형태를 정의한다. 특정 객체가 어떤 property, method를 가져야 하는지를 나타낸다.
그로인해 만들어지는 클래스나 객체가 어떤 구조를 가져야 하는지에 대한 설계도(가이드라인)이다. 확장 및 조합에 용이하다.
사용방법은 위와 같다. interface라는 키워드를 사용하고 객체의 형태를 작성하면 된다. someMethod()는 값을 반환하지 안흔 함수라서 void type을 지정했다.
타입별칭과 interface가 비슷하게 느껴질 수 있다. type alias도 객체의 구조를 명시할 수 있기 때문이다.
하지만 type alias는 객체뿐 아니라 원시값, 리터럴, 함수 등 다양한 타입을 나타내며 type typa명 = {}과 같은 형태를 가진다.
반면에 interface는 객체와 함수(함수도 객체다.)만 나타낼 수 있으며 interface interface명 {}와 같이 사용한다.
그러면 interface로 나타내는건 type alias로도 나타낼 수 있는데 굳이 왜 사용해야 하느냐 하는 의문이 남을 수 있는데 https://tecoble.techcourse.co.kr/post/2022-11-07-typeAlias-interface/ 이 글을 보면 정리가 잘 되어있다.
해당 글의 정리에 따르면 가장 큰 차이점은 interface는 새로운 프로퍼티에 대해 열려있다는 점이다.
왼쪽과같이 interface로 선언했을 때에는 똑같은 이름으로 다시 선언해도 오류가 나지 않는다. window는 나중에 선언된 타입의 프로퍼티를 추가로 포함하게 되지만, 오른쪽과같이 type으로 선언한 경우에는 똑같은 이름을 사용하면 오류가 난다. 즉 type은 새로운 프로퍼티를 추가할 수 없다. 이것을 선언병합이 가능하다 or 불가능하다 라고 표현한다.
이렇게 중복 선언이 불가능한 type alias가 존재하는 이유는, 타입이 변할수도 있다는 생각을 하면서 개발을 하는 것이 고려할 사항이 더 많아지기 때문이다. 마치 let, const와 같다고 생각하면 될 것 같다.
주의할 점은 중복으로 선언해서 객체의 값을 추가하는게 불가능하다는 것이지 둘 다 특정 방법을 사용해서 확장하는게 가능하다는 점이다.
인터페이스는 extends를 이용해서 확장가능하며 타입별칭은 &을 이용하여 확장이 가능하다. employee는 확장된 타입을 모두 가져야 한다.
하나 이상을 확장할 때는 위와 같이 사용한다.
readonly와 optional property도 모두 사용 가능하다.
배열의 각 요소의 타입을 지정해서, 고정된 크기의 배열을 만들 때 사용하는 방법이다. 순서가 중요하며 자바스크립트에는 존재하지 않는 개념이다. 원래 자바스크립트의 배열은 각 배열 요소의 타입이나 크기에 제한이 없다.
기본 사용 방법은 위와 같다. 위에서 let을 사용한건, type만 지정하고 myTuple자체는 빈 값으로 둔 후에 나중에 값을 할당하기 위함이다. const로 만들면 초기화까지 한번에 해야한다.

지정한 형식과 다르게 입력하면 오류가 발생한다.
예시1 처럼 함수가 반환할 배열값의 형태를 지정하고, 그것을 디스트럭처링 할 수도 있고 예시2처럼 타입별칭과 함께 사용해서 0번, 1번 인덱스에 항상 어떠한 타입의 값이 오도록 만들 수도 있다. 이렇게 작성하면 항상 해당 배열의 각 인덱스에 어떤 값이 오게 될지 예측 할 수 있다.
위와 비슷하지만, 타입별칭으로 만든 Coord값만을 배열의 값으로 받는 coords를 만들수도 있다.
반복문을 할때도 디스트럭처링 + 튜플을 함께 사용 할 수 있다.
열거형인 이넘은 관련된 상수들의 집합을 정의할 수 있는 특별한 자료형이다. 여러 상수 값들을 그룹화 하는것이 목적이며 자바스크립트에 존재하지 않는 개념이다.
기본적인 사용방법은 위와 같은데, 상수값 1,2,3은 enum의 멤버라고 부르며, 각 멤버들마다 값이 존재한다.
위처럼 특정 값을 할당하지않으면 인덱스처럼 0번부터 시작해서 1씩 증가하는 값을 각 멤버가 가지게 된다.
각 멤버에 마우스를 올려보면 0부터 2까지 할당되어있는 것을 볼 수 있다.
이렇게 임의로 값을 주면 다음 값들은 자동으로 2, 3이 된다.
이렇게 각기 다른 값을 줄 수도 있지만, 실무에선 거의 사용하지 않는다고 한다. 위와 같은 경우를 숫자형 enum이라고 한다.
위의 예처럼 사용하면 된다. curretnState의 타입을 enum으로 지정하면, curretnState는 PlayerState로 지정한 값만 받을 수 있다.
문자형 enum은 말그대로 상수들의 값을 문자로 명시하는것을 말한다. 숫자형에비해 봤을때 예측이 쉬워서 디버깅이 쉽다. 숫자형의 경우 enum의 크기가 커져버리면 중간에 값들을 예측하기 힘들 수 있다.
switch문과 많이 사용한다.
타입스크립트의 강력한 기능 중 하나로, 변수나 표현식 등을 자동으로 컴파일러가 검사해서 타입을 결정하는 프로세스이다. 개발자가 명시하지 않아도 코드의 문맥을 바탕으로 적절한 타입을 추론한다.
변수의 타입추론 예시이다. 위와 같이 값을 할당하고 타입을 지정하지 않아도 타입추론에 의해서 타입이 지정된다. 처음에 할당한 값과 다른 타입의 값을 할당하려하면 오류가 난다.
함수의 타입추론 예시이다. 함수 리턴값의 타입을 명시하지 않았지만, 타입추론에 의해서 number타입으로 지정됐다.
배열이나 객체도 마찬가지다. 시그니처로 어떤 타입이 지정됐는지 확인 할 수 있다.
유니언타입도 마찬가지다.
컴파일러에게 개발자가 직접 변수나 객체가 가진 타입에 대해 알리는 방식으로, 개발자가 해당 타입에 대해 더 많은 정보를 가지고 있는 경우에 사용한다.
실제로 변수의 타입을 지정하는게 아니라 컴파일 시점의 타입체킹에만 적용이 된다.
someValue는 unknown타입이다. 말 그대로 어떤 타입이 오게 될지 모른다는 뜻이다. 어떤 타입이든 될 수 있는 any와는 다르다. 하지만 값은 문자열로 할당을 했다. 그런데 type이 unknown이기 때문에 ts는 length메서드를 사용 할 수 없다고 오류를 낸다.
위처럼 소괄호로묶고 as string을 하는것이 타입 단언이다. 그러면 해당 코드라인에서는 someValue를 string취급을 하기 때문에 length프로퍼티를 사용 할 수 있다. 실제로 변수의 타입을 지정한게 아니라는 의미도 위 코드에서 살펴볼 수 있다. someValue의 타입을 변경한게 아니기 때문에 다음줄에서 다시 length메서드를 사용하려하면 오류가 난다.
이렇게 ts는 unknown에 대해서 타입좁히기를 하기 전까지는 구체적인 연산을 할 수 없도록 하기 때문에 이런 상황에 대처할 수단으로 타입단언을 사용할 수 있다.
여기서 그럼 그냥 someValue의 타입을 string과 같이 직접 지정하면 되는거 아닌가..? 하는 의문이 들었는데 민수님한테 물어봐서 답을 들을 수 있었다.
위처럼 간단한 코드가 아니라, 예를들어 통신으로 어떤 값을 받아와야 할 때 현재 그 값이 어떤 상태인지 모를 수 있다. 그런데 나는 코드를 작성해야하니까 일단 unknown으로 받아오고, 여기서 조건문을 사용하는 등 해서 일단 값이 문자면 as를 써서 처리를 하면 되는거다.
타입 단언은 특히 html요소에 접근 할 때 많이 사용한다.
getElementById는 실제로 위와 같이 HTMLElement를 반환하거나 해당 요소가 존재하지 않으면 null을 반환한다. 그런데 button태그에는 disabled속성이 있는데, 일반적인 HTMLElement에는 disabled속성이 없을 수도 있기 때문에 오류가 난다.(강사님이 말하기로는, 실제로 속성이 없어서 문제인게 아니라 ts에서 제공하는 HTMLElement에 없을 수 있다고 한다.)
이럴경우 내가 ts보다 해당 코드에 대한 정보가 더 많다고 판단하고, 타입추론에 맡기는 것이 아니라 직접 타입을 지정하면 된다. 위처럼 as HTMLButtonElement라고 타입단언을 하면 disabled속성을 사용 할 수 있게 된다.
그냥 타입을 직접 지정해주면 되는거 아닌가? 했는데 안되더라. gpt한테 물어봤더니 getElementById자체가 ts에서 HTMLElement를 반환하도록 되어있기 때문에 안된다고 한다. as를 써야한다. 이렇게 내가 쓰는 메서드가 번환하는 타입이 내가 원하는 타입과 다를 때 사용 할 수 있다.
주의할 점은 만약 button이 존재하지 않으면 null을 반환하게 되니까 그에 대한 처리도 해줘야 한다.
객체를 생성하기 위한 하나의 청사진 역할을 하는 클래스는 es6의 문법과 ts의 문법이 대체로 같고, type이 추가된다.
기본적인 es6의 클래스 사용법은 위와 같다. 여기서 필드는 객체의 상태값을 의미한다. (변수)
ts의 기본적인 class 사용법은 위와 같다. es6와 같이 클래스를 만들고 클래스에 기반한 인스턴스를 만들면 된다. 각 필드는 타입이 존재하고 그에 맞는 값을 할당할 수 있다.
인스턴스를 만들면서 값을 할당하려면 constructor생성자를 사용하면 된다.
컴파일 하고나서 실행까지 해보면 잘 만들어져 있는 걸 볼 수 있다.
상속은 이렇게 extends 키워드를 사용해서 하면 된다. 이때 부모클래스의 constructor가 요구하는 값들을 super로 전달해줘야 한다. getInfo()를 통해서 모든 값을 반환하여 확인할 수 있다.
이 다음부터는 ts에서 추가된 기능이다. js에는 없다.
위 3개를 접근제어자 라고 한다. 클래스의 멤버들에 대해서 접근성을 제어하는 것을 말한다. 따로 명시하지 않으면 기본적으로 클래스 내부의 변수나 메서드에 자동으로 public이 지정된다.
그래서 class객체 외부에서도 클래스 내부의 값에 접근하여 참조 할 수 있다.
private으로 지정되어 있으면 클래스 내부에서만 접근할 수 있다. 위 코드에서 name을 private으로 지정했다. 그래서 getInfo에서 name을 참조하는데는 문제가 없다. 하지만 외부에서 country.name을 하려하면 오류가 발생한다. 프라이빗이기 때문이다.
만약 부모 class에서 continentName을 private으로 지정하면, 외부요소이자 자식요소인 Country class에서는 참조할 수 없기 때문에 getInfo에서 참조하려하면 오류가 난다. getContinentName은 부모클래스의 메서드이기때문에 참조할 때 오류가 나지 않는다. 하위클래스에서는 getContinentName으로만 continentName을 참조할 수 있다.
#으로 private를 대체 할 수 있다. 같은 의미다. 오류가 나는 이유는 이 문법이 ES6부터 가능하기 때문이다. tsconfig에서 target을 수정해줘야 한다.
수정해주고 name앞에 다 #을 붙여준다.
protected는 자신과 자신을 상속한 하위 클래스까지 접근이 가능하게 하는 옵션이다. 그래서 getInfo에서 접근해도 오류가 나지 않는다.
interface로 클래스가 따라야 할 구체적인 구조, 규칙을 정의할 수 있다.
implements라는 키워드를 사용해서 interface를 지정하면 되고, private이나 protected로 되어있는 값은 interface에 포함되지 않는다. interface는 기본적으로 public이기 때문이다. interface에 포함된 변수나 메서드는 class에 모두 구현되어야 한다.
상속된 클래스에 interface를 지정하려면 extends 뒤에 작성하면 된다. 순서를 지켜야 한다.
추상클래스라는게 존재한다. 클래스를 만들 때 abstract키워드를 사용하고, 내부에서도 abstract를 사용 할 수 있다. 이 추상클래스는 인스턴스를 직접 생성하지 못한다.
abstract 클래스를 상속받는 클래스를 따로 만들고 그 클래스로 인스턴스를 만들어야한다. myCountry는 추상클래스를 상속받았다. 추상클래스에서 displayInfo()는 void를 반환한다고만 되어있지 구체적으로 함수의 내용을 정하지는 않았다. 상속을 받는 자식 클래스에서는 이것을 구체적으로 명시를 해야한다.
c#등에서는 제네릭이 존재하지만 원래 js에는 없는 개념이다.
함수, 클래스, 인터페이스 등에 타입을 지정할 때, 타입을 매개변수로 받아서 사용할 수 있다. 즉, 다양한 타입의 데이터를 처리 할 수 있도록 범용적인 패턴을 제공한다는 뜻이다. 뭔말이냐면
위 코드에서 화살괄호 <T>를 포함해서 T라고 작성되어 있는 부분은 원래 type이 지정되는 부분이다. 그런데 해당 함수가 특정 타입의 값만을 인자로 받거나 반환하도록 하면, 함수의 유연함이 떨어진다. 그럴 때 위처럼 제네릭을 이용해서 표시하면, 함수를 사용 할 때 타입을 지정하고 그것을 파라미터처럼 받아서 전달하게 되는거다.
이런식으로 getFirstElement는 number타입에도 사용 할 수 있고, string타입에도 사용 할 수 있다. 위 코드에서 보이는 것 처럼 제네릭을 직접 표시해도되고, 타입추론으로 타입이 지정되게 해도 된다. 위 코드에서 배열의 길이가 없으면 undefined를 반환하도록 해야하는데, undefined는 완전히 다른 타입이기 때문에 return값으로 사용하기 위해서 union을 사용해서 추가했다.
+) 라이브 강사님이 알려주신것. 타입추론으로 값이 들어가게 두는거는 강제성이 없다. 그냥 말그대로 지금 들어왔는 값을 확인해서 리턴되는걸 추론할 뿐이다. 내가 정말 숫자만 받거나 문자만 받고싶으면 제네릭을 써서 명시해줘야한다. 써주는게 좋다!
자바스크립트의 객체에서 key는 무조건 string이지만, value는 어떤 타입이든 될 수 있다. 그래서 이 값의 타입을 유연하게 하고 싶을 때 제네릭을 사용 할 수 있다.
인터페이스를 사용해서 객체를 만드는경우에는 제네릭 타입을 명시해야한다. 위처럼 작성하면 <T>에 전달되는 타입을 값의 타입으로 사용할 수 있다.
만약 값으로 여러개의 타입을 사용하고 싶으면 위와같이 하면 된다. 화살괄호 내부의 문자는 어떤것을 적어도 상관없다. T가 아니어도 되지만 보통 type이라는 뜻의 T를 많이 사용한다. 이전 예시에서는 굳이 key에 string이라고 명시했지만 굳이 안해도 되기때문에 이 예시에서는 쓰지 않았다.
간단한 클래스에 generic을 적용했다. private content는 T or null이다. 인스턴스 생성시에 constructor에 의해서 기본적으로 null이된다. 셋아이템으로 content의 값을 지정하고, 겟아이템으로 값을 확인 할 수 있다.
이렇게 숫자 혹은 문자로 모두 사용 할 수 있다.
interface + class + generic을 모두 사용한 예시이다. UserRepository 클래스는 Store interface의 구조를 따라야함으로 Stor에 있는 메서드가 모두 구현되어 있어야한다. 그리고 Store에서 사용되는 type은 User interface가 된다. findById와 save메서드는 User interface형태의 객체만을 인자로 받거나 리턴할 수 있다.
tsc해서 js파일을 실행해보면 잘 적용된 걸 확인할 수 있다.
Store가 제네릭이기 때문에 이런식으로 User가 아니라 Product로 바꿔서 사용할 수 있다.
제네릭에는 제약을 걸 수 있다. 위와같이 extends키워드를 사용하면 제약이 된다. Store interface는 만드시 WithIn에 명시된 값을 가지고 있어야 한다.
지금 Store는 User interface인데,
User interface에서 id를 지워버리면
바로 오류가 난다. WithId에서 지정한대로 Store에 지정되는 제네릭은 반드시 Id를 가져야 하기 때문이다.
타입스크립트 자체적으로 DOM API의 타입을 제공하고, 경우에 따라서 맞는 DOM type을 선택해야 한다.
strcitNullCheckes를 true로 설정하고 시작한다. null과 undefined에 대해서 좀 더 엄격하게 검사한다. dom type을 다룰 때 나오는 경우가 많기 때문이다.
ts의 타입 정의 파일은 lim.dom.d.ts이 있다. 이 파일을 기반으로 컴파일러가 정적타입체킹을 하고 자동완성 등을 제공한다.
가장 기본적인 dom interface는 HTMLElement다. div나 a태그 등을 다루는 여러 속성과 메서드가 해당 인터페이스에 포함되어있다.
다양한 속성과 메서드가 존재하는걸 볼 수 있다.
주석과 같은 태그가 있다치고, 해당 dom 요소에 접근하는 방법은 위의 두가지가 있다.

타입 추론에 의해서 타입이 지정된다. 메서드에 따라서 다르게 지정된다.
a태그에는 href라는 어트리뷰트가 있지만 접근하려하면 오류가 난다. ts는 실행시점에 타입을 체킹하지 않고, 컴파일 시점에 체킹하기 때문에 현재 myLink가 존재하는지 알 수 없어서 null도 고려한다.
그래서 타입좁히기로 if문을 쓰면 이번에는 href속성이 없다고 한다. a태그와 맞는 타입을 지정해서 해당하는 메서드들을 사용할 수 있도록 해야한다. lim.dom.d.ts에서 모두 확인할 수 있다.
HTMLAnchorElement로 타입단언을 하거나 instanceof를 사용하면 된다. 타입단언을 하면 null을 고려하지 않기 때문에 조건문은 사용하지 않아도 된다.
강의때 제네릭에 대한 언급이 없어서 해봤는데, 제네릭은 null을 고려해야 했다.
모든 dom 메소드가 정확한 타입을 찾지 못해서 타입단언등을 해야하는 건 아니다. createElement은 자체적으로 제네릭 타입이다. 제네릭 타입은 동적으로 타입을 받아서 파라미터나 리턴에 적용하기 때문에 위에서 볼 수 있는 것처럼 해당 dom요소의 인터페이스를 반환한다. createElement에 전달한 문자열과 일치되는 타입으로 반환값이 지정된 모습이다.
querySeletor도 classname이나 id를 사용하지 않고 태그를 전달하면 일치되는 인터페이스를 반환하지만 null과 함꼐 union타입이다.
querySelectAll은 HTMLAnchorElement가 있는 리스트를 반환한다.
for문을 돌면 개별 요소에 대해서도 타입지정을 잘 해준다.
이벤트는 dom에서 가장 많이 쓰이는 것 중에 하나다.
click은 마우스 이벤트이기때문에 e에 타입추론으로 마우스 이벤트 타입이 지정됐다.
그래서 마우스 이벤트의 메서드들을 사용할 수 있다.
마우스 이벤트만이 아니라 키보드 이벤트 등도 추가하고 싶으면 타입을 유니언으로 사용하면 되고 타입좁히기를 하면 된다. 아니며 좀 더 광범위하게 일반화된 Event타입을 사용해도 된다.
변수의 타입을 보다 구체적으로 좁혀나가는 과정으로, 타입 좁히기는 주로 조건문과 타입 가드를 사용해서 이뤄진다.
타입 가드(Type Guard)는 TypeScript 에서 특정 조건을 통해 변수의 타입을 좁히는 기법을 의미하며 주로 사용되는 방법으로 typeof, instanceof, in, 동일성 좁히기, 사용자 정의 가드 등이 있다.
typeof
주로 함수에서 사용된다. 매개변수의 타입이 여러가지일 때 타입좁히기를 통해서 두 경우를 모두 처리해야한다.
동등성 좁히기
변수가 특정 값이나 값의 집합과 같음을 확인하여 타입을 좁히는 방법으로, 비교 연산자를 사용하여 변수가 특정 값과 동일한지 여부를 확인한다.
in
obj in 키워드로 타입좁히기를 하는 경우이다. in 연산자를 사용하여 객체에 특정 속성이 존재하는지 확인한다. 인터페이스나 객체 리터럴 타입을 좁히는 데 유용하다. os는 ios 또는 android타입의 객체이다. os에 어떤 값이 존재하느냐에 따라서 다르게 실행한다.
instanceof
any타입은 초기화를 안해도 되지만 타입이 명확한 경우에는 초기화를 해야해서 빈문자열로 일단 초기화를 했다.
파라미터는 any임으로 어떤 타입이든 될 수 있다. 그러면 내부에서 좁히기를 해줘야한다. instanceof를 사용해서 어떤 클래스로부터 만들어진 인스턴스인지를 확인하고 처리할 수 있다.
사용자 정의 타입가드
타입가드함수는 ts의 특이한 함수다. 위에서 response is ErrorResponse부분이다. 원래 해당 부분은 리턴의 타입이 들어가는데 사용자정의 타입가드를 사용하면 해당 함수는 response가 ErrorResponse인지를 확인하는 함수다 라고 ts에게 정보를 전달한다고 생각하면 된다.
근데 강의 코드를 보면서 들었던 생각인데, 타입 단언을 하는데 굳이 타입가드를 써줘야하나 싶어서 없앴는데 그냥 잘 되더라.
그래서 지피티한테 물어봤는데 타입가드를 사용하지 않으면, 결과는 똑같이 나와도 ts입장에서 함수의 결과가 true라도 response가 ErrorResponse인지를 명확하게 알 수 없다고 한다. 뭐.. 그런 역할을 하나보다.. 하고 넘어가야겠다.. 코드를 작성한 강사님과 대화를 할 수 없으니 정확한 의도는 파악할 수가 없다. 이게 vod강의의 아쉬운 점인것 같다.
참고1, 참고2 그래서 좀 더 찾아봤다. isCar에서 인자로 받은 vehicle이 Car 인터페이스인지 확인하는 함수가 isCar다. vehicle에 브랜드와 스피드가 있으면 Car인터페이스라고 정해준다.
그걸 이용해서 아래 있는 displayVehicleInfo함수에서 타입좁히기에 사용을 하는거다. 그래서 결국에 사용자 정의 타입가드가 왜 필요한지는 모르겠지만.. 그냥 더 분명하게..? 해주는 느낌이다.. ts는 전체적으로 그렇다..
다시 본래 코드로 돌아가보면, 타입단언은 해야한다. 만약 apiResponse가 인자로 들어오면 message가 없어서 오류가 날것이기 때문이다.
식별 가능한 유니언 타입
타입스크립트의 여러 타입 중 하나를 식별할 수 있는 공통 속성을 가진 유니언 타입..? 이라고 강사님이 설명해줬는데 뭔소린지 모르겠다. 보조자료에 의하면 식별 가능한 유니언 타입은 공통된 속성을 사용하여 유니언 타입을 구분하는 방법입니다. 이는 특정 속성 값을 통해 타입을 좁히는 방식입니다.라고 한다.
위 코드에서 객체 두개는 다 type을 가지고 있으니, 어떤 객체가 어떤 속성을 가졌느냐로는 두개를 구별할 수가 없다.
그래서 type 프로퍼티의 값으로 구분을 한다.
기본 타입을 확장하여 복잡한 타입관계와 구조를 표현할 수 있게 해주는 다양한 타입 기능을 알아보자.
TypeScript는 기본적인 타입 시스템 외에도 복잡한 타입 관계와 구조를 표현할 수 있는 다양한 고급 타입 기능을 제공한다. 이러한 고급 타입 기능을 사용하면 더욱 강력하고 유연한 타입 체크를 수행할 수 있다.
교차타입
기본 사용법은 위와 같다. &을 이용해서 두 타입을 결합한다.
이렇게 여러 타입을 만들어놓고 원하는 타입만 골라서 사용 할 수 있다.
조건부타입
밑줄은 이란 무시하고, 삼항연산자다. T가 U를 확장하고 있은면 X를 실행하고 아니면 Y를 실행해라.
result1이 Yes가 됐다. Result2는 No가 된다.
제네릭을 이용해서 인풋에 따라 타입을 바꾸는 방식이다.
keyof
key를 유니언타입으로 만든다.
위처럼 활용 할 수 있다. getProp은 두개의 제네릭을 받아서 파라미터로 전달할 수 있다. obj자체와, obj에서 T의 key중 하나인 z를 전달한다. z는 T의 key중 하나여아함으로 관련없는 p같은게 들어가면 오류가 난다.
맵핑타입
기존 타입을 순회해서 새로운 타입으로 반환한다. 기존 타입의 프로퍼티를 반복해서 새로운 타입을 생성하거나 새로운 값을 추가 한다. 위 코드처럼 사용하면, T를 받아서 T의 key들을 순회해서 해당 타입 객체 안에 넣어주는 것 같다.

옵셔널 타입은 usertype의 키들을 순회하면서 옵셔널로 만들고, 리드온니타입은 리드온니로 만든다. 맵핑이라고 하니까,, 맵함수와 비슷한 것 같다.
타입스크립트에서 제공하는 내장 타입으로, 기존 타입을 변환하거나 새로운 타입을 생성하는데 사용된다. 유틸리티 타입은 코드를 간결하게 작성하고 재사용성을 높이는 데 도움이 된다.
강의에서는 실무에서 자주 사용되는 타입을 소개했고, ts 공식홈페이지에 가면 유틸리티 타입을 모두 확인할 수 있다.
Partial<T>

적혀 있는 그대로 모든 프로퍼티를 옵셔널로 만든다.
Readonly<T>
리드온니로 바꾼다.
Pick<T>
T에서 K만 추출한다.
Omit<T>
T에서 K만 제외한다.
여러개 한번에 지우기 할 땐 유니온
Record<T>
객체의 키와 값의 타입을 정함
객체를 값으로 활용한 예
Parameter<T>
함수의 매개변수타입을 튜플로 추출해서 타입으로 만든다. 함수를 재사용할 때 파라미터도 재사용 가능하게 해준다. someFunction은 숫자를 받아서 실행하는 함수다. 해당 함수를 Parameters의 제네릭으로 전달하면 위에서 보이는 것처럼 함수의 인자를 튜플(배열)로 만들어준다. 그래서 함수 내부에서 구조분해할당해서 사용할 수 있다.
실무에서 자주 사용되고 매우 유용하다.
배열 또는 객체를 변경 불가능한 상태로 만든다. 코드 내에서 해당 배열이나 객체를 변경할 수 없는 안정적인 상태로 만든다.
readonly가 됐다.
저 기능만 있는건 아니다. statusCodeMap객체의 값에 특정 문자열들을 모두 지정을 했는데 ts는 string으로 타입추측을 한다. 다른 어떤 스트랑 타입의 값으로 대체될 수 있기 때문이다.
상수단언을 하면 readonly가 되면서, 어차피 변경되지 않을 것이기 때문에 string이라는 타입이 되는게 아니라 넣어준 문자열로 확정된다.
keyof를 사용해서 statusCodeMap의 키들을 가져와서 사용하기 쉽게 할 수 있다. export하고 있다. = 다른 파일에서 statusCodeMap을 import해서 사용 할 때 어차피 상수단언 되어있으니 객체를 변경할 일은 없고, key를 통해서 값만 가지고 객체를 참조만 하면 되기 때문에 key만 있으면 된다.
typeeof를 사용한건 statusCodeMap가 type이나 인터페이스가 아닌 객체리터럴이니까 type을 가져오는 keyof를 직접 사용 할 수 없다. 그래서 객체의 타입을 가져오고 거기에 keyof를 하는 것이다.
이런식으로 활용할 수 있다. statusCodeMap의 key만 뽑은게 statusCode니까 없는 값에 접근할 위험 없이 메세지를 사용할 수 있다.
타입스크립트에서 에러 처리는 자바스크립트의 기능을 기반으로 한다. 자바스크립트 문법과 똑같이 사용하면되고 에러에 타입을 붙이면 된다.
try / catch를 사용해서 런타임에서 발생하는 에러를 처리할 수 있다.
기본 사용법은 위와 같다.
기본 사용법처럼 error에 타입을 주지 않으면 any타입이 된다. ts에서 any는 타입 검사를 할 필요가 없음으로 ts를 사용하는 의미가 없어진다. 그래서 unknown을 사용한다. unknown은 타입검사가 필요하고, 타입좁히기를 해줘야한다. 이렇게 하면 에러검사를 좀 더 확실하게 할 수 있다.
lib.d.ts파일에서 Error interface도 확인할 수 있다.
에러는 내장객체임으로 위 이미지처럼 브라우저 콘솔에서 확인할 수 있다. 메세지, 네임, 스택을 프로퍼티로 가지고 있다.
그것을 활용해서 내장객체 Error를 상속받는 커스텀 에러 클래스를 만들 수 있다. 위 코드처럼 작성하면 error의 name을 customerror로 만들어 준다. tryCatch에서도 활용할 수 있다.
비동기 처리도 자바스크립트의 방식을 그대로 사용한다. 다만 함수의 반환타입을 Promise<T>로 지정해서 작업의 결과 타입을 명확하게 한다.
js에서 Promise의 기본 사용방법은 위와 같다.
ts에서는 프로미스를 제네릭을 함수의 반환타입으로 지정해준다.
제네릭에 string을 전달해서 반환값 data는 string이다.
async / await으로 수정하면 이렇다.
에러를 더해준다.
앞에서는 string으로 간단하게 했지만, data를 받아올 경우 타입을 정확하게 명시하는게 중요해서 interface로 교체한다.
type과 유틸리티타입을 함께 사용 할 수도 있다.
제네릭을 사용할 수 있다. 리턴값에서 타입단언을 한번 더 해주고 있다.
사실 없어도 되는데, 타입스크립트에서는 가능하면 타입을 사용해서 명시를 다 해주는게 좋은 것 같다. 그게 타입스크립트를 사용하는 이유다.
모듈은 관련된 코드가 모여있는 하나의 파일을 의미한다.
모듈은 저마다 자체 스코프를 가진다. 두 스코프가 소통할 수 있는 방법은 불러오기, 내보내기 키워드를 사용하는 것이다. 안에 정의된 변수, 함수, 클래스, 타입 등은 기본적으로 해당 모듈 내부에서만 접근이 가능하다.
html파일 안에 script태그로 여러 js파일을 불러오면, 두 파일을 하나의 스코프로 보고 변수나 타입을 공유한다.
a파일에서 만든 add함수를 b파일에서 사용해도 문제가 없다. 서로 스코프를 공유하기 때문이다.
그래서 모듈을 사용한다. 각 모듈은 독립적인 스코프를 가질 수 있기 때문에 파일마다 분리를 하는 것이다.
지정하는 방법이 따로 있는게 아니라 import / export를 사용하면 된다. 그럼 모듈 스코프로 취급한다.
utils 파일의 add 함수를 export하면 main파일에서 import해서 가져와서 사용 할 수 있다. 파일명에 .ts/.js/.tsx/.jsx와 같은 확장자명은 사용하지 않아도 된다.
export default를 하면 해당 파일을 모듈로 내보냈을 때 이 함수를 메인모듈로 한다는 의미다.
한번에 여러개의 내보내기를 하고, 받아올 수 있다.
이거 진짜 처음보는거! as를 사용하면 불러오기를 할 때 함수 이름을 원하는대로 지정할 수 있다. 받아오기 하는 변수, 함수, 타입 등등을 더 명확하게 이름을 붙일 수 있다.
앞에서 말한것 처럼 타입에도 적용 가능하다.
.d.ts 의 확장자를 가지는 파일은 주로 내장 객체나 외부 라이브러리 등의 type을 설명하는 파일이다.
.ts가 구현을 제공한다면, .d.ts는 특정 타입이 어떻게 존재하는지 선언해주는게 목적이다.
log에 마우스를 올려보면 console의 메서드이고 any type의 data라는 변수를 받는것을 볼 수 있다.
print라는 메서드는 없다. vscode에서 이렇게 정보를 알려주는 것도 vscode가 ts를 내장하고 있기 때문이다. 그런데 ts도 없는걸 알 수는 없다. ts는 내장 객체를 설명하는 선언파일에서 참조를 하는 것이다.
console을 우클릭해서 정의로 이동하면 Console interface를 확인할 수 있다. 구현부로 이동되면서 console의 다양한 메서드를 확인할 수 있다. 파일명을보면 lib.dom.d.ts
이렇게 .d.ts는 타입 정보만을 포함하고 있다. 기존의 js기반 프로젝트를 ts로 이전할 때도 효과적으로 사용 할 수 있다.
일반 프로젝트에서 .d.ts를 사용하는 경우는 외부 라이브러리를 프로젝트에서 사용할 때다.
예시로 lodash를 사용해보자.
npm init -y -> npm i --save-dev lodash
max는 배열에서 제일 큰 값을 찾는거고 셔플은 무작위로 섞는거다. 밑줄은 .d.ts가 없어서 그렇다는데 일단 무시해도 실행은 된다.
이건 강의 장면이긴 한데, 강사님은 이런 오류가 떳다고 한다. 이게 왜냐면
tsconfig파일에서 esModuleInterop옵션을 true로 지정을 해야한다. 나는 원래 true여서 저 오류가 안나고 컴파일이 잘 됐다.
원래 라이브러리들은 기본적으로 import / export가 없을테니까, 임의로 import해서 가져오면 오류가 나는 것 같다. 그래서 저 옵션을 켜야한다.
그리고 max의 시그니쳐를 확인해보면 따로 지정해주지 않아서 any타입으로 뜬다.
타입을 지정할 수 있는 두가지 방법이 있다.

위와 같이 코드를 입력하면 된다. import를 lodash로 했으니 여기서도 lodash로 작성하면 된다.
그러면 이렇게 지정한대로 타입이 제공된다.하지만 이 직접 타입을 지정하는 이 방법은 정확한 정보를 제공하지 않고, 또 메서드를 사용할 때마다 추가해야하는 번거로움이 있다.
npm i --save-dev @types/lodash를 하면
이렇게 타입스크립트 파일을 다운받는다.
그러면 이렇게 명확한 타입을 확인할 수 있다.많이 들어본 번들러..중의 하나가 webpack이다. 번들러는 웹 애플리케이션을 구성하는 asset(스크립트, 스타일시트, 이미지 등)을 모두 묶어서 최적화된 파일로 만들어 주는 도구이다.
사용하는 가장 큰 목적은 개발하고 배포할 때 코드를 최적화해서 브라우저가 파일을 좀 더 빠르게 다운받을 수 있도록 도와주고 코드를 체계적으로 구성하고 관리하기 위함이다.
여러 파일 js, html, css, 이미지를 번들하여 하나 또는 여러개의 파일로 묶어주는것이 js 애플리케이션을 위한 정적 모듈 번들러다.
(뒤에 나오지만, 번들러를 사용하면 브라우저에 파일들을 전달할 때 하나로정리해서 전달하더라! 이렇게 해서 다운로드 받는 시간을 줄여준다는 의미 아닐까? 생각했다)
오버헤드때문에 웹팩이 느려서 대체하는 여러 번들러가 나오고느 있지만 전통적으로 오래 사용되기도 했고 아직은 시장에서 차치하는비율이 웹팩이 가장 높아서 실무에서는 웹팩을 마주칠 확률이 높다.
개발과정에서는 모듈을 분리하여 관리하는 것이 효율적이지만, 배포시에는 하나 혹은 적은 수의 파일로 결합해서 로딩시간을 줄이는 것이 중요하다.
를 한다.
컨피그 파일은 직접 만들었다. src폴더의 내용을 컴파일하며 dist폴더를 생성하여 거기에 저장한다.
src 폴더를 만들고 그 안에 index.html파일을 만든다.
html5로 선택해서 자동완성 했다.
util폴더도 만들고 dice.ts파일을 만든다.
랜덤하게 값을 하나 뽑아주는 클래스를 만든다. 위 클래스는 숫자를 받아서 1~dice 사이의 랜덤한 숫자를 반환한다.
src폴더에 index.ts도 하나 만든다.
위 코드는 버튼과 div를 만들고, 버튼을 누르면 1-6주에 랜덤한 숫자가 div에 그려진다.
웹팩을 사용하기 위해서 파일은 위와같이 간단하게 만들었다. 이제 웹팩으로 번들링을 해보자 그러려면 웹팩 관련 패키지를 설치해야한다.
npm i —save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin ts-loader
웹팩이랑 웹cli는 함께 동작한다. 하나만 있으면 안되고 둘 다 있어야한다.
웹팩데브서버는 로컬 환경 개발시에만 사용이 되는 라이브러리이다. 웹팩을 실행하고 번들된 결과를 메모리에 임시 저장해서 빠르게 로드를 하고 수정 사항을 적용시킬 수 있게 도와준다.
html웹팩플로그인은 html파일을 얘가 생성해서 웹팩의 번들 결과물을 화면에 처리한다.
ts로더는 .ts같은 확장자들을 표준 자바스크립트로 컴파일한다 내부적으로 tsc를 사용한다.
설치하고나면 웹팩의 환경설정을 해줘야한다.
웹팩 공식 홈페이지에 가서 다큐멘트 -> 가이드 -> 타입스크립트를 선택하면 공홈에서 작성해야하는 내용을 제공해준다.
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.ts",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
},
plugins: [
new HtmlWebpackPlugin({
template: "src/index.html",
}),
],
};
webpack.config.js파일을 생성해서 위 내용을 작성한다.
공홈에서 제공해주는거에서 맨아래의 plugins와 mode만 추가했다.
HtmlWebpackPlugin은 화면에 번들을 그리는 파일을 어디서 가져오면 되는지
모드는 개발모드로 해놓으면 빠른 빌드를 위한 설정을 해주는 것
엔트리는 웹팩이 빌드를 시작할 진입파일을 지정하는 것
모듈은 프로젝트에서 사용되는 모듈에 대한 처리 규칙을 정의
test는 정규표현식을 사용해서 저런 확장자를 가진 애를 대상으로 하라는 거고, ts-loader를 사용해서 컴파일을 하라는 거고 nodemodules폴더는 제외해라라는 의미
리졸브는 모듈을 해석할 때 웹팩이 찾을 수 있는 확장자를 지정
아웃풋은 빌드된 파일의 출력설정을 지정
파일네임은 최종적으로 사용할 자바스크립트 파일의 이름
패스는 경로 지정을 하는데 dist폴더를 만들으라는 소리.
즉 dist폴더에 bundle.js가 생성될거다.
다음으로 package.json에서 스크립트를 작성한다.
start는 웹팩데브서버를 사용한다. 오픈은 브라우저를 자동으로 열어주는거고 모드는 현재 개발모드라는 뜻이다. 개발모드일때는 소스 맵을 생성한다던가 빌드 시간이 빠른 등 개발자에게 유용한 기능을 활성화 한다.
build는 웹팩으로하고, 배포용이니까 프로덕션으로 바꾼다.
npm run start 하면 

브라우저가 켜지고 잘 동작한다. 소스탭을 보면 bundle.js가 생성된걸 볼 수 있다. 우리의 파일을 하나로 번들링 한 모습이다. 해당 파일로 화면이 그려진다.
dice도 가보면 개발환경에 맞게 작성이 되어 있다. 브레이크포인트를 거는등 디버거를 활용할 수 있다.
npm run build 하면 
dist폴더가 생기고 배포에 맞는 파일이 생성되어 있다.
리액트에서 파일이 생기고 dist가 생기고 하는게 이런 이유였구나.. 신기하당
tsc 할 때 파일명을 지정해서
tsc 파일명하면 오류가 났다. 이게 오류가 아니라 원래 그렇다고한다. ts-node라는 라이브러리를 설치해야 하면 오류없이 컴파일이 된다.
6.26 증맬증맬 중요하다는 typscript! 사실 개념만 알고 있을때는 코드를 알아보기도 힘들게 만드는 것 같고, 추가 과정이 들어가면 코드도 무거워지고 느려지는거 아닌가? 생각했었는데, 얼마전에 프로젝트를 만들다가 필요성을 절실하게 느꼈다.
mypage에 유저가 북마크한 영화리스트는 dragNdrop으로 옮기거나 휴지통으로 끌어서 삭제할 수 있다.
그런데 드래그를 하고 나면 저장된 영화 id의 타입이 숫자형으로 바뀌고 있었던 것이다. 이걸 어떻게 발견했냐면
북마크가 되어 있는 것들은 localstorage에 저장이 되고, 영화 디테일 페이지에 들어가면 그것을 참조해서 값이 존재하면 북마크표시가 빨갛게 나오도록 만들었는데, 분명히 mypage에서는 영화가 보이는데 디테일 표이지로 가면 북마크표시가 회색으로 나타나는 것이었다. 그래서 살펴보니까 드래그를 하는 시점에 저장된 값이 숫자가 된다는걸 발견했고..
storage에 저장하기 전에 string형태로 저장하게 했더니 의도대로 동작했다. 이 일을 바로 어제! typescript를 공부하기 하루 전인 바로 어제! 겪어서 아 이래서 타입스크립트를 쓰는거구나.. 하고 느꼈다..
6.27 타입만 배웠는데도 약간 햇갈린다; 비슷한 개념이 많아서 그런 것 같다.. 특히 enum은 왜쓰는건지 굳이 객체가 있는데 쓸 필요가 있나 싶다. https://engineering.linecorp.com/ko/blog/typescript-enum-tree-shaking 해당 블로그 글을 보면 사용하지 않는걸 추천한다고한다. 그냥 union을 쓰라고.. 채은조교님 말씀으로는 비슷한 값을 쓰는 경우에 오타가 발생할 확률을 줄이거나, 미리 범위를 정해주고 자동완성으로 사용해서 실수를 줄일 수 있는 이점이 있는데 실무에서 사용하지 않느냐 하면 그건 사람에 따라 다른거라서 개념을 익히고 있어야 한다고 하셨다.
6.28 타입스크립트 너무 좀 중복되는 개념..? 느낌이 많은것도 같고.. 잘 이해가 안되서 그런거겠지만.. 실습없이 이론만 배우다보니 파악도 잘 안되는거같다.. 수업 끝나면 투두리스트라도 만들어봐야겠다..
7.2 타입스크립트 끝! 활용은 못하겠지만^_^..주말안에 꼭 투두리스트 만들어야지..