배경
생각해보면, 예전에 교수님 수업시간에 C언어 설명하실 때도, GENERIC으로 표현된 C언어 함수를 보여주셨던 것 같다.
그 때는, 거의 코딩을 모르는 상태라서 아예 못알아봤던 것 같고
늘 모르고 살던 것을 알았을 때의 "개안" 상태가 된 것 같다. 늘.. T가 뭐지? 하면서 라이브러리나 github 소스코드를 보곤 했었으니까..
문제
function checkNotNull(arg: number | null): number {
if (arg == null) {
throw new Error("not valid number!");
}
return arg;
}
const result = checkNotNull(23);
console.log(result);
checkNotNull(null);
위와 같이 작성하면, argument(전달인자, 인수)가 number이거나 null 인 것에 한해서만 동작하는 것을 알 수 있다.
그러나, 여기서 string이나, boolean 타입도 들어온다고 하면 문제가 발생한다. return되는 타입도 다를 수 있기 때문이다.
아래와 같이 짜는 것처럼 return type도 많아지면, 더이상 result 변수의 type이 무엇인지 보장할 수 없게 된다.
function checkNotNull(arg: number | null): number | string {
if (arg == null) {
throw new Error("not valid number!");
}
return arg;
}
const result = checkNotNull(23);
console.log(result);
checkNotNull(null);
해결
이를 해결하기 위해 사용하는 것이 GENERIC이다.
그래서 아래와 같이 작성한다. T는 Type의 줄임말로 사용된 것이다. 실제로는 어떤 문자열을 넣어도 관계 없다. 관용적으로 T를 사용한다. 인자가 2개이고, payload와 관련된 것을 인자로 받는 경우는 P로 하는 경우도 있는 것 같다. 그러나, 1개일 때는 대부분 T를 사용한다. 그게 개발자들끼리의 느끰적인 느끰이다. 다른 거 쓴다고 까불지 말자.
function checkNotNull<T>(arg: T | null): T {
if (arg == null) {
throw new Error("not valid number!");
}
return arg;
}
const bool = checkNotNull(true);
const number = checkNotNull(123);
const bool 이나 number를 보면, type추측이 boolean, number로 타입이 자동 추측 되는 것을 알 수 있다.
응용편
// either: a or b
interface Either<L, R> {
left: () => L;
right: () => R;
}
class SimpleEither<L, R> implements Either<L, R> {
constructor(private leftValue: L, private rightValue: R) {}
left(): L {
return this.leftValue;
}
right(): R {
return this.rightValue;
}
}
const either: Either<number, number> = new SimpleEither(4, 5);
either.left(); // 4
either.right(); // 5
const best = new SimpleEither(5, "hi");
const today = new SimpleEither({ number: 1 }, "hello");
위와 같은 형식으로 사용하는 것도 가능해진다. 요즘은 React를 사용하면서 주로 Functional로 작성하니까 class 형태로 쓸 일이 별로 없지만, 위와 같이 class 형태로 작성하는 것도 유용하다.
여기서 중요한 점은 new 생성자를 통해 object 타입의 인자를 넘겨주는 것도 가능하다.
일반적으로 1개의 약어로 GENERIC의 타입을 명명한다.
보너스
const obj = {
name: "yj",
age: 20,
};
const obj2 = {
animal: "🐱",
age: 10,
};
console.log(getValue(obj, "name")); // yj
console.log(getValue(obj2, "animal")); // 🐱
// K는 T에 속한 key 중 하나
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
위와 같이 작성하는 방법도 있다. K extends keyof T 라는 부분을 잘 몰랐는데, 오늘 알게 되었다. 생각해보면, 걍 문법대로, 읽히는 대로 이해하면 되는 거였다. 만약에 obj에 속하지 않은 key에 해당하는 property명을 인자로 전달할 경우 Error로 처리된다.
회사에 작성된 api 함수 구문 중에 위와 같은 부분이 있었는데, 좀 더 정확히 이해하게 되어서 기쁘다.
'React & TypeScript > TypeScript' 카테고리의 다른 글
[ESLint] naming-convention 설정방법 (0) | 2023.11.23 |
---|---|
비공개 - [TypeScript] 활용법 Basic (이 건 뭐고, 저 건 또 뭐야? 싶을 때 보는) (0) | 2023.07.10 |
[TypeScript] as 사용에 대한 이해 (feat. Type Assertions) (1) | 2022.12.27 |
[React / TypeScript] props를 구조분해할당으로 전달받을 때 Type Error (TS2322) (0) | 2022.12.20 |
[TypeScript] Tip - 자동으로 Type 추출 (0) | 2022.12.19 |
[React / TypeScript / API] API 구조 작성은 이렇게 한다. (0) | 2022.12.16 |
[React / TypeScript] interface로 타입 정의 중 발생한 이슈 (useParams, Params) - TS2344 (0) | 2022.12.09 |
댓글