首页 / Astro 教程 / 组件 Props 与 TypeScript 类型

Astro 教程

组件 Props 与 TypeScript 类型

本教程共 56 篇 · 第 8 篇 · 更新于 2026-08-07 · 约 10 分钟阅读

AstroAstro 教程组件 PropsAstro.propsTypeScriptProps 接口默认值

本节目标:学会让 .astro 组件接收外部传进来的参数(Props),用 Astro.props 取值,并用 TypeScript 给参数加类型检查与默认值。

组件要“可复用”,关键得能接收不同参数。比如一个“标题组件”,这次传“你好”,下次传“欢迎”,它就该显示不同文字。Astro 里,这种从外面传进来的参数叫 Props(property 的简写,就是“组件的属性”)。

Props 从哪来

当你在别处这样用一个组件:

<GreetingHeadline greeting="Howdy" name="Partner" />

那两个 greeting="..."name="..." 就是传给组件的 Props。在组件内部,它们都能从全局对象 Astro.props 里拿到。

看一个接收两个 Props 的组件:

---
// 用法:<GreetingHeadline greeting="Howdy" name="Partner" />
const { greeting, name } = Astro.props;
---

<h2>{greeting}, {name}!</h2>

写法是:在 frontmatter 里从 Astro.props 把要用的字段“解构”出来,然后模板直接 {greeting}{name} 引用。

在别处传入 Props

组件自己定义好接收哪些 Props 后,在另一个组件、布局或页面里引入它,就能像写 HTML 属性一样传值:

---
import GreetingHeadline from './GreetingHeadline.astro';
const name = 'Astro';
---

<h1>Greeting Card</h1>
<GreetingHeadline greeting="Hi" name={name} />
<p>I hope you have a wonderful day!</p>

这里有个细节:greeting="Hi" 是写死的字符串;name={name}{} 包了一个脚本里的变量。两种都合法——字符串直接写,变量用 {} 传。

用 TypeScript 给 Props 加类型

光接收还不够稳。万一调用方把 name 漏了、或者传了个数字进来,页面可能出怪问题。Astro 支持用 TypeScript(给 JavaScript 加“类型”的语言,能在写代码时发现类型错误)来约束 Props。

做法是:在 frontmatter 里定义一个叫 Props 的接口(interface),列出每个参数叫什么、是什么类型。Astro 会自动识别这个 Props 接口,给你类型提示和报错。

---
interface Props {
  name: string;       // name 必填,必须是字符串
  greeting?: string;  // greeting 可选(带问号),字符串
}

const { greeting = "Hello", name } = Astro.props;
---

<h2>{greeting}, {name}!</h2>

这段里的 interface Props 就是类型契约:

  • name: string 表示 name 必传,且得是字符串;
  • greeting?: string 的问号表示“可选”,不传也不会报错。

如果你传 <GreetingHeadline name={123} />,由于 name 被声明成 string,TypeScript 会提示类型不对。这能在写代码阶段就拦住很多低级错误,对初学者非常友好。

Note

你不用专门学透 TypeScript 也能用这招。记住一条:写组件时顺手用 interface Props { ... } 把参数“登记”一下,Astro 就会替你看着类型。这是 Astro 项目里约定俗成的写法。

给 Props 设默认值

很多时候,你希望某个参数“不传就用个默认”。解构时直接赋值即可:

---
const { greeting = "Hello", name } = Astro.props;
---

<h2>{greeting}, {name}!</h2>

这里 greeting 没传时,就自动是 "Hello"。结合上一节的接口,还能给可选参数同时设默认:

---
interface Props {
  name: string;
  greeting?: string;
}

const { greeting = "Hello", name } = Astro.props;
---

<h2>{greeting}, {name}!</h2>

如果调用方没给 greeting,页面显示的就是 Hello, xxx!

不写 TypeScript 时也能设默认,接口只是可选的“加强版”:

---
const { greeting = "Hello", name = "Astronaut" } = Astro.props;
---

<h2>{greeting}, {name}!</h2>

这回 name 也带默认值 "Astronaut",谁都不传也能正常渲染。

不写 TypeScript 也完全能用

前面那套 interface Props 是“加强版”。即使你完全不写类型,Props 照样工作——只是少了编译期的类型检查:

---
const { greeting = "Hello", name } = Astro.props;
---

<h2>{greeting}, {name}!</h2>

新手阶段可以先不碰 TypeScript,等熟悉了再回头加接口,Astro 完全兼容两种写法。

传递对象或数组作为 Props

Props 不限于字符串、数字,也能传整个对象或数组。类型里把结构写清楚即可:

---
interface Props {
  user: { name: string; age: number };
}

const { user } = Astro.props;
---

<p>{user.name} 今年 {user.age} 岁</p>

调用时把对象直接写进 {}

<ProfileCard user={{ name: "小明", age: 18 }} />

数组同理,类型写成 string[]{ ... }[] 都行。这样组件就能接收一整份结构化数据。

解构时给 Props 改名

偶尔外部传来的参数名,和你脚本里想用的变量名冲突,解构时可以顺手改名:

---
const { name: userName } = Astro.props;
---

<h2>你好,{userName}</h2>

这里外部仍传 name="...",组件内部叫它 userName,互不打架。

用 …rest 转发剩余属性

有时你想把“除了已知参数外、剩下所有属性”一股脑传给某个子元素。解构时用 ...rest 收集剩余项即可:

---
const { title, ...rest } = Astro.props;
---

<div {...rest}>{title}</div>

这里的 rest 是一个对象,装着除 title 外所有传进来的属性,{...rest} 再把它们铺回 <div> 上。第 11 章给子组件传 class 时用的就是这个技巧。

Props 是只读的

要记住:Astro.props 是只读的,不要试图直接改它里面的字段。如果想基于某个参数算出新值,先拷到自己的变量里再改:

---
const { name } = Astro.props;
const displayName = name.toUpperCase(); // 复制后再加工
---

<h2>{displayName}</h2>

直接写 Astro.props.name = ... 不会按你预期工作,还容易踩坑。养成“先复制、再加工”的习惯,代码更稳。

把所有 Props 整体往下传

偶尔你需要把当前组件收到的全部 Props,原样转发给另一个子组件。用展开语法一行搞定:

<ChildComponent {...Astro.props} />

这在做“包装组件”时很常见:外层只加一点样式或逻辑,其余参数原封不动交给内层。

必填还是可选?怎么设计

设计组件接口时,有个实用原则:高频必填的字段设成必填(不带问号),可有可无的设成可选并给默认值。比如一个“用户卡片”,name 几乎每次都要,就必填;avatar 头像没有时显示默认图,就可选加默认。这样调用方用起来既灵活又不会漏掉关键信息。接口写清楚后,代码编辑器还会给你自动补全和悬停提示,写调用代码更省心,这也是推荐用 TypeScript 描述 Props 的隐形好处。

一个含多参数的完整示例

把前面几点合起来,一个“带类型、带默认、可复用”的标题组件就长这样:

---
// GreetingHeadline.astro
interface Props {
  name: string;
  greeting?: string;
}

const { greeting = "Hello", name } = Astro.props;
---

<h2>{greeting}, {name}!</h2>

调用方可以自由组合:

<GreetingHeadline name="码上学" />
<GreetingHeadline greeting="Hi" name="小明" />

第一行用默认问候语,第二行自定义。组件本身一次写好,到处复用。

Tip

把“会变化的数据”都做成 Props,是写好 Astro 组件的习惯。这样组件像积木一样,换个参数就换副面孔,不用复制粘贴改代码。

Astro 怎么认出 Props 接口

你可能会好奇:我写的是 interface Props,Astro 怎么知道「这个接口就是给 Props 用的」?

规则很简单:Astro 会在组件脚本里自动找一个名字就叫 Props 的接口(或类型别名),把它当成这个组件的参数契约。所以请固定用 Props 这个名,别写成 MyPropsHeadlineProps 之类——换名字 Astro 就不认了,类型检查会失效。

---
// ✅ 正确:名字是 Props,Astro 认
interface Props {
  title: string;
}
const { title } = Astro.props;
---

如果你确实想用别的名字,可以显式标注组件的 Props 类型,但新手阶段没必要折腾,老老实实写 Props 最省事。

用 astro check 把错误拦在写代码时

类型写好了,怎么确认没写错?除了编辑器里飘红提示,你还能在终端跑一条命令统一检查:

npm run check

它背后是 astro check,会扫描项目里所有 .astro.ts 文件,把类型错误、明显的写法问题一次性列出来。比如你把 name 传成了数字、漏了必填参数,它都会报。

建议养成习惯:写完一组组件、准备构建前,先跑一次 astro check。比起等构建报错或上线后出怪问题,提前查一遍省心得多。

小结

Props 是组件之间“传数据”的通道:在外面像写属性一样传,在里面从 Astro.props 解构取。用 interface Props 加 TypeScript 类型,能让传参出错在写代码时就被发现;解构时赋值则能给参数设默认值。下一章讲另一个组件间通信的机制——插槽(slot),它负责把“一整块 HTML 内容”塞进组件的指定位置。