제네릭을 통해 호출하는 방식에 따라 다양한 타입으로 작동하도록 의도할 수 있습니다.
✨ 제네릭 함수
function identity<T>(input: T){
return input
}
const identity2 = <T>(input: T) => input;
화살표 함수의 제네릭은 .tsx 파일에서 JSX 구문과 충돌합니다.
명시적 제네릭 호출 타입
function logWrapper<Input>(callback: (input: Input) => void) {
return (input: Input) => {
console.log("Input:", input);
callback(input)
}
}
logWrapper((input: string) => {
console.log(input.length);
})
logWrapper((input) => {
console.log(input.length) // ERROR 'input' is of type 'unknown'.
})
기본 값이 unknown으로 설정되는 것을 피하기 위해 명시적 제네릭 타입 인수를 사용할 수 있습니다.
logWrapper<string>((input) => {
console.log(input.length)
})
다중 함수 타입 매개변수
function makeTuple<First, Second>(first: First, second: Second) {
return [first, second] as const
}
const tuple1 = makeTuple(true,"abc") // const tuple1: readonly [boolean, string]
const tuple2 = makeTuple<any,string>(true,"abc") // const tuple2: readonly [any, string]
✨ 제네릭 인터페이스
인터페이스도 제네릭으로 선언할 수 있습니다.
interface Box<T> {
inside : T
}
const stringBox: Box<string> = {
inside : "ABC"
}
타입 스크립트의 내장 Array 메서드는 제네릭 인터페이스로 정의된다.
interface Array<T> {
// ...
pop() : T | undefined;
push(...items: T[]): number;
}
유추된 제네릭 인터페이스 타입
interface LinkedNode<Value> {
next?: LinkedNode<Value>;
value: Value;
}
function getLastNode<Value>(node: LinkedNode<Value>): Value {
return node.next ? getLastNode(node.next) : node.value;
}
let lastDate = getLastNode({
value: new Date()
}) // Date 타입으로 유추
let lastString = getLastNode({
next: {
value: 123
}
value: "ABC"
}) // ERROR Type 'string' is not assignable to type 'number'.
✨ 제네릭 클래스
클래스에도 제네릭을 사용할 수 있습니다.
class Secret<Key, Value>{
key: Key;
value: Value
constructor(key: Key, value: Value) {
this.key = key;
this.value = value;
}
// ...
}
명시적 제네릭 클래스 타입
클래스 타입 인수를 유추할 수 없는 경우에는 타입 인수의 기본값은 unknown이 됩니다.
class CurriedCallback<Input> {
#callback: (input: Input) => void
constructor(callback: (input:Input) => void) {
this.#callback = (input: Input) => {
console.log("Input:", input)
callback(input)
}
}
}
new CurriedCallback((input: string)=>{
console.log(input.length) // OK
})
new CurriedCallback((input)=>{
console.log(input.length) // ERROR 'input' is of type 'unknown'.
})
제네릭 클래스 확장
제네릭 클래스 역시 extends 키워드 다음에 오는 클래스로 사용할 수 있지만, 타입을 유추하진 않습니다. 명시적 타입 애너테이션을 통해 지정해야 합니다.
class Quote<T> {
line: T;
constructor(line: T) {
this.line = line
}
}
class SpokenQuote extends Quote<string[]> { // OK 제네릭을 명시해 주어야 한다.
speak() {
console.log(this.line.join('\\n'))
}
}
class AttributedQuote<Value> extends Quote<Value> { // OK
speaker: string
constructor(value: Value, speaker: string){
super(value);
this.speaker = speaker;
}
}
제네릭 인터페이스 구현
interface Interface<Type> {
prop: Type
}
class Class implements Interface<string>{
prop: string
constructor(value: string) {
this.prop = value
}
} // OK
class Class2 implements Interface<string>{
prop: number;
/* ERROR
Property 'prop' in type 'Class2' is not assignable to the same property in base type 'Interface<string>'.
Type 'number' is not assignable to type 'string'.
*/
}
메서드 제네릭
클래스 메서드는 인스턴스와 별개로 자체 제네릭 타입을 선언할 수 있습니다.
class Factory<Key> {
key: Key;
constructor(key: Key){
this.key = key
}
createPair<Value>(value: Value){
return {key: this.key, value};
}
}
정적 클래스 제네릭
클래스의 정적 멤버는 다른 특정 인스턴스와 연결되어 있지 않습니다.
따라서 클래스에 선언된 어떤 타입 매개변수에도 접근할 수 없습니다.
class Logger<Instance> {
static staticLog<Value>(value: Value) {
let instance: Instance
// ERROR Static members cannot reference class type parameters.
console.log(value);
return value
}
}
'기술 서적 > 하루 30분 러닝 타입스크립트' 카테고리의 다른 글
[하루 30분 러닝 타입스크립트] 선언 파일 (0) | 2024.01.30 |
---|---|
[하루 30분 러닝 타입스크립트] 타입 제한자 (0) | 2024.01.12 |
[하루 30분 러닝 타입스크립트] 클래스(2) (0) | 2024.01.08 |
[하루 30분 러닝 타입스크립트] 클래스(1) (0) | 2024.01.07 |
[하루 30분 러닝 타입스크립트] 인터페이스 (0) | 2024.01.04 |