TypeScript์ ๊ฝ๐ท์ด๋ผ ํ ์ ์๋ ํ์ ์ถ๋ก (Type Inference)๊ณผ Generic Type์ ๋ถ๋ถ ํ์ ์ถ๋ก ๋ฌธ์ ๋ํด ์์๋ด ๋๋ค.
๋ค์ด๊ฐ๊ธฐ ์์..
์ฌ๋ฌ๋ถ์ ๋ชจ๋๋๋ผ๋ ๊ฐ๋ ์ ์๊ณ ์๋์?
์ด๋ค ๊ฐ๋ ์ ๋น๋ก์ ๊นจ๋ซ๋ ์๊ฐ์ ‘Aha moment’๋ผ ๋ถ๋ฅด๊ธฐ๋ ํ๋๋ฐ, ๋๊ธ๋ผ์ค ํฌ๋กํฌ๋์ ๋ง์ ์ธ์ฉํ์๋ฉด, ๋ชจ๋๋๋ฅผ ์ดํดํ๋ Aha moment๋ฅผ ๊ฒช๋ ์๊ฐ, ๋ค๋ฅธ ์ฌ๋์๊ฒ ๋ชจ๋๋๋ฅผ ์ค๋ช ํ ์ ์๊ฒ ๋๋ ์ ์ฃผ์ ๊ฑธ๋ฆฐ๋ค๊ณ ํ๋ค.
๊ทธ๋ฐ๋ฐ, ๋ชจ๋๋๋ง ๊ทธ๋ฌํ๊ฐ? ์ธ์์ ๋ง์ ์ดํด์ ๋ฌธ์ ๋ค์ด ๋น์ทํ ์ํฉ์ ๋์ฌ ์๋ค. ๊ฐ๋ตํํ ์ ๋นํ ๋น์ ๋ฅผ ํตํด ์ค๋ช ํ๋ ๊ฒ ์ฒ์ ์ ํ๋ ์ฌ๋๋ค์๊ฒ ์ฝ๊ฒ ๋๊ปด์ง๊ฒ ์ง๋ง, ๋๋ถ๋ถ์ ๊ฒฝ์ฐ๋ ๊ฒฐ๊ตญ ๋ถํ์ํ ์คํด๋ฅผ ๊ฐ์ ธ์จ๋ค.
์ถ์ฒ ์๋ฌธ
๋ง์ฝ ์ด ๊ธ์ ์ฝ๊ณ ๋ถ๋ถ ํ์ ์ถ๋ก ๋ฌธ์ ์ ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋ฐฉ์์ด ์ ์ดํด๋์ง ์๋๋ค๋ฉด ์ ๋ ๋ชจ๋๋์ ์ ์ฃผ์ ๊ฑธ๋ฆฐ ๊ฒ์ ๋๋ค...
์ด ์ฃผ์ ์ ๋ํด ๊ด์ฌ์ด ์๊ธด๋ค๋ฉด ๊ผญ ๋๊น์ง ํํค์ณ ๊ฐ๋ ์ ์ดํดํ์๊ธธ ๋ฐ๋๋๋ค.
์ด ๊ธ์ ํ์ ์คํฌ๋ฆฝํธ๋ฅผ ํ์ฉํด ๋ณด์์ผ๋ฉฐ, Generic Type์ ๋ํ ๊ธฐ๋ณธ์ ์ธ ์ดํด๊ฐ ์์์ ๋ฐํ์ผ๋ก ํฉ๋๋ค.
์์๋ก ๋ฑ์ฅํ๋ custom swr hook์ ๋ถ๋ถํ์ ์ถ๋ก ๋ฌธ์ ๋ swr@2.1.0์์ ๋ฐ์ํ์๊ณ , swr@2.1.1์์ ์ด ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋์์ต๋๋ค.
custom swr hook์ BlockingDataํ์ ๊น์ง ๋งค๋๋ฌ์ด ํ์ ์ถ๋ก ์ ๊ฐ๋ฅํ๊ฒ ํ๊ณ ์ ํ๋ค๋ฉด, ๋ถ๋ถํ์ ์ถ๋ก ์ ๋ฌธ์ ์ ํด๊ฒฐ๋ฐฉ์๊น์ง ํ์ธํด๋ณด์๊ธธ ์ถ์ฒ๋๋ฆฝ๋๋ค.
TL;DR
TypeScript Generic์ ์๋ฒฝํ์ง ์์ต๋๋ค. ์กฐ๊ธ ๋ ์์ธํ ๋งํด๋ณด์๋ฉด Generic์ ๋ถ๋ถ์ ์ธ ์ถ๋ก ์ ์๋ฒฝํ์ง ์์ต๋๋ค.
TypeScript๋ ๋ชจ๋ Generic์ ๋ํด์ ์ถ๋ก ํด ๋ผ ์ ์์ง๋ง, ๋ถ๋ถ์ ์ธ ์ถ๋ก ์ ๋ํด์๋ ์ง์ํ์ง ์์ต๋๋ค. (์ ์ด๋ ํ์ฌ๊น์ง๋)
๋ถ๋ถ ์ถ๋ก ํ์ ์ด ์ ํ๋๋ ์ํฉ์์ Generic Function์ ๋ํด ์ ์์ ์ธ ์ถ๋ก ์ ์ํด ๋ช ๊ฐ์ง ๋ฐฉ์์ด ์กด์ฌํฉ๋๋ค.
ํ์ฌ ํ์ฌ์์๋ ์๋ฒ ์ํ๊ด๋ฆฌ๋๊ตฌ๋ก swr์ ์ฑํํ์ฌ ํ์ฉ์ค์ด๊ณ , ์ฌ์ฌ์ฉ์ ๊ณ ๋ คํ custom swr hook์ ๋ง๋ค์ด ํ์ฉ ์ค์ ์์ต๋๋ค.
๋๋ต์ ์ธ ๋ชจ์ต์ ์๋์ ๊ฐ์ต๋๋ค.
export type UseRequestOption<Data> = SWRConfiguration<Data>;
export function useRequest<Data>(endpoint: string, options?: UseRequestOption<Data>) {
return useSWR(
endpoint,
async url => {
const resp = await axiosInstance.get<Data>(url);
return resp.data;
},
options,
);
}
์ด๋ฅผ ํ์ฉํด, ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ๋๋ก ์ปค์คํ ํ ์ ๋ง๋ค์ด ํ์ฉํ๊ฒ ๋ฉ๋๋ค.
interface Post {
title: string;
}
interface UsePostsReturn {
posts: Post[];
}
export function usePosts(option?: UseRequestOption<UsePostsReturn>) {
return useRequest<UsePostsReturn>('/api/posts', option);
}
์ค์ ์ฌ์ฉํ ๋์๋ ์ด๋ฐ ๋ชจ์ต์ด ๋ฉ๋๋ค. ์ฌ์ฉ์ฒ์์ swr option์ ํ์ฉํ์ฌ ๊ฐ๊ฐ์ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋๋ก ํฉ๋๋ค.
์ด ์ํฉ์์ ๋ถ๋ถ ํ์ ์ถ๋ก ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค.
const Component = () => {
const { data } = usePosts();
// ^? data: UsePostsReturn
};
swr์ ๋ฐํ๊ฐ ์ค data๋ undefined๊ฐ ๋ ์ ์๋ ์ํฉ์ด ์กด์ฌํฉ๋๋ค. ์ฆ ์บ์๋ ๋ฐ์ดํฐ๊ฐ ์๋ ์ํฉ์ ๋๋ค.
swr์ suspense๋ fallbackData ์ต์ ์ ์ฃผ์ด BlockingData(NotNullable) ํ์ ์ผ๋ก ๋ง๋ค ์ ์์ง๋ง, ์ฃผ์ง ์์์์๋ BlockingData๋ก ์ฒ๋ฆฌ๋์์ต๋๋ค. ์์ธ์ ์ดํด๋ด ์๋ค.
TypeScript Generic์ ์๋ฒฝํ์ง ์์ต๋๋ค. ์กฐ๊ธ ๋ ์์ธํ ๋งํด๋ณด์๋ฉด Generic์ ๋ถ๋ถ์ ์ธ ์ถ๋ก ์ ์๋ฒฝํ์ง ์์ต๋๋ค.
TypeScript๋ ๋ชจ๋ Generic์ ๋ํด์ ์ถ๋ก ํด ๋ผ ์ ์์ง๋ง, ๋ถ๋ถ์ ์ธ ์ถ๋ก ์ ๋ํด์๋ ์ง์ํ์ง ์์ต๋๋ค. (์ ์ด๋ ํ์ฌ๊น์ง๋)
type Foo = <A = any, B = any, C = any>(a: A, b: B, c: C) => [A, B, C]
const foo: Foo = (a, b, c) => [a, b, c]
const bar1 = foo(1, 2, 3)
// ^? const bar1: [number, number, number]
const bar2 = foo<number>(1, 2, 3)
// ^? const bar2: [number, any, any] - Passing just one explicit generic to `foo` removes type inference
โญ๏ธ Generic ์ธ์๋ฅผ ๋ถ๋ถ์ ์ผ๋ก ์ ๋ฌํ ๋, ๋๋จธ์ง๋ ๊ธฐ๋ณธ๊ฐ์ ๋ฐ๋ผ๊ฐ ๋ฟ์ ๋๋ค. ์ด๊ฒ์ด ๋ถ๋ถ ํ์ ์ถ๋ก ์ ๋ฌธ์ ์ ๋๋ค.
๊ทธ๋ผ, swr์์ ๋ฐ์ํ ๋ฌธ์ ๋ฅผ ์ง์ด๋ณด๊ธฐ ์ํด swr์ ๋จ์ํํ ๋ฒ์ ์ SWRHook์ ๋ง๋ค์ด๋ด ๋๋ค.
type MySWRConfig<Data> = { fallbackData?: Data };
type BlockingData<Data = any, Options = MySWRConfig<Data>> = Options extends undefined
? false
: Options extends { fallbackData: Data; }
? true
: false;
type MySWRResponse<Data, Options = MySWRConfig<Data>> = {
data: BlockingData<Data, Options> extends true ? Data : Data | undefined;
};
interface MyHook {
<Data>(key: string): MySWRResponse<Data>;
<Data, Config extends MySWRConfig<Data> | undefined = MySWRConfig<Data>>(key: string, config: Config | undefined): MySWRResponse<Data, Required<Config>>;
}
const mySWR: MyHook = () => {}
mySWR์ ํตํด ํ์ธํด๋ด ์๋ค.
type User = {
name: string;
age: number
};
const fallbackData: User = { name: 'Jonghwan', age: 17 };
const t0 = mySWR<User>('/');
const t1 = mySWR<User>('/', {});
const t2 = mySWR<User>('/', { fallbackData });
โญ๏ธ Quiz. mySWR๋ก ์์ฑ๋ t0, t1, t2 ์ค BlockingData๊ฐ ์๋ ๊ฒ(data=undefined๊ฐ ๋ ์ ์๋)์ ๋ฌด์์ด๊ณ ์ ๊ทธ๋ด๊น์?
์์์ ๋ถ๋ถ ํ์ ์ถ๋ก ์ ์๋ฒฝํ์ง ์๋ค๊ณ ํ์ต๋๋ค. ์ธ ๊ฐ์ง ๊ฒฝ์ฐ, ๋ชจ๋ ๋ค Generic ์ ํ์ ์๋ฒฝํ ์ง์ ํด์ฃผ์ง ์์์ต๋๋ค.
๊ทธ๋ ๋ค๋ฉด ๋ถ๋ถ์ถ๋ก ์ ํ์ ์ํด, Config๋ ๊ธฐ๋ณธ๊ฐ์ธ MySWRConfig<Data>๊ฐ ๋งตํ๋ฉ๋๋ค.(์ด๋ ์ค์ ํจ์(mySWR)๋ฅผ ํธ์ถํ ๋ ์ ๋ฌํ config์ ๋ชจ์์ด ์ด๋ป๋ ์๊ด์์ต๋๋ค.)
์ค์ ์ ๋ฌ๋ option์ ๋ชจ์๊ณผ๋ ๋ฌด๊ดํ๊ฒ, Generic ์ ํ์ ์๋ค๊ณ ํ๋จ๋๋ ๋ฐํ ํ์ ์ Requred<Config>…, ์ด ํ์ ์ { fallbackData: Data } ์ ํ์ด ๋๊ณ .. BlockingData ๊ฒ์ฌ์๋ ํต๊ณผํ๊ณ , Data๊ฐ ๋ฉ๋๋ค.
์ด๊ฒ์ด ์ฐ๋ฆฌ์ custom SWR hook์ด NonNullableํ ์ด์ ์์ต๋๋ค.
๊ทธ๋ผ, ๋ถ๋ถ ํ์ ์ถ๋ก ์ ๋ฌธ์ ๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ ์ ์์ต๋๊น?
๋ถ๋ถํ์ ์ถ๋ก ์ ์ดํดํ๊ณ , Generic ์ ํ๋ค์ ์์ ํด ๋ณผ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ , TypeScript๊ฐ ์จ์ ํ ํ์ ์ถ๋ก ํ ์ ์๋๋ก ๋ง๋ค์ด์ค๋๋ค.
interface MyHook {
<Data = any>(key: string): MySWRResponse<Data>;
// ๋ถ๋ถํ์
์ถ๋ก ์ผ๋ก ํญ์ ์กด์ฌํ๋ ๊ธฐ๋ณธ๊ฐ์์ undefined๊ฐ ๋ ์ ์์์ผ๋ก ๋ณ๊ฒฝํฉ๋๋ค
- <Data = any, Config extends MySWRConfig<Data> | undefined = MySWRConfig<Data>>(key: string, config: Config | undefined): MySWRResponse<Data, Required<Config>>;
+ <Data = any, Config extends MySWRConfig<Data> | undefined = MySWRConfig<Data> | undefined>(key: string, config: Config): MySWRResponse<Data, Config>;
}
Generic ์ธ์๋ฅผ ๋ชจ๋ ์ถ๋ก ๊ฐ๋ฅํ๊ฒ ๋ง๋ค์ด์ฃผ๊ฑฐ๋, ๋ชจ๋ ๋ช ์์ ์ผ๋ก ์ง์ ํฉ๋๋ค.
const config = { fallbackData };
const t0 = mySWR<User>('/');
t0.data.name; // data = undefined
const t1 = mySWR<User>('/', {});
t1.data.name; // data = undefined
const t2 = mySWR<User, typeof config>('/', config);
t2.data.name; // data = User
// ์กฐ๊ธ ๋ ๋ณธ๋์ swr์ ๊ตฌํํ๋ค๊ณ ๊ฐ์ , (fetcher๋ฅผ ํฌํจ)
const t3 = mySWR('/', () => { return {} as User }, { });
t3.data // data = User | undefined
const t4 = mySWR('/', () => { return {} as User }, { fallbackData });
t4.data // data = User
๋ง์กฑ์ค๋ฝ์ต๋๋ค. ์ด์ ์ผ ํ์ ์ถ๋ก ์ด ์ ์์ ์ผ๋ก ๋์ํฉ๋๋ค.
ํ์ง๋ง, ๊ฐ๋ฐ์๊ฐ ๋ชจ๋ ์ ๋ค๋ฆญ ์ ํ์ ํ ๋นํด ์ฃผ๋ ์ผ์ ํ์ค์ธ๊ณ์์๋ ์ฌ์ฌ์ฉ์ฑ์ ๊ณ ๋ คํ ์ถ์ํ์ ๋ฒ์์ ๋ฐ๋ผ ์ด๋ ต๊ณ ๋ฒ๊ฑฐ๋ก์ด ์ผ์ด ๋ ๊ฐ๋ฅ์ฑ์ด ๋ง์ต๋๋ค.
๋ ๋์ ๋ฐฉ๋ฒ์ ์์๊น์?
๋ถ๋ถ ์ถ๋ก ํ์ ์ด ์ ํ๋๋ ์ํฉ์์ Generic Function์ ๋ํด ์ ์์ ์ธ ์ถ๋ก ์ ์ํด ๋ช ๊ฐ์ง ๋ฐฉ์์ด ์กด์ฌํฉ๋๋ค.
์ ๋ด์ฉ ์ค ์ปค๋ง ๋ฐฉ์์ผ๋ก createSWRHook์ ๋ง๋ค์ด Custom SWR Hook์ ๋ง๋ค์๊ณ , ์ ํํ๊ฒ ํ์ ์ถ๋ก ์ด ๋ ์ ์๋๋ก ๊ฐ์ ํด ๋ณผ ์ ์์ต๋๋ค.
์ฌ๊ธฐ๊น์ง ๋ถ๋ถํ์ ์ถ๋ก ๋ฌธ์ ์ ๋ํด ์์๋ณด์๊ณ , ๋์๊ฐ ๋ณด๋ค ์๋ฒฝํ ํ์ ์ถ๋ก ์ํ๋ฅผ ๋ง๋๋ ํ ํฌ๋์ ๋ํด ์ดํด๋ณด์์ต๋๋ค.
'๊ฐ๋ฐ > ๊ธฐํ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
ํ์ค์ ์ธ ๊ฐ๋ฅ์ฑ๊ณผ ๋ ผ๋ฆฌ์ ์ธ ๊ฐ๋ฅ์ฑ (0) | 2023.09.06 |
---|---|
Avoid Hasty Abstractions(AHA) (0) | 2023.08.28 |
shadcn/ui (0) | 2023.08.24 |
Interface vs Type ๋ฌด์์ ์จ์ผํ๋ ? (0) | 2023.04.09 |
2022๋ ์ ๋ฐ๊ธฐํ๊ณ : ํ์ ๋ฒ ๋ ์ฌ๋์ ๋คํ์ ๋์ ๋ณด์ง ์๋๋ค (0) | 2022.09.02 |