Skip to content

Commit f7b35bc

Browse files
ericyangpanclaude
andcommitted
fix(types): improve type safety in validators and page components
Schema validators: - Add explicit type imports for all schema types - Replace 'any' with proper type annotations - Ensure type-safe parameter passing to validation functions Page components: - Add explicit type casting for locale parameter - Add undefined fallback for optional download URLs - Remove unnecessary 'as any' type assertions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 58513bf commit f7b35bc

File tree

3 files changed

+57
-29
lines changed

3 files changed

+57
-29
lines changed

src/app/[locale]/clis/[slug]/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,14 @@ export default async function CLIPage({
9999
description: cli.description,
100100
vendor: cli.vendor,
101101
websiteUrl,
102-
downloadUrl: cli.resourceUrls?.download,
102+
downloadUrl: cli.resourceUrls?.download || undefined,
103103
version: cli.latestVersion,
104104
platforms: cli.platforms,
105-
pricing: cli.pricing as any,
105+
pricing: cli.pricing,
106106
license: cli.license ? translateLicenseText(cli.license, tGlobal) : undefined,
107107
},
108108
category: 'clis',
109-
locale,
109+
locale: locale as Locale,
110110
})
111111

112112
return (

src/app/[locale]/ides/[slug]/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ export default async function IDEPage({
107107
softwareVersion: ide.latestVersion,
108108
description: ide.description,
109109
url: websiteUrl,
110-
downloadUrl: ide.resourceUrls?.download,
111-
installUrl: ide.resourceUrls?.download,
110+
downloadUrl: ide.resourceUrls?.download || undefined,
111+
installUrl: ide.resourceUrls?.download || undefined,
112112
author: {
113113
'@type': 'Organization',
114114
name: ide.vendor,

src/lib/metadata/schemas/validators.ts

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,18 @@
44
* Helps catch errors during development
55
*/
66

7-
import type { AnySchema } from './types'
7+
import type {
8+
AnySchema,
9+
SchemaArticle,
10+
SchemaBreadcrumbList,
11+
SchemaFAQPage,
12+
SchemaItemList,
13+
SchemaOffer,
14+
SchemaOrganization,
15+
SchemaProduct,
16+
SchemaSoftwareApplication,
17+
SchemaWebSite,
18+
} from './types'
819

920
/**
1021
* Validation result
@@ -37,30 +48,30 @@ export function validateSchema(schema: AnySchema): ValidationResult {
3748
if (schema['@type']) {
3849
switch (schema['@type']) {
3950
case 'Organization':
40-
validateOrganization(schema, errors, warnings)
51+
validateOrganization(schema as SchemaOrganization, errors, warnings)
4152
break
4253
case 'SoftwareApplication':
43-
validateSoftwareApplication(schema, errors, warnings)
54+
validateSoftwareApplication(schema as SchemaSoftwareApplication, errors, warnings)
4455
break
4556
case 'Product':
46-
validateProduct(schema, errors, warnings)
57+
validateProduct(schema as SchemaProduct, errors, warnings)
4758
break
4859
case 'Article':
4960
case 'TechArticle':
5061
case 'BlogPosting':
51-
validateArticle(schema, errors, warnings)
62+
validateArticle(schema as SchemaArticle, errors, warnings)
5263
break
5364
case 'ItemList':
54-
validateItemList(schema, errors, warnings)
65+
validateItemList(schema as SchemaItemList, errors, warnings)
5566
break
5667
case 'BreadcrumbList':
57-
validateBreadcrumbList(schema, errors, warnings)
68+
validateBreadcrumbList(schema as SchemaBreadcrumbList, errors, warnings)
5869
break
5970
case 'WebSite':
60-
validateWebSite(schema, errors, warnings)
71+
validateWebSite(schema as SchemaWebSite, errors, warnings)
6172
break
6273
case 'FAQPage':
63-
validateFAQPage(schema, errors, warnings)
74+
validateFAQPage(schema as SchemaFAQPage, errors, warnings)
6475
break
6576
default:
6677
warnings.push(`Unknown schema type: ${schema['@type']}`)
@@ -77,7 +88,7 @@ export function validateSchema(schema: AnySchema): ValidationResult {
7788
/**
7889
* Validate Organization schema
7990
*/
80-
function validateOrganization(schema: any, errors: string[], warnings: string[]) {
91+
function validateOrganization(schema: SchemaOrganization, errors: string[], warnings: string[]) {
8192
if (!schema.name) errors.push('Organization: Missing required field "name"')
8293
if (!schema.url) errors.push('Organization: Missing required field "url"')
8394
else if (!isValidUrl(schema.url)) errors.push(`Organization: Invalid URL "${schema.url}"`)
@@ -90,7 +101,11 @@ function validateOrganization(schema: any, errors: string[], warnings: string[])
90101
/**
91102
* Validate SoftwareApplication schema
92103
*/
93-
function validateSoftwareApplication(schema: any, errors: string[], warnings: string[]) {
104+
function validateSoftwareApplication(
105+
schema: SchemaSoftwareApplication,
106+
errors: string[],
107+
warnings: string[]
108+
) {
94109
if (!schema.name) errors.push('SoftwareApplication: Missing required field "name"')
95110
if (!schema.applicationCategory) {
96111
errors.push('SoftwareApplication: Missing required field "applicationCategory"')
@@ -119,7 +134,7 @@ function validateSoftwareApplication(schema: any, errors: string[], warnings: st
119134
/**
120135
* Validate Product schema
121136
*/
122-
function validateProduct(schema: any, errors: string[], warnings: string[]) {
137+
function validateProduct(schema: SchemaProduct, errors: string[], warnings: string[]) {
123138
if (!schema.name) errors.push('Product: Missing required field "name"')
124139
if (!schema.description) errors.push('Product: Missing required field "description"')
125140
if (!schema.brand) errors.push('Product: Missing required field "brand"')
@@ -132,7 +147,7 @@ function validateProduct(schema: any, errors: string[], warnings: string[]) {
132147
/**
133148
* Validate Article schema
134149
*/
135-
function validateArticle(schema: any, errors: string[], warnings: string[]) {
150+
function validateArticle(schema: SchemaArticle, errors: string[], warnings: string[]) {
136151
if (!schema.headline) errors.push('Article: Missing required field "headline"')
137152
if (!schema.author) errors.push('Article: Missing required field "author"')
138153
if (!schema.datePublished) errors.push('Article: Missing required field "datePublished"')
@@ -154,13 +169,13 @@ function validateArticle(schema: any, errors: string[], warnings: string[]) {
154169
/**
155170
* Validate ItemList schema
156171
*/
157-
function validateItemList(schema: any, errors: string[], warnings: string[]) {
172+
function validateItemList(schema: SchemaItemList, errors: string[], warnings: string[]) {
158173
if (!schema.itemListElement) {
159174
errors.push('ItemList: Missing required field "itemListElement"')
160175
} else if (!Array.isArray(schema.itemListElement)) {
161176
errors.push('ItemList: "itemListElement" must be an array')
162177
} else {
163-
schema.itemListElement.forEach((item: any, index: number) => {
178+
schema.itemListElement.forEach((item, index: number) => {
164179
if (!item['@type'] || item['@type'] !== 'ListItem') {
165180
errors.push(`ItemList: Item ${index + 1} missing @type "ListItem"`)
166181
}
@@ -183,13 +198,17 @@ function validateItemList(schema: any, errors: string[], warnings: string[]) {
183198
/**
184199
* Validate BreadcrumbList schema
185200
*/
186-
function validateBreadcrumbList(schema: any, errors: string[], _warnings: string[]) {
201+
function validateBreadcrumbList(
202+
schema: SchemaBreadcrumbList,
203+
errors: string[],
204+
_warnings: string[]
205+
) {
187206
if (!schema.itemListElement) {
188207
errors.push('BreadcrumbList: Missing required field "itemListElement"')
189208
} else if (!Array.isArray(schema.itemListElement)) {
190209
errors.push('BreadcrumbList: "itemListElement" must be an array')
191210
} else {
192-
schema.itemListElement.forEach((item: any, index: number) => {
211+
schema.itemListElement.forEach((item, index: number) => {
193212
if (!item['@type'] || item['@type'] !== 'ListItem') {
194213
errors.push(`BreadcrumbList: Item ${index + 1} missing @type "ListItem"`)
195214
}
@@ -209,7 +228,7 @@ function validateBreadcrumbList(schema: any, errors: string[], _warnings: string
209228
/**
210229
* Validate WebSite schema
211230
*/
212-
function validateWebSite(schema: any, errors: string[], warnings: string[]) {
231+
function validateWebSite(schema: SchemaWebSite, errors: string[], warnings: string[]) {
213232
if (!schema.name) errors.push('WebSite: Missing required field "name"')
214233
if (!schema.url) errors.push('WebSite: Missing required field "url"')
215234
else if (!isValidUrl(schema.url)) errors.push(`WebSite: Invalid URL "${schema.url}"`)
@@ -222,15 +241,15 @@ function validateWebSite(schema: any, errors: string[], warnings: string[]) {
222241
/**
223242
* Validate FAQPage schema
224243
*/
225-
function validateFAQPage(schema: any, errors: string[], warnings: string[]) {
244+
function validateFAQPage(schema: SchemaFAQPage, errors: string[], warnings: string[]) {
226245
if (!schema.mainEntity) {
227246
errors.push('FAQPage: Missing required field "mainEntity"')
228247
} else if (!Array.isArray(schema.mainEntity)) {
229248
errors.push('FAQPage: "mainEntity" must be an array')
230249
} else if (schema.mainEntity.length === 0) {
231250
warnings.push('FAQPage: "mainEntity" is empty')
232251
} else {
233-
schema.mainEntity.forEach((question: any, index: number) => {
252+
schema.mainEntity.forEach((question, index: number) => {
234253
if (!question['@type'] || question['@type'] !== 'Question') {
235254
errors.push(`FAQPage: Question ${index + 1} missing @type "Question"`)
236255
}
@@ -249,10 +268,15 @@ function validateFAQPage(schema: any, errors: string[], warnings: string[]) {
249268
/**
250269
* Validate Offer or array of Offers
251270
*/
252-
function validateOffers(offers: any, errors: string[], _warnings: string[], parentType: string) {
271+
function validateOffers(
272+
offers: SchemaOffer | SchemaOffer[],
273+
errors: string[],
274+
_warnings: string[],
275+
parentType: string
276+
) {
253277
const offerArray = Array.isArray(offers) ? offers : [offers]
254278

255-
offerArray.forEach((offer: any, index: number) => {
279+
offerArray.forEach((offer, index: number) => {
256280
if (!offer['@type'] || offer['@type'] !== 'Offer') {
257281
errors.push(`${parentType}: Offer ${index + 1} missing @type "Offer"`)
258282
}
@@ -299,12 +323,16 @@ export function validateAndLog(schema: AnySchema, pageName: string): void {
299323

300324
if (result.errors.length > 0) {
301325
console.error('❌ Errors:')
302-
result.errors.forEach(error => console.error(` - ${error}`))
326+
for (const error of result.errors) {
327+
console.error(` - ${error}`)
328+
}
303329
}
304330

305331
if (result.warnings.length > 0) {
306332
console.warn('⚠️ Warnings:')
307-
result.warnings.forEach(warning => console.warn(` - ${warning}`))
333+
for (const warning of result.warnings) {
334+
console.warn(` - ${warning}`)
335+
}
308336
}
309337

310338
console.groupEnd()

0 commit comments

Comments
 (0)