(타입스크립트 공부중)
배운 것:
- 타입스크립트에서 keyof과 typeof과 그 사용례에 대해 더 체계적으로 정리해봤다. (어제치 TIL에 업데이트함)
- 타입스크립트의 엔티티를 여러가지로 구분지어 보았다. 예를 들어 ‘타입 선언’ 자리에 올 수 있는 엔티티는 열거형(enum)과 타입(type), 인터페이스, 원시형뿐이다. (아마도)
- 객체의 “계산된 프로퍼티(Computed property)” 방식을 이용해 프로퍼티 키 값을 런타임시에 동적으로 할당해줄 수 있다.
느낀 점
지난 이주, 타입스크립트를 새롭게 공부하며 도무지 감이 잡히지 않는 기분이었다. 어렵긴 어려운데 뭔가 납득도 안 되는 느낌? 이쪽 구멍을 막으면 저쪽이 뚫리고 저쪽을 막으면 다른 곳이 고장나는 항아리를 어떻게든 절묘하게 완성시키라는 것 같았다. 어느 구멍이 어느 구멍(들)을 촉발시키는지, 각 구멍을 막을 수 있는 조각들은 정확히 어떤 게 얼만큼 있는 건지도 제대로 알려주지 않고서. 이렇게 저렇게 알려주긴 하는데 퍼즐이 너무 흩어져있는 느낌이었다.
그러던 차에 오늘에 와서 드디어 실마리를 잡게 되었다. Enum에 대한 간단한 연습 문제 10개가 제공됐는데, ‘된다/안 된다’가 판정나는 샘플이 여러 개 있으니 여기에다 임의로 여러가지 테스트를 하며 항아리 구멍에 대한 이해도를 늘릴 수 있었다. 그리고 그동안 아등바등 지나오며 쌓인 예제 코드들까지 더해지니 이제야 한바탕 속시원히 정리가 된 것 같다. 특히 아래의 타입스크립트 엔티티 모음과 분류 부분은 검색해도 찾지 못한 나만의 정리본이다. 틀린 부분이 있을 수도 있지만.
아무튼 다행이다. 이제라도 좀 정리가 돼서.
(나중에 보면 좋을 사이트)
- 트렁크 기반 개발 (GIthub) - https://trend21c.tistory.com/2231
이걸 적용해볼 수 있을까 이번 프로젝트 때..?
- 타입 호환성에 대하여TypeScript 타입 시스템 뜯어보기: 타입 호환성토스 Node.js 챕터에서는 높은 코드 가독성과 품질을 위해 TypeScript의 타입 시스템을 적극적으로 활용하고 있고 이에 대한 이해도를 높이기 위해 스터디를 꾸준히 진행하고 있습니다. TypeScript의 타입 시스템에 대해 공부해보던 중 알게된 흥미로운 몇가지 토픽들을 소개하려 합니다. 그 중 한가지로 이번글에서는 "타입 호환성 (type compatibility)"에 대해 알아보고자 합니다.
https://toss.tech/article/typescript-type-compatibility
- 명목적 서브타이핑 V.S. 구조적 서브타이핑
- Index signature V.S. Branded type
[타입스크립트] 자동 실행 환경 설정하기 with Nodemon
[타입스크립트] 자동 실행 환경 설정하기 with Nodemon
= TypeScript를 cold reloading하며 실행할 수 있도록 환경 설정하기
npm i typescript @types/node nodemon ts-node -D
npm tsc --init
- 타입스크립트 초기화 및 tsconfig.json 파일 생성
- tsconfig.json 파일 설정
// tsconfig.json { "compilerOptions": { "target": "es2016", "module": "commonjs", "lib": ["DOM"], "outDir": "build", "rootDir": "src", // 이 폴더 안의 ts 파일들을 컴파일함 "strict": true, "noImplicitAny": true, "esModuleInterop": true, "noEmitOnError": true, "moduleDetection": "force" // 작성하는 ts 파일마다 다른 파일의 동일 변수명과 별개의 변수로 인식하게 함. } }
⇒ outDir로 “build” 폴더를 설정해놓긴 했지만 ts-node를 이용할 것이므로 실제로 트랜스파일된 파일이 생성되진 않을 것이다.
- package.json과 같은 레벨에 nodemon.json 파일 만들기
(
nodemon --init
명령이 가능한지는 모르겠다. 그냥 수동으로 만들자)// nodemon.json { "watch": ["src"], // 지켜볼 폴더 "ext": ".ts,.js", // 지켜볼 확장자 "ignore": [], // "src" 안에서 무시했으면 하는 폴더나 파일들 리스트 "exec": "npx ts-node ./src/*.ts" // 명령어... "exec": "npx ts-node ./src/index.ts" }
⇒ “ignore” : src 폴더 안에 변경 사항을 추적하지 않을 여러 폴더를 또 만들 거라면 [”src/enums”, “src/generics”]같이 추가해주면 된다.
⇒ “exec” : 윈도우 환경에서는 밑의 줄로 적어주도록 한다.
- package.json 수정
// package.json "scripts": { ... "start:dev": "npx nodemon -q" }
⇒ 플래그 -q (-quiet)로 파일이 재실행될 때마다 나오는 메세지를 감춰줌.
유의할 점:
- tsconfig.json의 “rootDir” 경로 = 트랜스파일할 ts 폴더/파일 위치
- nodemon.json의 “watch” 경로 = 변화 감지 위치
- nodemon.json의 “exec” 경로 = 트랜스파일할 ts 파일 위치
이 셋이 통일되도록 잘 맞춰줘야 한다.
Cold Reloading이란
Cold Reloading이란
: 코드를 수정할 때마다 수동으로 앱을 재실행(building + running)시키지 않고, 자동으로 되게 하는 것.
이에 관해 내가 찾은 유일한 레퍼런스는 이것이었다:
Cold reloading will automatically rebuild the application every time a code change is detected preventing the need to rebuild the application manually. (참조: https://stephendoddtech.com/blog/game-design/cold-reloading-typescript-project)
여기서는 타입스크립트로 브라우저에서 실행하는 간단한 게임을 만드는데, Cold reloading은 코드 변경이 발생할 시 앱을 알아서 재실행시키되 브라우저는 내가 새로고침을 해야 반영이 되는 것이고, Hot reloading은 브라우저까지도 자동으로 새로고침이 되는 방식이라고 소개한다.
가장 많이 본 레퍼런스는 리액트에 있는 Hot reloading과 Live reloading 개념을 비교하는 글이었다.
- Hot reloading: 코드를 수정하면 앱을 재실행하는 것 없이 변경 사항을 반영하여 볼 수 있도록 하는 것. 지금 실행중인 앱에 그 바뀐 코드만 교체해 넣으므로써 앱이 가지고 있는 상태(state)은 리셋되지 않는다.
- Live reloading: 코드를 수정하면 앱 전체를 다시 실행하는 것. 전에 실행되며 가지고 있던 상태(state) 데이터가 리셋된다.
- (내가 끼워넣어본) Cold reloading: Live reloading과 비슷한 개념인 것 같다. ‘앱 전체’를 다시 실행(rebuild)한다는 점에서.
에러 ‘n p m ‘은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다. ✔️
에러 ‘n p m ‘은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다. ✔️
발생 상황: 파이참 IDE에서 여러 단축키를 실험해보던 중 갑자기 터미널과 에디터에서 영어가 “apple”이 아니라 “a p p l e “같이 입력됨.
타입: 바보이슈
원인: 키보드 입력 설정이 ‘반자’가 아니라 ‘전자’로 설정되어 있어서 그렇다.
시도:

브라우저나 다른 창에서는 영문 입력이 잘 되길래 파이참에서 저 “2 spaces”설정이 space를 끼워넣는 주범인 줄 알고 그걸로 검색함.
“pycharm how to set 2 spaces”
⇒ “pycharm how to set default space size”
⇒ “intellij default spaces are 2”
그러나 indents와 tab 사이즈가 자꾸 “2 spcaes”로 된다고/안 된다고 아우성치는 검색 결과밖에 얻지 못함.
해결:

alt + “=”로 반자/전자를 바꿔주면 된다. 아니면 윈도우 시작줄에서 직접 선택해주어도 된다.
“intellij 문자 입력시 띄어쓰기” 이걸로 원인을 한 방에 찾았다. 때로는 한글 검색이 와따봉이다.
(참조: https://ttend.tistory.com/601)
[자바스크립트] 객체 생성시 변수에 담긴 값을 key로 사용하는 방법, “계산된 프로퍼티”, 동적 키 할당
[자바스크립트] 객체 생성시 변수에 담긴 값을 key로 사용하는 방법, “계산된 프로퍼티”, 동적 키 할당
: [키 변수] : 값
혹은 [키 변수]: 값 변수
: 변수명을 대괄호 []에 감싸서 넣어준다.
ES6부터 도입된 방법이라고 한다.
const sorter = 'username';
const order = 'desc';
const resultObject = {
[sorter] : order
}
console.log(resultObject);
// => 결과:
// {'username': 'desc'}
변수에 담기지 않은 계산값도 같은 방법으로 대입해줄 수 있다:
const someArray: string[] = ["one", "two", "three"]
const resultObject = {
[someArray[0].length] : someArray[0]
}
console.log(resultObject);
// => 결과:
// {'3': 'one'}
참고로 변수에 담긴 값을 value로 사용하고 싶을 땐 그냥 변수명을 넣어주면 된다.
(참고: https://koonsland.tistory.com/146)
계산된 프로퍼티(Computed property)와 동적 키 할당
: 객체를 만들 때 객체 리터럴 안의 프로퍼티 키가 대괄호로 둘러싸여 있는 경우
이와 함께 대괄호 표기법(Square Bracket Notation)의 특징인 “변수나 문자열뿐만 아니라 모든 표현식의 평가 결과를 프로퍼티 키로 사용할 수 있다”, 즉 “평가가 끝난 이후의 결과가 프로퍼티 키로 사용된다”는 특징을 이용하면 다음과 같이 동적으로 프로퍼티 키를 할당해주는 것도 가능하다 :
// 예시:
let fruit = prompt("어떤 과일을 구매하시겠습니까?", "apple");
let bag = {
[fruit]: 5, // 변수 fruit에서 프로퍼티 이름을 동적으로 받아 옵니다.
};
alert( bag.apple ); // fruit에 "apple"이 할당되었다면, 5가 출력됩니다.
(참고: https://ko.javascript.info/object)
그 밖에 ‘객체’의 특징
- 프로퍼티 키는 문자열이나 심볼이어야 한다. 보통은 문자열이다.
- 값은 어떤 자료형도 가능하다.
- 문자형이나 심볼형에 속하지 않은 값은 문자열로 자동 형 변환된다.
예를 들어 키에 숫자 0을 넣으면 문자열 “0”으로 자동 변환된다.
- 객체의 프로퍼티 {키: 값}를 부르는 명칭:
⇒ 키 : 프로퍼티 이름 = 프로퍼티 키 = 프로퍼티 식별자
⇒ 값 : 프로퍼티 값
- 객체의 프로퍼티 정렬 방식: 정수 프로퍼티(integer property)는 자동으로 정렬되고, 그 외의 프로퍼티는 객체에 추가한 순서 그대로 정렬된다.
그 밖에 ‘객체’와 관련된 용어들
- Trailing(Hanging) comma: 마지막 프로퍼티 끝에 붙는 쉼표 ✔️
- 점 표기법(Dot notation)과 대괄호 표기법(Square bracket notation) ✔️
- 계산된 프로퍼티(Computed property) : { [fruit]: 5 }같이 프로퍼티 키가 대괄호로 둘러싸인 경우. 표현식의 평가 결과를 프로퍼티 키로 할당한다고 하여 ‘계산된 프로퍼티’라고 부른다. ✔️
- 단축 프로퍼티(프로퍼티 값 단축 구문)(Property value shorthand) : { name: name}을 { name }이라고 쓰는 것. ✔️
- 프로퍼티 이름에 제약사항은 없다. “__proto__” 를 제외하면 예약어도 키로 가능하다.
delete obj.prop
: “prop” 프로퍼티 삭제 ✔️
"key" in obj
: 프로퍼티 키 “key”의 존재 여부 확인 ✔️
for (let key in obj)
반복문 : 객체의 모든 키 순회 ✔️
- 정수 프로퍼티(Integer property) : 변형 없이 정수로 왔다 갔다 할 수 있는 문자열을 키로 가지는 프로퍼티. “49”같은 것. 반면에 '+49’와 '1.2’는 정수 프로퍼티가 아니다.
(참고: https://ko.javascript.info/object)
[타입스크립트] 객체를 타입으로 지정하고 싶을 땐
[타입스크립트] 객체를 타입으로 지정하고 싶을 땐
const obj = { red: 'apple', yellow: 'banana', green: 'cucumber' } as const;
type Keys = keyof typeof obj; // = "red" | "green" | "yellow"
type Values = typeof obj[keyof typeof obj]; // = "apple" | "cucumber" | "banana"
const value1: Values = 'apple';
const value2: Values = obj.red;
const key1: Keys = 'red';
typeof를 이용해서 객체 → ‘type’으로 만들어 사용한다.
예를 들어 함수를 ‘선언할 땐’: 타입이 필요함.
그리고 함수를 ‘호출할 땐’: 타입에 맞는 실제 인스턴스(변수)가 필요함.
이를 코드로 나타내보면:
// ODirecton이라는 객체가 있는데
const ODirection = {
Up: 0,
Down: 1,
Left: 2,
Right: 3,
} as const
// 이 객체의 속성값을 ODirection.Up같이 인자로 받고 싶음:
run(ODirection.Up); // = 0
// 함수를 '선언할 때'는 객체가 아니라 타입이 필요함:
// => 타입 추출후 함수 선언에 넣어주기
type Direction = typeof ODirection[keyof typeof ODirection]; // type Direction = 1 | 2 | 3 | 0
function fun(dir: Direction) { console.log(dir) }
// => "Down" | "Left" | "Right" | "Up"이 아니라 1 | 2 | 3 | 0로 추출해야 한다. 함수가 '받는 것'이므로.
// 함수를 '호출할 때'는 (원래 사용하려고 한 목적인) 실제 객체가 필요함:
run(ODirection.Up) // 0
참고로 Enum은 그 자체로 타입으로 넣어줄 수도 있고 호출 시 ‘값’으로도 넣어줄 수 있다.
// EDirection이라는 Enum이 있으면
enum EDirection {
Up,
Down,
Left,
Right,
}
// 이것 그대로 함수 선언에도 사용하고
function walk(dir: EDirection) { console.log(dir) };
// 이것 그대로 함수 호출에도 사용할 수 있다.
walk(EDirection.Up); // 0
[타입스크립트] 엔티티 선언 방식 모음
[타입스크립트] 엔티티 선언 방식 모음
‘객체형’과 ‘클래스’형 선언 형식에 유의미한 차이가 있는지, 있다면 어떤 타입들이 어떤 쪽에 해당하는지를 정리하고 싶어서 모아본 용례 모음이다.
종류: 객체, enum, type, interface, 클래스, 함수
// 인터페이스
interface IDirection { // 콤마로 구분
Up: number,
Down?: number,
Left?: number,
Right: number,
}
interface LabeledValue { // 세미콜론으로도 구분
readonly label: string;
}
interface User { // 아예 아무 구분자도 없어도 괜찮음.
id: number
firstName: string
lastName: string
role: string
}
interface login { // 함수
(username: string, password: string): boolean;
}
interface CraftBeer { // 클래스 형식
beerName: string;
nameBeer(beer: string): void;
}
// 클래스
class ClassExample {
constructor(public name: string, public age: number) {}
}
// 열거형
enum EDirection { // 숫자형
Up,
Down,
Left,
Right,
}
enum ProgrammingLanguage { // 문자형
TypeScript = "TypeScript",
JavaScript = "JavaScript",
Python = "Python",
Golang = "Golang",
}
// 객체 (리터럴)
const ODirection = {
Up: 0,
Down: 1,
Left: 2,
Right: 3,
}
// type alias
type TOrder = {
[key: string]: {
status: Status,
color: Color,
availableColors: Color[],
orderedBy: Customer
}
};
type TProgrammingLanguages = {
[key: string]: string
};
type ColorKey = keyof typeof Color;
type Customer = {
firstName: string;
lastName: string;
};
// 함수
function create(o: object | null): void {}
[타입스크립트] 엔티티 분류 - “Type 군”과 “Value 군”, “선언 타입군”
[타입스크립트] 엔티티 분류 - “Type 군”과 “Value 군”, “선언 타입군”
내가 지금까지 본 코드 용례들을 기준으로 정리해 보았다.
‘Type 군’에 속하는 것
= 핵심적으로는 ‘key’ 이름만 있고 ‘value’가 없는 것. 그리고 구조 틀과 타입을 명시한다.
= keyof
의 피연산자로 쓸 수 있는 종류.
예를 들어 keyof 오브젝트
는 ‘type값을 기대하고 있는데 value 값이 들어와서 안됨’ 컴파일 에러를 띄운다.
‘key’와 틀이 있는 것이라면 (keyof 파트에서 다뤘듯이) :
- class
- interface
- type alias로 선언한 type
이 있다.
‘Value 군’에 속하는 것
= ‘key’와 함께 실제 내용물(’value’)이 있는 것. 틀만으로 구성되지 않은 것.
= typeof
의 피연산자로 쓸 수 있는 종류.
- 객체
- enum
- function
- class
타입 선언 자리에 올 수 있는 것(’선언 타입군’)
- enum
- type alias로 선언한 type
- interface - 객체
- interface - 클래스
- interface - 함수
- 원시형
이만하면 지금까지 다룬 타입들을 다 커버한 것 같다.
Uploaded by N2T