3장 고급 타입

3.1 타입스크립트만의 독자적 타입 시스템

3.1.1 any 타입

3.1.2 unknown 타입


let unknownvalue unknown;
unknownValue = 100; // any 타입과 유사하게 숫자이든
unknownvalue = "hello world"; // 문자열이든
unknownvalue = () =console.log("this is any type"); // 함수이든 상관없이 할당이 가능하지만
let someValuel any = unknownvalue; // (0) any 타입으로 선언된 변수를 제외한 다른 변수는
모두 할당이 불가
let someValue2 number = unknownvalue; // (X)
let someValue3 string = unknownvalue; // (X)
// 할당하는 시점에서는 에러가 발생하지 않음
const unknownFunction unknown = () => console.log("this is unknown type");
// 하지만 실행 시에는 에러가 발생; Error: Object is of type 'unknown’.ts (2571)
unknownFunction();

3.1.3 void 타입

3.1.4 never 타입

function generateError(res Response) never {
    throw new Error(res.getMessageO);
}
- 무한히 함수가 실행되는 경우
    - 드물지만 함수 내에서 무한 루프를 실행하는 경우가 있을 수 있다. 무한 루프는 결국 함수가 종료되지 않음을 의미하기 때문에 값을 반환하지 못한다

3.1.5 Array 타입

const arrayl Array<number | string> = [1, "string"];
const array2: number[] | string[] = [1, "string"];
// 후자의 방식은 아래와 같이 선언할 수도 있다
const arrays (number | string)[] = [1, "string"];
let tuple [number] = [1]
tuple = [1, 2]; // 불가능
tuple = [1, "string"]; // 불가능
let tuple [number, string, boolean] = [1, "string", true]; // 여러 타입과 혼합도 가능하다

3.1.6 enum 타입

enum ProgrammingLanguage {
Typescript = "Typescript",
Javascript = "Javascript",
Java = 300,
Python = 400,
Kotlin, // 401
Rust, // 402
Go, // 403
}
ProgrammingLanguage[300] // "Java"
ProgrammingLanguage[200]; // undefined를 출력하지만 별다른 에러를 발생시키지 않는다
// 다음과 같이 선언하면 위와 같은 문제를 방지할 수 있다
const enum ProgrammingLanguage {
    // ...
}
const enum NUMBER {
    ONE = 1,
    TWO = 2,
}
const myNumber: NUMBER = 100; // NUMBER enum에서 100을 관리하고 있지 않지만 이는 에러를 발생시키지 않는다
const enum STRING_NUMBER {
ONE = "ONE",
TWO = "TWO",
}
const myStringNumber STRING_NUMBER = "THREE"; // Error

3.2 타입 조합

3.1.1 교차 타입(Intersection)

type Productltem = {
    id number;
    name: string;
    type: string;
    price number
    imageUrl string
    quantity number;
}

type ProductItemWithDiscount = Productitem & { discountAmount: number };

3.1.2 유니온 타입(Union)

type Cardltem = {
    id number;
    name string
    type: string;
    imageUrl string;
}
type PromotionEventltem = Productltem | Cardltem;

const printPromotionltem = (item PromotionEventltem) => {
    console.log(itein.name); // 0
    console.log(iteni.quantity); // 컴파일 에러 발생
};

3.1.3 인덱스 시그니처(Index Signatures)

interface IndexSignatureEx {
[key string] number;
}

interface IndexSignatureEx2 {
    [key string] number | boolean;
    length number;
    isValid boolean;
    name string; // 에러 발생
}

3.1.4 인덱스드 액세스 타입(Indexed Access Types)

type Example = {
    a number
    b string;
    c boolean
};
type IndexedAccess = Example["a"];
type IndexedAccess2 = Example["a" | "b"]; // number | string
type IndexedAccessB = Example[keyof Example]; // number | string | boolean
type ExAlias = "b" | "c";
type IndexedAccess4 = Exainple[ExAlias]; // string | boolean
type IndexedAccess2 = Example["a" | "b"]; // number | string
type IndexedAccessB = Example[keyof Example]; // number | string | boolean
type ExAlias = "b" | "c";
type IndexedAccess4 = Example[ExAlias]; // string ! boolean

3.1.5 맵드 타입(Mapped Types)

type Example = {
    a number;
    b: string;
    c boolean;
};

type Subset<T> = {
[K in keyof T]?: T[K];
};
// keyof Example = a | b | c
// T[K] : 프로퍼티 'K'의 타입을 'T'에서 가져옴

const aExample Subset<Example> = { a: 3 };
const bExample: Subset<Example> = { b "hello" };
const acExample: Subset<Example> = { a 4, c true };
const BottomSheetMap = {
    RECENT_CONTACTS: RecentContactsBottomSheet,
    CARD_SELECT: CardSelectBottomSheet,
    SORT_FILTER SortFilterBottomSheet,
    PRODUCT_SELECT: ProductSelectBottomSheet,
    REPLY_CARD_SELECT: ReplyCardSelectBottomSheet,
    RESEND ResendBottomSheet,
    STICKER StickerBottomSheet,
    BASE: null,
};

export type BOTTOM_SHEET_ID = keyof typeof BottomSheetMap;
// "RECENT.CONTACTS" | "CARD.SELECT" | "SORT_FILTER" | "PRODUCT.SELECT" | "REPLY_CARD_SELECT" | "RESEND" | "STICKER" | "BASE";

// 불필요한 반복이 발생한다
type BottomSheetStore = {
RECENT_CONTACTS: {
    resolver?: (payload any) => void;
    args? any;
    isOpened boolean;
};
CARD_SELECT: {
    resolver?: (payload any) => void;
    args?; any;
    isOpened boolean;
}
SORT_FILTER: {
    resolver? (payload: any) => void;
    args? any;
    isOpened: boolean;
}
// ...
}

// Mapped Types를 통해 효율적으로 타입을 선언할 수 있다
type Bottomsheetstore = {
    [index in BOTTOM_SHEET_ID]: {
        resolver? (payload any) => void;
        args?: any;
        isOpened boolean
    }
}
type BottomSheetStore = {
    [index in BOTTOM_SHEET_ID as '${index}_BOTTOM_SHEET']: {
        resolver?: (payload any) => void;
        args? any;
        isOpened boolean;
    }
}

3.1.6 템플릿 리터럴 타입(Template Literal Types)

type Stage =
| "init"
| "select-image"
| "edit-image"
| "decorateord"
| "capture-image";
type StageName = `${Stage}-stage`;
// 'init-stage' i 'select-image-stage' ! ’edit-image-stage' i 'decorate-card-stage'i
capture-image-stage'

3.1.7 제네릭(Generic)

type ExampleArrayType<T> = T[];
const arrayl ExampleArrayType<string> = ["치킨", "피자", "우동"];
interface SubmitEvent<T = HTMLElement> extends SyntheticEvent<T> { submitter T;

}
function exampleFunc2<T>(arg T) number {
return arg.length; // 에러 발생: Property 'length' does not exist on type 'T'
}
interface TypeWithLength {
    length number;
}
function exampleFunc2<T extends TypeWithLength>(arg: T): number {
    return arg.length;
}
// 에러 발생: JSX element 'T' has no corresponding closing tag
const arrowExampleFunc = <T>(argT): T[] => {
    return new Array(3).fill.(arg);
};

// 에러 발생 X
const arrowExampleFunc2 = <T extends {}>(arg T) T[] => {
    return new Array(3).fiVL(arg);
}

3.2 제네릭 사용법

3.2.1 함수의 제네릭

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

3.2.2 호출 시그니처의 제네릭

interface useSelectPaginationProps<T> {
    categoryAtom RecoilState<number>;
    filterAtom RecoilState<string[]>; sortAtom
    RecoilState<SortType>;
    fetcherFunc (props CommonListRequest) => Promise<DefaultResponse<ContentListRes
    ponse<T>>>
}

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

3.2.3 제네릭 클래스

class LocalDB<T> {
    // ...
    async put(table string, row T) Promise<T> {
        return new Promise<T>((resolved, rejected) => { /* T 타입의 데이터를 DB에 저장 */ });
    }
    async get(table:string, key any) Promise<T> {
        return new Promise<T>((resolved, rejected) => { /* T 타입의 데이터를 DB에서 가져옴 */
         })
    }
    async getTable(table string) Promise<T[]> {
        return new Promise<r[]>((resolved, rejected) => { /* T[] 타입의 데이터를 DB에서 가져옴*/ })
    }
}

export default class IndexedDB implements ICacheStore {
    private _DB? LocalDB<{ key string; value Promise<Record<string, unknown>>;
    cacheTTL: number }>;
    private DB() {
        if (!this._DB) {
            this._DB = new LocalDB("localCache", { ver: 6, tables; [{ name: TABLE_NAME,
            keyPath "key" }] });
        }
        return this._DB;
    }
    //...
}

3.2.4 제한된 제네릭

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

3.2.5. 확장된 제네릭

 <Key extends string>
 <Key extends string | number>

3.2.6. 제네릭 예시

export interface MobileApiResponse<Data> {
    data Data;
    statusCode string
    statusMessage? string;
}
export const fetchPriceInfo = (): Promise<MobileApiResponse<PriceInfo>> => {
    const priceUrl = "https:~~~" // url 주소
    return request({
        method "GET",
        url priceUrl,
    })
}

export const fetchOrderlnfo = () Promise<MobileApiResponse<Order>> => {
    const orderUrl =  "https:~~~" // url 주소
    return requests({
        method: "GET",
        url orderUrl,
    })
};
제네릭을 굳이 사용하지 않아도 되는 타입
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 사용하기
type ReturnType<T = any> = {
    // ...
};
가독성을 고려하지 않은 사용

끝!