Leveraging schema-dts Type Safety
Svead uses schema-dts
which provides TypeScript types for all 800+
Schema.org types. Here’s how to leverage it effectively:
Type-Safe Schema Creation
Get full autocomplete and type checking:
import type { BlogPosting, Person, Organization } from 'schema-dts';
import type { SchemaOrgProps } from 'svead';
// Strongly typed - autocomplete available for all properties
const author: Person = {
'@type': 'Person',
name: 'John Doe',
url: 'https://example.com/author/john',
sameAs: [
'https://twitter.com/johndoe',
'https://github.com/johndoe',
],
jobTitle: 'Developer', // Autocomplete knows valid properties
};
const blog_post: BlogPosting = {
'@type': 'BlogPosting',
headline: 'My Blog Post',
author: author, // Type-safe nesting
datePublished: '2023-08-22T10:00:00Z',
// TypeScript will error on invalid properties
};
const schema: SchemaOrgProps['schema'] = blog_post;
Reusable Typed Schemas
Create reusable, type-safe schema fragments:
import type { Person, Organization, ImageObject } from 'schema-dts';
// Typed person schema
const create_person = (name: string, url: string): Person => ({
'@type': 'Person',
name,
url,
sameAs: [
`https://twitter.com/${name.toLowerCase()}`,
`https://github.com/${name.toLowerCase()}`,
],
});
// Typed organization schema
const create_organization = (
name: string,
url: string,
): Organization => ({
'@type': 'Organization',
name,
url,
logo: {
'@type': 'ImageObject',
url: `${url}/logo.png`,
},
});
// Use in schemas with type safety
const schema: SchemaOrgProps['schema'] = {
'@type': 'BlogPosting',
headline: 'Article',
author: create_person('John Doe', 'https://example.com'),
publisher: create_organization('Example', 'https://example.com'),
};
Union Types for Flexible Schemas
schema-dts supports union types for properties that accept multiple types:
import type { BlogPosting, Person, Organization } from 'schema-dts';
// author can be Person, Organization, or array of either
const schema: BlogPosting = {
'@type': 'BlogPosting',
headline: 'Article',
// Single person
author: {
'@type': 'Person',
name: 'John',
},
// Or organization
// author: {
// '@type': 'Organization',
// name: 'Company'
// },
// Or array of authors
// author: [
// { '@type': 'Person', name: 'John' },
// { '@type': 'Person', name: 'Jane' }
// ]
};
Array Schemas with Type Safety
When using arrays, schema-dts maintains type safety:
import type { ItemList, ListItem } from 'schema-dts';
const item_list: ItemList = {
'@type': 'ItemList',
itemListElement: [
{
'@type': 'ListItem',
position: 1,
item: {
'@type': 'Product',
name: 'Product 1',
},
},
{
'@type': 'ListItem',
position: 2,
item: {
'@type': 'Product',
name: 'Product 2',
},
},
],
};
Complex Nested Schemas
schema-dts handles deep nesting with full type safety:
import type {
Recipe,
HowToStep,
NutritionInformation,
} from 'schema-dts';
const recipe: Recipe = {
'@type': 'Recipe',
name: 'Chocolate Cake',
recipeIngredient: ['flour', 'sugar', 'cocoa'],
recipeInstructions: [
{
'@type': 'HowToStep',
name: 'Mix ingredients',
text: 'Mix all ingredients together',
image: 'https://example.com/step1.jpg',
},
{
'@type': 'HowToStep',
name: 'Bake',
text: 'Bake at 350°F for 30 minutes',
},
],
nutrition: {
'@type': 'NutritionInformation',
calories: '350 calories',
fatContent: '12g',
proteinContent: '5g',
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: '4.8',
reviewCount: '124',
},
};
Using WithContext Type
schema-dts provides WithContext<T>
to include @context
:
import type { WithContext, BlogPosting } from 'schema-dts';
// This type includes @context automatically
const schema: WithContext<BlogPosting> = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: 'Article',
};
// SchemaOrg component adds @context if missing, so this is optional
Type Guards and Validation
Use TypeScript to validate schema structure:
import type { Thing } from 'schema-dts';
// Function that only accepts valid Thing types
function validate_schema(schema: Thing): boolean {
// TypeScript ensures schema has correct structure
return !!schema['@type'];
}
// Type-safe schema manipulation
const schema: Thing = {
'@type': 'BlogPosting',
headline: 'Title',
};
if (validate_schema(schema)) {
// Safe to use
}
Importing Specific Types
Import only what you need for better tree-shaking:
// Import specific types
import type {
BlogPosting,
Person,
Organization,
ImageObject,
BreadcrumbList,
ListItem,
} from 'schema-dts';
// Instead of importing everything
// import type { Thing } from 'schema-dts';
Common Type Patterns
Frequently used type combinations:
import type {
BlogPosting,
Person,
Organization,
ImageObject,
} from 'schema-dts';
// Author (can be Person or Organization)
type Author = Person | Organization;
// Image (can be string URL or ImageObject)
type Image = string | ImageObject | ImageObject[];
// Reusable function with types
function create_blog_post(
headline: string,
author: Author,
image?: Image,
): BlogPosting {
return {
'@type': 'BlogPosting',
headline,
author,
...(image && { image }),
datePublished: new Date().toISOString(),
};
}
// Type-safe usage
const post = create_blog_post(
'My Post',
{ '@type': 'Person', name: 'John' },
'https://example.com/image.jpg',
);
Catching Errors at Compile Time
schema-dts catches errors before runtime:
import type { BlogPosting } from 'schema-dts';
const schema: BlogPosting = {
'@type': 'BlogPosting',
headline: 'Title',
// ❌ TypeScript error: 'invalidProperty' doesn't exist on BlogPosting
// invalidProperty: 'value',
// ❌ TypeScript error: wrong type
// datePublished: 123,
// ✅ Correct
datePublished: '2023-08-22T10:00:00Z',
};
Extending Schemas with Custom Properties
While schema-dts is strict, you can add custom properties:
import type { BlogPosting } from 'schema-dts';
// Extend with custom properties if needed
interface CustomBlogPosting extends BlogPosting {
customField?: string;
}
const schema: CustomBlogPosting = {
'@type': 'BlogPosting',
headline: 'Title',
customField: 'Custom data',
};
// Note: Custom properties won't validate with Schema.org validators