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