refactor(types): centralize type definitions in shared types file
Move all interface and type definitions to src/types/index.ts to reduce code duplication and improve maintainability. This includes types for components, blog posts, personal info, and localization.
This commit is contained in:
@@ -1,19 +1,6 @@
|
|||||||
import type { Lang } from '../i18n/utils';
|
import { type AuthorCardProps } from '@/types';
|
||||||
import { personalInfo } from '@/lib/data';
|
import { personalInfo } from '@/lib/data';
|
||||||
|
|
||||||
interface AuthorCardProps {
|
|
||||||
lang: Lang;
|
|
||||||
author?: {
|
|
||||||
name: string;
|
|
||||||
bio?: string;
|
|
||||||
avatar?: string;
|
|
||||||
website?: string;
|
|
||||||
twitter?: string;
|
|
||||||
github?: string;
|
|
||||||
linkedin?: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function AuthorCard({ lang, author }: AuthorCardProps) {
|
export default function AuthorCard({ lang, author }: AuthorCardProps) {
|
||||||
// Default author info based on personalInfo
|
// Default author info based on personalInfo
|
||||||
const defaultAuthor = {
|
const defaultAuthor = {
|
||||||
|
|||||||
@@ -1,27 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslations, type Lang } from '../i18n/utils';
|
import { useTranslations, type Lang } from '@/i18n/utils';
|
||||||
|
import { type BlogPost, type BlogListProps } from '@/types';
|
||||||
/**
|
|
||||||
* Blog post interface definition
|
|
||||||
*/
|
|
||||||
interface BlogPost {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
image: string;
|
|
||||||
slug: string;
|
|
||||||
tags: string[];
|
|
||||||
date: string;
|
|
||||||
readTime: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Props interface for BlogList component
|
|
||||||
*/
|
|
||||||
interface BlogListProps {
|
|
||||||
posts: BlogPost[];
|
|
||||||
lang: Lang;
|
|
||||||
baseUrl?: string; // Base URL for blog posts, defaults to '/blog/posts/'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reusable blog list component that displays blog posts in a grid layout
|
* Reusable blog list component that displays blog posts in a grid layout
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ import { personalInfo } from "@/lib/data";
|
|||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { defaultLang } from "@/i18n/ui";
|
import { defaultLang } from "@/i18n/ui";
|
||||||
|
import { type FooterProps } from "@/types";
|
||||||
interface FooterProps {
|
|
||||||
lang?: Lang;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Footer({ lang: propLang }: FooterProps) {
|
export default function Footer({ lang: propLang }: FooterProps) {
|
||||||
// 优先使用props传入的语言,如果没有则尝试从HTML lang属性获取
|
// 优先使用props传入的语言,如果没有则尝试从HTML lang属性获取
|
||||||
|
|||||||
@@ -3,13 +3,12 @@ import LanguageSwitcher from "./LanguageSwitcher";
|
|||||||
import ThemeToggle from "./ui/theme-toggle";
|
import ThemeToggle from "./ui/theme-toggle";
|
||||||
import { useTranslations, getLocalizedPath, type Lang } from "@/i18n/utils";
|
import { useTranslations, getLocalizedPath, type Lang } from "@/i18n/utils";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
import { Menu, X } from "lucide-react";
|
import { Menu, X } from "lucide-react";
|
||||||
import { defaultLang } from "@/i18n/ui";
|
import { defaultLang } from "@/i18n/ui";
|
||||||
|
import { type GlassHeaderProps } from "@/types";
|
||||||
|
|
||||||
// 从window.document.documentElement.lang获取当前语言
|
// 从window.document.documentElement.lang获取当前语言
|
||||||
interface GlassHeaderProps {
|
|
||||||
lang?: Lang;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function GlassHeader({ lang: propLang }: GlassHeaderProps) {
|
export default function GlassHeader({ lang: propLang }: GlassHeaderProps) {
|
||||||
// 优先使用props传入的语言,如果没有则尝试从HTML lang属性获取
|
// 优先使用props传入的语言,如果没有则尝试从HTML lang属性获取
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { getLocalizedPath, type Lang } from "@/i18n/utils";
|
|||||||
import { languages as i18nLanguages, defaultLang } from "@/i18n/ui";
|
import { languages as i18nLanguages, defaultLang } from "@/i18n/ui";
|
||||||
import { Languages, Check, ChevronDown } from "lucide-react";
|
import { Languages, Check, ChevronDown } from "lucide-react";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
|
import { type LanguageSwitcherProps } from "@/types";
|
||||||
|
|
||||||
const availableLanguages = Object.entries(i18nLanguages).map(([code, name]) => ({
|
const availableLanguages = Object.entries(i18nLanguages).map(([code, name]) => ({
|
||||||
code: code as Lang,
|
code: code as Lang,
|
||||||
@@ -11,10 +12,6 @@ const availableLanguages = Object.entries(i18nLanguages).map(([code, name]) => (
|
|||||||
icon: code === 'en' ? '🇬🇧' : code === 'zh' ? '🇨🇳' : '🌐'
|
icon: code === 'en' ? '🇬🇧' : code === 'zh' ? '🇨🇳' : '🌐'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface LanguageSwitcherProps {
|
|
||||||
lang?: Lang;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function LanguageSwitcher({ lang: propLang }: LanguageSwitcherProps) {
|
export default function LanguageSwitcher({ lang: propLang }: LanguageSwitcherProps) {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
// 获取当前语言,优先使用props传入的语言
|
// 获取当前语言,优先使用props传入的语言
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import type { Lang } from '../i18n/utils';
|
import { type ShareButtonsProps } from '@/types';
|
||||||
|
|
||||||
interface ShareButtonsProps {
|
|
||||||
lang: Lang;
|
|
||||||
title: string;
|
|
||||||
url?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ShareButtons({ lang, title, url }: ShareButtonsProps) {
|
export default function ShareButtons({ lang, title, url }: ShareButtonsProps) {
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
import { motion, useAnimation } from "framer-motion";
|
import { motion, useAnimation } from "framer-motion";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useTranslations } from "@/i18n/utils";
|
import { useTranslations } from "@/i18n/utils";
|
||||||
|
import { type SkillItem } from "@/types";
|
||||||
/**
|
|
||||||
* Skill item interface for the marquee component
|
|
||||||
*/
|
|
||||||
interface SkillItem {
|
|
||||||
name: string;
|
|
||||||
icon: string; // skillicons icon name
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All skills data with corresponding skillicons names
|
* All skills data with corresponding skillicons names
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
---
|
---
|
||||||
|
import { type BlogPost as BaseBlogPost } from '@/types';
|
||||||
|
|
||||||
const currentPath = Astro.url.pathname;
|
const currentPath = Astro.url.pathname;
|
||||||
const isZh = currentPath.includes('/zh/');
|
const isZh = currentPath.includes('/zh/');
|
||||||
const lang = isZh ? 'zh' : 'en';
|
const lang = isZh ? 'zh' : 'en';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import type { Lang } from '../../i18n/utils';
|
import { type Lang } from '@/types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
lang: Lang;
|
lang: Lang;
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
// src/i18n/utils.ts
|
// src/i18n/utils.ts
|
||||||
import { ui, defaultLang, languages } from './ui';
|
import { ui, defaultLang, languages } from './ui';
|
||||||
|
import { type Lang, type UiKeys } from '@/types';
|
||||||
|
|
||||||
export type Lang = keyof typeof languages;
|
// 重新导出类型,以保持向后兼容性
|
||||||
|
export type { Lang, UiKeys };
|
||||||
// 简化类型定义,直接使用字符串类型
|
|
||||||
// 这样可以兼容点符号键,同时避免复杂的类型推导问题
|
|
||||||
export type UiKeys = string;
|
|
||||||
|
|
||||||
|
|
||||||
export function useTranslations(lang: Lang | undefined) {
|
export function useTranslations(lang: Lang | undefined) {
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
---
|
---
|
||||||
import type { MarkdownLayoutProps } from 'astro';
|
import type { MarkdownLayoutProps } from 'astro';
|
||||||
import { type Lang } from '@/i18n/utils';
|
import { type Lang, type FrontmatterProps } from '@/types';
|
||||||
import { defaultLang } from '@/i18n/ui';
|
import { defaultLang } from '@/i18n/ui';
|
||||||
import GlassHeader from '@/components/GlassHeader';
|
import GlassHeader from '@/components/GlassHeader';
|
||||||
import Footer from '@/components/Footer';
|
import Footer from '@/components/Footer';
|
||||||
import "../styles/global.css";
|
import "../styles/global.css";
|
||||||
|
|
||||||
// Define the frontmatter structure
|
|
||||||
interface FrontmatterProps {
|
|
||||||
title: string;
|
|
||||||
description?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use Astro's MarkdownLayoutProps for proper type safety
|
// Use Astro's MarkdownLayoutProps for proper type safety
|
||||||
export type Props = MarkdownLayoutProps<FrontmatterProps>;
|
export type Props = MarkdownLayoutProps<FrontmatterProps>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
import type { MarkdownLayoutProps } from 'astro';
|
import type { MarkdownLayoutProps } from 'astro';
|
||||||
import { type Lang } from '@/i18n/utils';
|
import { type Lang } from '@/i18n/utils';
|
||||||
|
import { type FrontmatterProps } from '@/types';
|
||||||
import { defaultLang } from '@/i18n/ui';
|
import { defaultLang } from '@/i18n/ui';
|
||||||
import GlassHeader from '@/components/GlassHeader';
|
import GlassHeader from '@/components/GlassHeader';
|
||||||
import Footer from '@/components/Footer';
|
import Footer from '@/components/Footer';
|
||||||
@@ -11,19 +12,6 @@ import PostMeta from '@/components/blog/PostMeta.astro';
|
|||||||
|
|
||||||
import "../styles/global.css";
|
import "../styles/global.css";
|
||||||
|
|
||||||
// Define the frontmatter structure
|
|
||||||
interface FrontmatterProps {
|
|
||||||
title: string;
|
|
||||||
description?: string;
|
|
||||||
publishDate?: string;
|
|
||||||
date?: string; // Alternative field name for publish date
|
|
||||||
author?: string;
|
|
||||||
tags?: string[];
|
|
||||||
category?: string | string[];
|
|
||||||
readingTime?: number;
|
|
||||||
readTime?: string; // Alternative field name for reading time
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use Astro's MarkdownLayoutProps for proper type safety
|
// Use Astro's MarkdownLayoutProps for proper type safety
|
||||||
export type Props = MarkdownLayoutProps<FrontmatterProps>;
|
export type Props = MarkdownLayoutProps<FrontmatterProps>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
export const personalInfo = {
|
import type { PersonalInfo } from '@/types';
|
||||||
|
|
||||||
|
export const personalInfo: PersonalInfo = {
|
||||||
name: "Joy Zhao",
|
name: "Joy Zhao",
|
||||||
location: "China",
|
location: "China",
|
||||||
avatar: "https://avatars.githubusercontent.com/u/24975063?v=4",
|
avatar: "https://avatars.githubusercontent.com/u/24975063?v=4",
|
||||||
|
|||||||
158
src/types/index.ts
Normal file
158
src/types/index.ts
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
/**
|
||||||
|
* 集中管理项目中的类型定义
|
||||||
|
* 这个文件包含了项目中常用的接口和类型定义,减少代码冗余和提高可维护性
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { languages } from '@/i18n/ui';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语言类型定义
|
||||||
|
*/
|
||||||
|
export type Lang = keyof typeof languages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际化文本键类型
|
||||||
|
*/
|
||||||
|
export type UiKeys = string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多语言文本类型
|
||||||
|
*/
|
||||||
|
export type LocalizedText = {
|
||||||
|
[K in Lang]: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 作者信息接口
|
||||||
|
*/
|
||||||
|
export interface Author {
|
||||||
|
name: string;
|
||||||
|
bio?: string;
|
||||||
|
avatar?: string;
|
||||||
|
website?: string;
|
||||||
|
twitter?: string;
|
||||||
|
github?: string;
|
||||||
|
linkedin?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 博客文章接口
|
||||||
|
*/
|
||||||
|
export interface BlogPost {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
image: string;
|
||||||
|
slug: string;
|
||||||
|
tags: string[];
|
||||||
|
date: string;
|
||||||
|
readTime: string;
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 博客文章前置元数据接口
|
||||||
|
*/
|
||||||
|
export interface FrontmatterProps {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
publishDate?: string;
|
||||||
|
date?: string; // Alternative field name for publish date
|
||||||
|
author?: string;
|
||||||
|
tags?: string[];
|
||||||
|
category?: string | string[];
|
||||||
|
readingTime?: number;
|
||||||
|
readTime?: string; // Alternative field name for reading time
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 技能项接口
|
||||||
|
*/
|
||||||
|
export interface SkillItem {
|
||||||
|
name: string;
|
||||||
|
icon: string; // skillicons icon name
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 个人信息接口
|
||||||
|
*/
|
||||||
|
export interface PersonalInfo {
|
||||||
|
name: string;
|
||||||
|
location: string;
|
||||||
|
avatar: string;
|
||||||
|
email: string;
|
||||||
|
github: string;
|
||||||
|
linkedin: string;
|
||||||
|
website: string;
|
||||||
|
twitter: string;
|
||||||
|
position: LocalizedText;
|
||||||
|
description: LocalizedText;
|
||||||
|
about: {
|
||||||
|
[K in Lang]: string[];
|
||||||
|
};
|
||||||
|
stats: {
|
||||||
|
repositories: number;
|
||||||
|
commits: string;
|
||||||
|
contributions: number;
|
||||||
|
};
|
||||||
|
skills: {
|
||||||
|
frontend: string[];
|
||||||
|
backend: string[];
|
||||||
|
database: string[];
|
||||||
|
devops: string[];
|
||||||
|
mobile: string[];
|
||||||
|
};
|
||||||
|
terminal: {
|
||||||
|
username: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件 Props 接口
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 作者卡片组件 Props
|
||||||
|
*/
|
||||||
|
export interface AuthorCardProps {
|
||||||
|
lang: Lang;
|
||||||
|
author?: Author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享按钮组件 Props
|
||||||
|
*/
|
||||||
|
export interface ShareButtonsProps {
|
||||||
|
lang: Lang;
|
||||||
|
title: string;
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页脚组件 Props
|
||||||
|
*/
|
||||||
|
export interface FooterProps {
|
||||||
|
lang?: Lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玻璃标题组件 Props
|
||||||
|
*/
|
||||||
|
export interface GlassHeaderProps {
|
||||||
|
lang: Lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 博客列表组件 Props
|
||||||
|
*/
|
||||||
|
export interface BlogListProps {
|
||||||
|
posts: BlogPost[];
|
||||||
|
lang: Lang;
|
||||||
|
baseUrl?: string; // Base URL for blog posts, defaults to '/blog/posts/'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语言切换器组件 Props
|
||||||
|
*/
|
||||||
|
export interface LanguageSwitcherProps {
|
||||||
|
lang: Lang;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
"include": [
|
"include": [
|
||||||
".astro/types.d.ts",
|
".astro/types.d.ts",
|
||||||
"src/**/*.ts",
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
"src/**/*.astro",
|
"src/**/*.astro",
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
|
|||||||
Reference in New Issue
Block a user