(타입스크립트 공부중)
배운 것:
- 타입스크립트에서 keyof typeof 꼼수의 활용례와 원리에 대해 알아봤다.
- 타입스크립트의 열거형을 Jest를 활용한 테스트코드와 연계하여 연습해보았다.
- 데이터베이스의 인덱스 개념과 selectivity 이슈, 정규화와 역정규화에 대한 예제를 조금 더 익혔다. 검색 속도를 빠르게 하기 위해 도리어 정규화의 반대 루트(역정규화)를 택할 때도 있다는 사실이 흥미로웠다.
Jest 테스트코드 사용 예제 비교
describe("테스트 큰 제목", () => {
it("테스트 작은 제목", () => {
expect(...).toBe(false);
}
}
vs
describe("테스트 큰 제목", () => {
test("테스트 작은 제목", async () => {
expect(...).toEqual(false);
}
}
it()은 test()와 같다. (test()의 별명)
toBe는 toEqual에 더해 주소값까지 같음을 비교하는 메소드이다.
export enum Status {
Initialized = "Initialized",
Pending = "Pending",
Complete = "Complete",
}
export function isStatusPending(status: Status): boolean {
return status == Status.Pending;
}
isStatusPending(Status.Pending) // ⇒ Status타입이 와야 하는 자리에 이렇게 넣을 수 있다. type은 Status.Pending이고 근본타입(값)은 "Pending"
= isStatusPending("Pending")
= true
⇒ Status타입이 와야 하는 자리에 이렇게 넣을 수 있다.
typeof 연산자
typeof 연산자
: 대상의 타입 자체를 나타내줄 수 있도록 만든 연산자.
: 객체, 열거형, 함수 등을 type
형태로 바꿔줌.
: typeof {value에 해당하는 값}
⇒ typeof {객체}
⇒ typeof {enum}
⇒ typeof {함수}
⇒ typeof {클래스}
(typeof {'type'에 해당하는 값}
처럼 사용시 ‘value’에 해당하는 값을 넣으라고 컴파일 에러 발생.)
// 간단 예시:
let s = "hello"'
let n: typeof s;
// => let n: string
- 객체 → type
: 객체의 타입 구조를 그대로 뽑아와 새 타입으로 만들어 사용하고 싶을 때 사용.
const obj = { red: 'apple', yellow: 'banana', green: 'cucumber', }; // 위의 객체를 타입으로 변환하여 사용하고 싶을때 type Fruit = typeof obj; /* type Fruit = { red: string; yellow: string; green: string; } */
- enum → type
열거형(Enum)도 타입 구조를 추출할 수 있다.
enum Color { Red, Blue, Black, } type TColor = typeof Color; /* type TColor = { Red: number; Blue: number; Black: number; } */
- 함수 → type
함수의 타입 구조도 뽑아올 수 있다.
function original(num: number, str: string): string { return num.toString(); } type original_Type = typeof original // type original_Type = (num: number, str: string) => string const 용례: original_Type = (num: number, str: string) => { return str; };
- 클래스 → type
클래스는 자체가 객체 타입이 될 수 있으므로 typeof를 붙이지 않고도 타입 구조를 뽑아올 수 있다.
class ClassExample { constructor(public name: string, public age: number) {} } type ClassExample_Type = ClassExample; // = type ClassExample_type = { name: string, age: number } const 용례: ClassExample_Type = { name: '마실', age: 99, }
- 인터페이스 → type
인터페이스는 자체가 타입이므로 ‘타입 구조를 뽑는다’는 것이 불가능함.
keyof 연산자
keyof 연산자
: ‘type(타입)’의 ‘키’들을 뽑아서 문자열 리터럴 유니온 타입으로 만들어 주는 연산자.
: keyof {type에 해당하는 값들}
type과 interface, class에 직접 적용 가능하다.
⇒ keyof {type}
⇒ keyof {interface}
⇒ keyof {class}
(keyof {value에 해당하는 값}
처럼 사용시 ‘type’을 넣어달라고 컴파일 에러가 난다.)
- (명시적) type → type
type Type = {
name: string;
age: number;
married: boolean;
}
type Union = keyof Type;
// type Union = "name" | "age" | "married"
const 용례: Union = 'name';
- class → type
class ClassExample {
constructor(public name: string, public age: number) {}
}
type ClassToKeys = keyof ClassExample;
// type ClassToKeys = "name" | "age"
- interface → type
interface IDirection {
Up: number,
Down?: number,
Left?: number,
Right?: number,
}
type InterfaceToKeys = keyof IDirection;
// type InterfaceToKeys = "Up" | "Down" | "Left" | "Right"
- 다른 것들 (객체, enum, 함수 등)은 typeof로 type화를 거쳐야 적용 가능하다.
⇒ (typeof로 변환한) type → type
// typeof 항목에서 봤던 대로
// 1. 객체 -> (typeof) -> (keyof) -> type
// 2. Enum -> (typeof) -> (keyof) -> type
// 3. 함수 -> 적용되지 않음. 생각해보면 함수의 typeof로 뽑아온 구조는 파라미터의 타입과 리턴값의 타입을 ㅈ어의해 놓은 것인데, 여기서 특별히 'key'라고 할만 한 게 없다.
function original(num: number, str: string): string {
return num.toString();
}
type FunctionType = typeof original;
// type FunctionType = (num: number, str: string) => string
type FunctionToKeys = keyof FunctionType
// type FunctionToKeys = never
// 4. 클래스 -> type
// 과 클래스 -> (typeof) -> (keyof) -> type 이 같음.
// 중간에 typeof를 거치든 안 거치든 결과가 같다.
// 5. 인터페이스 -> 컴파일 에러.
Enum에 적용하면 이상해진다:
enum EDirection { Up, Down, Left, Right, } type strangeType = keyof EDirection; // strangeType = "toString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf" | "toLocaleString"
⇒ Enum의 경우 typeof를 한 번 거쳐야함을 알게됨.
객체에 적용하면 아예 컴파일이 안 된다:
const ODirection = { Up: 0, Down: 1, Left: 2, Right: 3, } type newOb = keyof ODirection; // TS2749: 'ODirection' refers to a value, but is being used as a type here. Did you mean 'typeof ODirection'?
keyof typeof 조합
keyof typeof 조합
enum Color {
Red,
Blue,
Black,
}
type TColor = {
[key in keyof typeof Color]: string[];
}
// 이걸 작동되게 Tcolor를 만들고 싶다.
const colors: TColor = {
Red: ["red"],
Blue: [],
Black: ["obsidian", "ink"],
};
// typeof Color:
type TColor = {
Red: number;
Blue: number;
Black: number;
}
// keyof typeof Color:
type TColor = "Red" | "Blue" | "Black"
// [key in keyof typeof Color] 적용 후:
type TColor = {
"Red": string[],
"Blue": string[],
"Black": string[],
}
keyof Color
로 한 번에 키들을 빼오고 싶어도, keyof는 객체 형태에만(=type이나 객체인 형태. enum은 안됨.) 작용하므로 먼저 typeof로 ‘type’ 형태로 만들어주고, 그 후에 keyof를 적용시키는 꼼수이다.
한 마디로 정리하자면, keyof typeof
Enum
은, ‘Enum을 먼저 type으로 변환시킨 후 key 목록 추출’인 것.
종류:
// typeof 항목과 keyof 항목에서 살펴본 것 요약본:
// 1. 객체 -> (typeof) -> (keyof) -> type
// 2. Enum -> (typeof) -> (keyof) -> type
// 3. 함수 -> 적용되지 않음.
// 4-1. 클래스 -> type
// 4-2. 클래스 -> (typeof) -> (keyof) -> type
// (클래스는 중간에 typeof를 거치든 안 거치든 결과가 같다.)
객체에서 ‘key’ 추출
객체의 ‘key’값들을 리터럴 유니온 타입(상수 타입)으로 뽑아내고 싶다면:
const obj = { red: 'apple', yellow: 'banana', green: 'cucumber' } as const;
type Keys = keyof typeof obj;
// type Keys = "red" | "green" | "yellow"
let ob2: Keys = 'red';
let ob3: Keys = 'yellow';
let ob4: Keys = 'green';
객체에서 ‘value’ 추출
객체의 ‘value’값들을 리터럴 유니온 타입(상수 타입)으로 뽑아내고 싶다면:
const obj = { red: 'apple', yellow: 'banana', green: 'cucumber' } as const;
type Values = typeof obj[keyof typeof obj];
// type Values = "apple" | "cucumber" | "banana"
let ob2: Values = 'apple';
let ob3: Values = 'banana';
let ob4: Values = 'cucumber';
Uploaded by N2T