Bài 29.1: Typing getStaticProps và getStaticPaths

Bài 29.1: Typing getStaticProps và getStaticPaths
Chào mừng bạn trở lại với hành trình khám phá Next.js! Sau khi đã làm quen với sức mạnh của Static Site Generation (SSG) thông qua getStaticProps
và getStaticPaths
, bước tiếp theo để làm chủ môi trường phát triển hiện đại chính là tích hợp TypeScript. Việc gõ kiểu (typing) cho các hàm dữ liệu như getStaticProps
và getStaticPaths
không chỉ giúp code của bạn an toàn hơn, dễ bảo trì hơn mà còn mang lại trải nghiệm tuyệt vời nhờ tính năng tự động hoàn thành (autocompletion) và phát hiện lỗi sớm từ trình soạn thảo.
Trong bài viết này, chúng ta sẽ đi sâu vào cách tận dụng TypeScript để định nghĩa kiểu cho getStaticProps
và getStaticPaths
, đảm bảo rằng dữ liệu bạn nhận được và xử lý luôn đúng định dạng mong muốn. Hãy cùng bắt đầu!
Tại sao phải Typing getStaticProps và getStaticPaths?
Next.js cung cấp các kiểu dựng sẵn (built-in types) cho các hàm này, giúp chúng ta dễ dàng tích hợp TypeScript. Lợi ích của việc này bao gồm:
- An toàn kiểu (Type Safety): Ngăn chặn các lỗi runtime phổ biến liên quan đến việc truy cập các thuộc tính không tồn tại hoặc sai kiểu dữ liệu.
- Tự động hoàn thành (Autocompletion): Trình soạn thảo của bạn (như VS Code) có thể hiểu rõ cấu trúc dữ liệu bạn đang làm việc, cung cấp gợi ý code thông minh, giúp viết code nhanh hơn và chính xác hơn.
- Phát hiện lỗi sớm: TypeScript sẽ báo lỗi ngay trong quá trình phát triển nếu bạn sử dụng dữ liệu sai cách, thay vì đợi đến lúc chạy ứng dụng.
- Dễ đọc và bảo trì: Việc định rõ kiểu dữ liệu giúp các nhà phát triển khác (hoặc chính bạn trong tương lai) dễ dàng hiểu code mong đợi kiểu dữ liệu nào.
Typing getStaticProps
Hàm getStaticProps
được sử dụng để tìm nạp dữ liệu tại thời điểm build (build time) và truyền nó dưới dạng props
tới component Page. Khi sử dụng TypeScript, chúng ta cần định nghĩa kiểu cho cả dữ liệu trả về (các props
) và tham số đầu vào (context
).
Next.js cung cấp kiểu GetStaticProps
từ package next
.
1. Định nghĩa kiểu cho Props
Đầu tiên, bạn cần định nghĩa kiểu cho dữ liệu mà getStaticProps
sẽ trả về và component Page sẽ nhận được. Thường thì chúng ta sử dụng một interface
.
interface Product {
id: number;
name: string;
price: number;
}
interface ProductPageProps {
product: Product;
}
Trong ví dụ này, Product
là kiểu dữ liệu cho một sản phẩm, và ProductPageProps
là kiểu cho toàn bộ props
mà component Page sẽ nhận (ở đây là một object chứa thuộc tính product
có kiểu Product
).
2. Sử dụng kiểu GetStaticProps
Bây giờ, chúng ta sẽ sử dụng kiểu GetStaticProps
và truyền kiểu ProductPageProps
mà chúng ta vừa tạo vào đó.
// pages/products/[id].tsx
import { GetStaticProps, GetStaticPropsContext } from 'next';
import { ParsedUrlQuery } from 'querystring';
interface Product {
id: number;
name: string;
price: number;
}
interface ProductPageProps {
product: Product;
}
// Component Page nhận props đã được typed
const ProductPage: React.FC<ProductPageProps> = ({ product }) => {
return (
<div>
<h1>{product.name}</h1>
<p>Price: ${product.price}</p>
</div>
);
};
// Typing cho getStaticProps
// <ProductPageProps> là kiểu của object được trả về trong 'props'
// <ParsedUrlQuery> là kiểu mặc định cho context.params
export const getStaticProps: GetStaticProps<ProductPageProps, ParsedUrlQuery> = async (context) => {
// Giả lập tìm nạp dữ liệu từ API hoặc file
const productId = context.params?.id; // context.params có kiểu ParsedUrlQuery
// Trong thực tế, bạn sẽ gọi API hoặc đọc file dựa vào productId
const dummyProduct: Product = {
id: Number(productId),
name: `Sample Product ${productId}`,
price: 19.99 * Number(productId),
};
// Kiểm tra nếu không tìm thấy sản phẩm (ví dụ thực tế phức tạp hơn)
if (!dummyProduct || isNaN(dummyProduct.id)) {
return {
notFound: true, // Trả về trang 404
}
}
return {
props: {
product: dummyProduct,
},
// revalidate: 60 // Optional: Incremental Static Regeneration
};
};
// getStaticPaths cũng cần được export cho dynamic routes
export { getStaticPaths } from './'; // Sẽ giải thích ở phần sau
export default ProductPage;
Giải thích:
- Chúng ta import
GetStaticProps
vàGetStaticPropsContext
từnext
. GetStaticProps
là một Generic Type. Chúng ta truyềnProductPageProps
vào làm tham số Generic đầu tiên (<ProductPageProps>
). Điều này cho TypeScript biết rằng hàmgetStaticProps
này sẽ trả về một object mà sau đó sẽ được truyền vào component Page dưới dạngprops
có kiểuProductPageProps
.- Tham số Generic thứ hai của
GetStaticProps
là kiểu chocontext.params
. Mặc định, nó làParsedUrlQuery
từ modulequerystring
của Node.js.ParsedUrlQuery
về cơ bản là một object nơi các giá trị có thể làstring
hoặcstring[]
hoặcundefined
. - Bên trong hàm
getStaticProps
, khi truy cậpcontext.params
, TypeScript đã biết rằng nó có kiểuParsedUrlQuery
. Nếu bạn biết chắc chắn kiểu của các tham số trong dynamic route, bạn có thể định nghĩa một
interface
cụ thể hơn chocontext.params
để có tính năng autocompletion tốt hơn:interface ProductParams extends ParsedUrlQuery { id: string; // Định nghĩa rõ 'id' là string } export const getStaticProps: GetStaticProps<ProductPageProps, ProductParams> = async (context) => { const productId = context.params?.id; // Bây giờ TypeScript biết productId là string | undefined // ... phần còn lại tương tự };
Sử dụng
extends ParsedUrlQuery
là một cách hay để đảm bảo tương thích nếu sau này Next.js thêm thuộc tính mới vàocontext.params
.- Kiểu trả về của
getStaticProps
cũng được kiểm tra. TypeScript đảm bảo rằng object bạn trả về từreturn { props: ... }
phải có thuộc tínhprops
chứa dữ liệu có kiểuProductPageProps
. Nó cũng hỗ trợ các trường hợp trả vềnotFound: true
hoặcredirect: { destination: ..., permanent: ... }
.
Typing getStaticPaths
Hàm getStaticPaths
được sử dụng trong các dynamic routes (ví dụ: pages/posts/[slug].tsx
) để khai báo những đường dẫn (paths) nào cần được render tĩnh tại thời điểm build. Tương tự như getStaticProps
, chúng ta cũng có kiểu GetStaticPaths
từ package next
.
1. Sử dụng kiểu GetStaticPaths
Kiểu GetStaticPaths
cũng là một Generic Type. Tham số Generic của nó là kiểu của các params
trong mỗi object path
mà bạn trả về.
// pages/posts/[slug].tsx
import { GetStaticPaths, GetStaticPathsContext } from 'next';
import { ParsedUrlQuery } from 'querystring';
// Định nghĩa kiểu cho params trong path
interface PostParams extends ParsedUrlQuery {
slug: string;
}
// Typing cho getStaticPaths
// <PostParams> là kiểu của object context.params VÀ kiểu của object params trong mỗi item của mảng 'paths'
export const getStaticPaths: GetStaticPaths<PostParams> = async () => {
// Giả lập tìm nạp danh sách slugs từ API hoặc file
const posts = [
{ slug: 'bai-viet-1' },
{ slug: 'bai-viet-2' },
{ slug: 'bai-viet-3' },
];
// Chuyển đổi danh sách posts thành định dạng paths mà Next.js yêu cầu
const paths = posts.map(post => ({
params: {
slug: post.slug, // TypeScript kiểm tra slug có kiểu PostParams['slug'] (string) không
},
}));
return {
paths,
fallback: false, // Hoặc 'blocking' hoặc true
};
};
// getStaticProps cho trang post cũng cần được export và typed
// ... (phần getStaticProps tương tự như ví dụ Product ở trên, sử dụng PostParams)
// export const getStaticProps: GetStaticProps<PostPageProps, PostParams> = async (context) => { ... }
// Component Page
// ... (phần component React nhận props)
// export default PostPage;
Giải thích:
- Chúng ta import
GetStaticPaths
vàGetStaticPathsContext
từnext
. - Kiểu
GetStaticPaths
nhận một tham số Generic (<PostParams>
) là kiểu của objectparams
mà bạn sẽ trả về trong mỗi phần tử của mảngpaths
. Điều này giúp TypeScript xác minh rằng cấu trúc củapaths
là chính xác. - Chúng ta định nghĩa
interface PostParams
để mô tả cấu trúc của objectparams
, ở đây chỉ có thuộc tínhslug
kiểustring
. - Bên trong
getStaticPaths
, khi chúng ta tạo mảngpaths
, TypeScript kiểm tra từng objectparams
bên trong và đảm bảo nó khớp với kiểuPostParams
đã khai báo. - Nếu bạn không truyền tham số Generic nào cho
GetStaticPaths
(ví dụ:const getStaticPaths: GetStaticPaths = async () => { ... }
), TypeScript sẽ sử dụng kiểu mặc định (ParsedUrlQuery
) choparams
, nhưng việc định nghĩa kiểu tường minh giúp code rõ ràng hơn và cung cấp autocompletion tốt hơn khi làm việc vớiparams
. - Kiểu trả về của
getStaticPaths
({ paths: ..., fallback: ... }
) cũng được TypeScript kiểm tra để đảm bảo đúng cấu trúc.
Tóm lược lợi ích
Việc dành thời gian để gõ kiểu cho getStaticProps
và getStaticPaths
ngay từ đầu sẽ mang lại nhiều lợi ích đáng kể cho dự án Next.js sử dụng TypeScript của bạn:
- Giảm thiểu lỗi: Bắt được các lỗi liên quan đến dữ liệu sai kiểu hoặc thiếu thuộc tính ngay trong quá trình code.
- Nâng cao năng suất: Autocompletion giúp bạn viết code nhanh hơn và ít phải tra cứu tài liệu.
- Cải thiện khả năng bảo trì: Code trở nên rõ ràng, dễ hiểu và dễ thay đổi hơn trong tương lai.
- Hỗ trợ Refactoring: Khi bạn thay đổi cấu trúc dữ liệu, TypeScript sẽ báo cáo tất cả những nơi cần cập nhật.
Bằng cách áp dụng các kiểu GetStaticProps
và GetStaticPaths
cùng với việc định nghĩa các interface
cho dữ liệu của bạn, bạn đang xây dựng một ứng dụng Next.js mạnh mẽ, đáng tin cậy và dễ làm việc hơn rất nhiều. Đây là một bước tiến quan trọng trong việc phát triển các ứng dụng web hiệu quả với Next.js và TypeScript.
Comments