cf. 우아한 타입스크립트 - 3장
목차
functuon
const
class Button {
name = "MyButton";
handleClickV1 = function() {
// 여기서의 this는 이 함수를 호출하는 주체(HTML 요소 등)가 될 수 있음
console.log(this.name); // ⚠️ 에러 가능성 높음
}
handleClickV2 = () => {
// 화살표 함수의 this는 무조건 Button 인스턴스를 가리킴
console.log(this.name); // ✅ "MyButton" (안전)
}
}
오버로딩
function: 중복 선언으로 오버로딩 가능const: 타입 단언 혹은 복잡한 인터페이스 정의가 필요최근은 export 가 대세
default export
export default는 1개만 가능P.S. export default가 강제되는 경우
- Next.js의 페이지 파일(
page.tsx)
-React.lazy사용할 때
// user.ts
const User = { name: "A" };
export default User;
...
// import 시 중괄호 {} 없음, 'User'를 'MyUser'로 이름 바꿔서 가져옴
import MyUser from './user';
named export
export 가능// user.ts
export const User = { name: "A" };
export const Role = "Admin";
...
// import 시 중괄호 {} 필수
import { User, Role } from './user';

https://www.notion.so/lsjoon/generic-refactoring-2f665bf2693d8063aac0c0c2eceb3e66?source=copy_link
// page.tsx
export async function generateMetadata({ searchParams }: {
searchParams: Promise<{ [key: string]: string | undefined }>;
}): Promise<Metadata> {
const searchParam = (await searchParams) as
| string
| string[][]
| Record<string, string>
| URLSearchParams
| undefined;
const queryString = new URLSearchParams(searchParam).toString();
return getDefaultMetaData({
title: "mock",
openGraphUrl: `https://mock-url.com${queryString ? "?" + queryString : ""}`,
});
}
///
type mockProps = {
searchParams: Promise<{ [key: string]: string | undefined }>;
};
export default async function MockPage({ searchParams }: mockProps) {
const searchParam = await searchParams;
const serverId = !searchParam?.temporaryServerId
? Number(searchParam?.serverId || 1)
: Number(searchParam?.temporaryServerId || 1);
if (isNaN(serverId)) {
return await getUserInfo(searchParam);
}
const [queryData, serverData] = await Promise.all([
queryMock(
serverId,
searchParam?.keyword,
searchParam?.nickname,
{ lastId: undefined, pageSize: 70 },
header
),
queryMockServer(),
]);
return (
<Mock
mockData={queryData}
mockServerData={serverData}
serverId={serverId}
nickname={searchParam?.nickname}
keyword={searchParam?.keyword}
/>
);
}
// type.ts
export type SearchParams = Record<string, string | undefined>;
export type SearchParamsWithServerId = {
serverId?: string;
} & SearchParams;
export type PageProps<T extends SearchParams = SearchParams> = {
params: Promise<T>;
searchParams: Promise<T>;
};
/** generateMetadata 전용 제네릭 타입 **/
export type GenerateMetadata<T extends SearchParams> = (
props: PageProps<T>
) => Promise<Metadata>;
// page.tsx
export async function generateMetadata({ searchParams }: PageProps<mockProps>) {
const searchParam = await searchParams;
return getDefaultMetaData({
title: "mock",
openGraphUrl: `https://mock-url.com${searchParam}`,
});
}
///
type mockProps = {
keyword: string;
nickname: string;
} & SearchParamsWithServerId;
export default async function MockPage({ searchParams }: PageProps<mockProps>) {
const { serverId, keyword, nickname } = await searchParams;
const id = isNaN(Number(serverId)) ? 1 : Number(serverId);
const [queryData, serverData] = await Promise.all([
queryMock(
id,
keyword,
nickname,
{ lastId: undefined, pageSize: 70 },
header
),
queryMockServer(),
]);
return (
<Mock
mockData={queryData}
mockServerData={serverData}
serverId={id}
nickname={nickname}
keyword={keyword}
/>
);
}
상수 파일을 만드는 것보다, type으로 추론 가능하도록 구현하는게 타입 스크립트를 더 올바르게 사용하는 방향성이 아닐까?
코드 레빗 도입 고민