πŸ‘©πŸ»‍πŸ’» Dev/Front-End

[TypeScript] μ œλ„€λ¦­ μ œλŒ€λ‘œ μ‚¬μš©ν•˜κΈ°

yesolz 2024. 11. 8. 00:29
728x90

 

μ œλ„€λ¦­(Generic)μ΄λž€?

Cλ‚˜ μžλ°” 같은 정적 μ–Έμ–΄μ—μ„œ λ‹€μ–‘ν•œ νƒ€μž… 간에 μž¬μ‚¬μš©μ„±μ„ 높이기 μœ„ν•΄ μ‚¬μš©ν•˜λŠ” 문법이닀. νƒ€μž…μŠ€ν¬λ¦½νŠΈλ„ 정적 νƒ€μž…μ„ κ°€μ§€λ―€λ‘œ μ œλ„€λ¦­ 문법을 μ§€μ›ν•œλ‹€.

μ œλ„€λ¦­μ˜ 사전적 μ˜λ―ΈλŠ” 일반적인 것(general)을 λœ»ν•˜λŠ”λ°, νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ μ œλ„€λ¦­λ„ 이와 λΉ„μŠ·ν•˜κ²Œ 'μΌλ°˜ν™”λœ 데이터' νƒ€μž…μ΄λΌ λ³Ό 수 μžˆλ‹€.

μ œλ„€λ¦­: ν•¨μˆ˜, νƒ€μž…, 클래슀 λ“±μ—μ„œ λ‚΄λΆ€μ μœΌλ‘œ μ‚¬μš©ν•  νƒ€μž…μ„ 미리 정해두지 μ•Šκ³  νƒ€μž… λ³€μˆ˜λ₯Ό μ‚¬μš©ν•΄μ„œ ν•΄λ‹Ή μœ„μΉ˜λ₯Ό λΉ„μ›Œ λ‘” λ‹€μŒμ—, μ‹€μ œλ‘œ κ·Έ 값을 μ‚¬μš©ν•  λ•Œ μ™ΈλΆ€μ—μ„œ νƒ€μž… λ³€μˆ˜ μžλ¦¬μ— νƒ€μž…μ„ μ§€μ •ν•˜μ—¬ μ‚¬μš©ν•˜λŠ” 방식

-> μ΄λ ‡κ²Œ ν•˜λ©΄ ν•¨μˆ˜, νƒ€μž…, 클래슀 λ“± μ—¬λŸ¬ νƒ€μž…μ— λŒ€ν•΄ ν•˜λ‚˜ν•˜λ‚˜ λ”°λ‘œ μ •μ˜ν•˜μ§€ μ•Šμ•„λ„ 되기 λ•Œλ¬Έμ— μž¬μ‚¬μš©μ„±μ΄ 크게 ν–₯μƒλœλ‹€.

νƒ€μž… λ³€μˆ˜λŠ” 일반적으둜 <T>와 같이 κΊΎμ‡ κ΄„ν˜Έ 내뢀에 μ •μ˜λ˜λ©°, μ‚¬μš©ν•  λ•ŒλŠ” ν•¨μˆ˜μ— λ§€κ°œλ³€μˆ˜λ₯Ό λ„£λŠ” 것과 μœ μ‚¬ν•˜κ²Œ μ›ν•˜λŠ” νƒ€μž…μ„ λ„£μ–΄μ€€λ‹€.

νƒ€μž… λ³€μˆ˜λͺ…μœΌλ‘œ T(Type), E(Element), K(Key), V(Value) λ“± ν•œ κΈ€μžλ‘œ 된 이름을 많이 μ‚¬μš©ν•œλ‹€.

 

anyμ™€μ˜ 비ꡐ

anyλŠ” λ°°μ—΄ μš”μ†Œλ“€μ˜ νƒ€μž…μ΄ μ „λΆ€ 같지 μ•Šμ„ 수 μžˆμ§€λ§Œ,

μ œλ„€λ¦­μ€ λ°°μ—΄ 생성 μ‹œμ μ— μ›ν•˜λŠ” νƒ€μž…μœΌλ‘œ νŠΉμ •ν•œλ‹€. 즉, λ°°μ—΄ μš”μ†Œκ°€ μ „λΆ€ λ™μΌν•œ νƒ€μž…μœΌλ‘œ 보μž₯ν•  수 μžˆλ‹€.

 

νƒ€μž… μΆ”λ‘ κ³Ό κΈ°λ³Έκ°’ μ‚¬μš©

function exampleFunc<T>(arg: T): T[] {
	return new Array(3).fill(arg);
}

exampleFunc("hello"); // TλŠ” string으둜 좔둠됨

interface SubmitEvent<T = HTMLElement> extends SyntheticEvent<T> {submitter: T;} // κΈ°λ³Έκ°’ μ‚¬μš©

μ œλ„€λ¦­ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•  λ•Œ νƒ€μž… λͺ…μ‹œ λΆ€λΆ„(<>)을 μƒλž΅ν•˜λ©΄ μ»΄νŒŒμΌλŸ¬κ°€ 인수λ₯Ό 보고 νƒ€μž…μ„ μΆ”λ‘ ν•΄μ€€λ‹€.

λ˜ν•œ νŠΉμ • μš”μ†Œ νƒ€μž…μ„ μ•Œ 수 없을 λ•ŒλŠ” μ œλ„€λ¦­ νƒ€μž…μ— 기본값을 μΆ”κ°€ν•  수 μžˆλ‹€.

 

주의점

λ°°μ—΄μ—λ§Œ μ‘΄μž¬ν•˜λŠ” length 속성을 μ œλ„€λ¦­μ—μ„œ μ°Έμ‘°ν•˜λ €κ³  ν•˜λ©΄ λ‹Ήμ—°νžˆ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€. μ»΄νŒŒμΌλŸ¬λŠ” μ–΄λ–€ νƒ€μž…μ΄ μ œλ„€λ¦­μ— 전달될지 μ•Œ 수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.

이럴 λ•ŒλŠ” μ œλ„€λ¦­ κΊΎμ‡ κ΄„ν˜Έ 내뢀에 'length 속성을 가진 νƒ€μž…λ§Œ λ°›λŠ”λ‹€'λΌλŠ” μ œμ•½μ„ κ±Έμ–΄μ€ŒμœΌλ‘œμ¨ length 속성을 μ‚¬μš©ν•˜κ²Œλ” λ§Œλ“€ 수 μžˆλ‹€.

interface TypeWithLength {
	length: number;
}

function exampleFunc2<T extends TypeWithLength>(arg: T): number {
	return arg.length;
}

 

파일 ν™•μž₯μžκ°€ tsx일 λ•Œ ν™”μ‚΄ν‘œ ν•¨μˆ˜μ— μ œλ„€λ¦­μ„ μ‚¬μš©ν•˜λ©΄ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

tsxλŠ” νƒ€μž…μŠ€ν¬λ¦½νŠΈ + JSXμ΄λ―€λ‘œ μ œλ„€λ¦­μ˜ κΊΎμ‡ κ΄„ν˜Έμ™€ JSX νƒœκ·Έμ˜ κΊΎμ‡ κ΄„ν˜Έλ₯Ό ν˜Όλ™ν•˜μ—¬ λ¬Έμ œκ°€ μƒκΈ°λŠ” 것이닀!

μ œλ„€λ¦­ 뢀뢄에 extends ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μ»΄νŒŒμΌλŸ¬μ—κ²Œ νŠΉμ • νƒ€μž…μ˜ ν•˜μœ„ νƒ€μž…λ§Œ 올 수 μžˆμŒμ„ ν™•μ‹€ν•˜κ²Œ μ•Œλ €μ£Όλ©΄ λœλ‹€.

보톡 μ œλ„€λ¦­μ„ μ‚¬μš©ν•  λ•ŒλŠ” function ν‚€μ›Œλ“œλ‘œ μ„ μ–Έν•˜λŠ” κ²½μš°κ°€ λ§Žλ‹€.

// μ—λŸ¬ λ°œμƒ: JSX element 'T' has no corresponding closing tag
const arrowExampleFunc = <T>(arg: T): T[] => {
	return new Array(3).fill(arg);
}

// μ—λŸ¬ λ°œμƒ X (객체 νƒ€μž… ν™•μž₯)
const arrowExampleFunc2 = <T extends {}>(arg: T): T[] => {
	return new Array(3).fill(arg);
};

 

 

μ œλ„€λ¦­ μ‚¬μš©λ²•

ν•¨μˆ˜μ˜ μ œλ„€λ¦­

μ–΄λ–€ ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜λ‚˜ λ°˜ν™˜ 값에 λ‹€μ–‘ν•œ νƒ€μž…μ„ λ„£κ³  싢을 λ•Œ

function ReadOnlyRepository<T>(target: ObjectType<T> | EntitySchema<T> | string):
Repository<T> {
	return getConnection("ro").getRepository(target);
}

// User μ—”ν‹°ν‹°μ˜ 읽기 μ „μš© μ €μž₯μ†Œλ₯Ό κ°€μ Έμ™€μ„œ 읽기 μ „μš© μž‘μ—…μ„ μˆ˜ν–‰
const userRepository = ReadOnlyRepository<User>(User);

 

호좜 μ‹œκ·Έλ‹ˆμ²˜(call signature)의 μ œλ„€λ¦­

호좜 μ‹œκ·Έλ‹ˆμ²˜: νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ ν•¨μˆ˜ νƒ€μž… λ¬Έλ²•μœΌλ‘œ, ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜μ™€ λ°˜ν™˜ νƒ€μž…μ„ 미리 μ„ μ–Έν•˜λŠ” 것

호좜 μ‹œκ·Έλ‹ˆμ²˜λ₯Ό μ‚¬μš©ν•  λ•Œ μ œλ„€λ¦­ νƒ€μž…μ„ 어디에 μœ„μΉ˜μ‹œν‚€λŠ”μ§€μ— 따라 νƒ€μž…μ˜ λ²”μœ„μ™€ μ œλ„€λ¦­ νƒ€μž…μ„ μ–Έμ œ ꡬ체 νƒ€μž…μœΌλ‘œ ν•œμ •ν• μ§€ κ²°μ •ν•  수 μžˆλ‹€.

export type UseRequesterHookType = <RequestData = void, ResponseData = void>(
baseURL?:
    string | Headers,
    defaultHeader?: Headers
) => [RequestStatus, Requester<RequestData, ResponseData>];

UseRequesterHookType νƒ€μž…μ˜ ν•¨μˆ˜λ₯Ό μ‹€μ œ ν˜ΈμΆœν•  λ•Œ μ œλ„€λ¦­ νƒ€μž…μ„ ꡬ체 νƒ€μž…μœΌλ‘œ ν•œμ •ν•œλ‹€.

μš”μ²­κ³Ό 응닡 νƒ€μž…μ΄ νŠΉμ • νƒ€μž…μœΌλ‘œ κ³ μ •λ˜μ§€ μ•Šκ³  상황에 맞좰 λ³€κ²½ κ°€λŠ₯해진닀.

 

μ œλ„€λ¦­ 클래슀

μ œλ„€λ¦­ 클래슀: μ™ΈλΆ€μ—μ„œ μž…λ ₯된 νƒ€μž…μ„ 클래슀 내뢀에 μ μš©ν•  수 μžˆλŠ” 클래슀

μ œλ„€λ¦­ 클래슀λ₯Ό μ‚¬μš©ν•˜λ©΄ 클래슀 전체에 걸쳐 νƒ€μž… λ§€κ°œλ³€μˆ˜κ°€ μ μš©λœλ‹€.

class Box<T> {
    private _value: T;

    constructor(value: T) {
        this._value = value;
    }

    getValue(): T {
        return this._value;
    }

    setValue(value: T): void {
        this._value = value;
    }
}

// Box<string> νƒ€μž…μ˜ 객체 생성
const stringBox = new Box<string>("Hello, world!");
console.log(stringBox.getValue()); // 좜λ ₯: Hello, world!

// Box<number> νƒ€μž…μ˜ 객체 생성
const numberBox = new Box<number>(42);
console.log(numberBox.getValue()); // 좜λ ₯: 42

 

 

νŠΉμ • λ©”μ„œλ“œλ§Œμ„ λŒ€μƒμœΌλ‘œ μ œλ„€λ¦­μ„ μ μš©ν•˜λ €λ©΄ ν•΄λ‹Ή λ©”μ„œλ“œλ₯Ό μ œλ„€λ¦­ λ©”μ„œλ“œλ‘œ μ„ μ–Έν•˜λ©΄ λœλ‹€. 

class Utility {
    static identity<T>(value: T): T {
        return value;
    }
}

// identity λ©”μ„œλ“œ 호좜 μ‹œ νƒ€μž…μ„ 지정
console.log(Utility.identity<string>("TypeScript")); // 좜λ ₯: TypeScript
console.log(Utility.identity<number>(123)); // 좜λ ₯: 123

 

μ œν•œλœ μ œλ„€λ¦­

μ œν•œλœ μ œλ„€λ¦­: νƒ€μž… λ§€κ°œλ³€μˆ˜μ— λŒ€ν•œ μ œμ•½ 쑰건을 μ„€μ •ν•˜λŠ” κΈ°λŠ₯

μ œν•œλœ μ œλ„€λ¦­μ„ μ‚¬μš©ν•˜λ©΄ νƒ€μž… μ•ˆμ „μ„±μ„ κ°•ν™”ν•˜λ©΄μ„œλ„ μœ μ—°ν•œ ν™•μž₯이 κ°€λŠ₯ν•˜λ‹€.

type ErrorRecord<Key extends string> = 
    Exclude<Key, ErrorCodeType> extends never 
        ? Partial<Record<Key, boolean>> 
        : never;

λ°”μš΄λ“œ νƒ€μž… λ§€κ°œλ³€μˆ˜(bounded type parameters): νƒ€μž… λ§€κ°œλ³€μˆ˜κ°€ νŠΉμ • νƒ€μž…μœΌλ‘œ λ¬Άμ˜€μ„ λ•Œ(bind) ν‚€λ₯Ό 지칭

그리고 μ—¬κΈ°μ„œ string을 ν‚€μ˜ μƒν•œ ν•œκ³„(upper bound)라고 ν•œλ‹€.

상속 받을 수 μžˆλŠ” νƒ€μž…μœΌλ‘œλŠ” κΈ°λ³Έ νƒ€μž…λΏλ§Œ μ•„λ‹ˆλΌ 상황에 따라 μΈν„°νŽ˜μ΄μŠ€λ‚˜ ν΄λž˜μŠ€λ„ μ‚¬μš©ν•  수 μžˆλ‹€.

λ˜ν•œ μœ λ‹ˆμ˜¨ νƒ€μž…μ„ μƒμ†ν•΄μ„œ μ„ μ–Έν•  μˆ˜λ„ μžˆλ‹€.

 

ν™•μž₯된 μ œλ„€λ¦­

μ œλ„€λ¦­ νƒ€μž…μ€ μ—¬λŸ¬ νƒ€μž…μ„ 상속받을 수 있으며 νƒ€μž… λ§€κ°œλ³€μˆ˜λ₯Ό μ—¬λŸ¬ 개 λ‘˜ μˆ˜λ„ μžˆλ‹€.

<Key extends string> 이런 μ‹μœΌλ‘œ νƒ€μž…μ„ μ œμ•½ν•˜λ©΄ μ œλ„€λ¦­μ˜ μœ μ—°μ„±μ„ μžƒμ–΄λ²„λ¦°λ‹€.

μ œλ„€λ¦­μ˜ μœ μ—°μ„±μ„ μžƒμ§€ μ•ŠμœΌλ©΄μ„œ νƒ€μž…μ„ μ œμ•½ν•΄μ•Ό ν•  λ•ŒλŠ” νƒ€μž… λ§€κ°œλ³€μˆ˜μ— μœ λ‹ˆμ˜¨ νƒ€μž…μ„ μƒμ†ν•΄μ„œ μ„ μ–Έν•˜λ©΄ λœλ‹€.

<Key extends string | number>

 

μœ λ‹ˆμ˜¨ νƒ€μž…μœΌλ‘œ Tκ°€ μ—¬λŸ¬ νƒ€μž…μ„ λ°›κ²Œ ν•  μˆ˜λŠ” μžˆμ§€λ§Œ, νƒ€μž… λ§€κ°œλ³€μˆ˜κ°€ μ—¬λŸ¬ 개일 λ•ŒλŠ” μ²˜λ¦¬ν•  수 μ—†λ‹€. 이럴 λ•ŒλŠ” λ§€κ°œλ³€μˆ˜λ₯Ό ν•˜λ‚˜ 더 μΆ”κ°€ν•˜μ—¬ μ„ μ–Έν•œλ‹€.

export class APIResponse<Ok, Err = string>{
	private readonly data: Ok | Err | null;
    private readonly status: ResponseStatus;
    private readonly statusCode: number | null;
    
    // ...
    
}

 

 

μ œλ„€λ¦­ μ˜ˆμ‹œ

μ œλ„€λ¦­μ˜ μž₯점은 λ‹€μ–‘ν•œ νƒ€μž…μ„ λ°›κ²Œ ν•¨μœΌλ‘œμ¨ μ½”λ“œλ₯Ό 효율적으둜 μž¬μ‚¬μš©ν•˜λŠ” 것이닀.

ν˜„μ—…μ—μ„œ κ°€μž₯ 많이 μ œλ„€λ¦­μ΄ ν™œμš©λ˜λŠ” 곳은, API 응닡 κ°’μ˜ νƒ€μž…μ„ 지정할 λ•Œμ΄λ‹€.

export interface MobileApiResponse<Data> {
    data: Data;
    statusCode: string;
    statusMessage?: string;
}

export const fetchPriceInfo = (): Promise<MobileApiResponse<PriceInfo>> => {
// ...
};
export const fetchOrderInfo = (): Promise<MobileApiResponse<Order>> => {
// ...
};

API 응닡 값에 따라 λ‹¬λΌμ§€λŠ” dataλ₯Ό μ œλ„€λ¦­ νƒ€μž… Data둜 μ„ μ–Έν•˜μ—¬ μΈν„°νŽ˜μ΄μŠ€ MobileApiResponseλ₯Ό λ§Œλ“€μ—ˆλ‹€.

λ‹€μ–‘ν•œ API 응닡 κ°’μ˜ νƒ€μž…μ— MobileApiResponseλ₯Ό ν™œμš©ν•΄μ„œ μ½”λ“œλ₯Ό 효율적으둜 μž¬μ‚¬μš©ν•  수 μžˆλ‹€.

 

 

μ œλ„€λ¦­μ„ ꡳ이 μ‚¬μš©ν•˜μ§€ μ•Šμ•„λ„ λ˜λŠ” νƒ€μž…

μ œλ„€λ¦­μ„ ν•„μš”ν•œ 곳에 μ‚¬μš©ν•˜λ©΄ 가독성과 νš¨μœ¨μ„±μ„ 높일 수 μžˆμ§€λ§Œ, 

ν•„μš”ν•˜μ§€ μ•Šμ€ 곳에 μ œλ„€λ¦­μ„ μ‚¬μš©ν•˜λ©΄ μ½”λ“œ 길이만 λŠ˜μ–΄λ‚˜κ³  가독성을 ν•΄μΉ  수 μž‡λ‹€.

// λΆˆν•„μš”ν•œ μ œλ„€λ¦­
type GType<T> = T;
type RequirementType = "USE" | "UN_USE" | "NON_SELECT";
interface Order {
    getRequirement(): GType<RequirementType>;
}

// μ•„λž˜μ™€ κ°™λ‹€
type RequirementType = "USE" | "UN_USE" | "NON_SELECT";
interface Order {
    getRequirement(): RequirementType;
}

 

ꡳ이 μ œλ„€λ¦­μ„ μ‚¬μš©ν•˜μ§€ μ•Šκ³  νƒ€μž… λ§€κ°œλ³€μˆ˜λ₯Ό κ·ΈλŒ€λ‘œ μ„ μ–Έν•˜λŠ” 것과 같은 κΈ°λŠ₯이닀. 

 

any μ‚¬μš©

anyλ₯Ό μ‚¬μš©ν•˜λ©΄ μ œλ„€λ¦­μ˜ μž₯점(μž¬μ‚¬μš©μ„±)κ³Ό νƒ€μž… μΆ”λ‘  및 νƒ€μž… 검사λ₯Ό ν•  수 μžˆλŠ” 이점을 λˆ„λ¦΄ 수 μ—†κ²Œ λœλ‹€.

any νƒ€μž…μ€ λͺ¨λ“  νƒ€μž…μ„ ν—ˆμš©ν•˜κΈ° λ•Œλ¬Έμ— 사싀상 μžλ°”μŠ€ν¬λ¦½νŠΈμ™€ λ™μΌν•œ 방식이며, μ œλ„€λ¦­μ„ 포함해 νƒ€μž…μ„ μ§€μ •ν•˜λŠ” μ˜λ―Έκ°€ μ‚¬λΌμ§€κ²Œ λœλ‹€.

 

가독성을 κ³ λ €ν•˜μ§€ μ•Šμ€ μ‚¬μš©

μ œλ„€λ¦­μ΄ κ³Όν•˜κ²Œ μ‚¬μš©λ˜λ©΄ 가독성을 ν•΄μΉ˜κΈ° λ•Œλ¬Έμ— μ½”λ“œλ₯Ό 읽고 νƒ€μž…μ„ μ΄ν•΄ν•˜κΈ° μ–΄λ €μ›Œμ§„λ‹€.

λ³΅μž‘ν•œ μ œλ„€λ¦­μ€ 의미 λ‹¨μœ„λ‘œ λΆ„ν• ν•΄μ„œ μ‚¬μš©ν•˜λŠ” 게 μ’‹λ‹€.

 

 

 

ref. μš°μ•„ν•œ νƒ€μž…μŠ€ν¬λ¦½νŠΈ with λ¦¬μ•‘νŠΈ - μ œλ„€λ¦­ 파트 

 

728x90