跳到主要内容

go:embed 指令

· 阅读需 3 分钟

[GoLang官方提案]https://go.googlesource.com/proposal/+/master/design/draft-embed.md

//go:embed 支持嵌入静态文件。

  1. 对于单个文件,可以嵌入为字符串和 byte slice
  2. 多个文件,支持嵌入为新的文件系统 FS
  3. 只支持嵌入为string, byte sliceembed.FS三种类型,这三种类型的别名(alias)和命名类型(如type S string)都不可以

嵌入字符串

//go:embed hello.txt
var s string

嵌入为 byte slice

//go:embed hello.txt
var b []byte

嵌入为fs.FS

基本使用

//go:embed hello.txt
var f embed.FS

支持同一变量上嵌入多个文件

//go:embed hello1.txt
//go:embed hello2.txt
var f embed.FS

func main() {
data, _ := f.ReadFile("hello.txt")
fmt.Println(string(data))
data, _ = f.ReadFile("hello2.txt")
fmt.Println(string(data))
}

匹配模式

go:embed dirpath 次文件夹下的除了 ._ 开头的文件/文件夹都会被嵌入(递归)。

如果要嵌入._开头的文件/文件夹,需要使用 go:embed dirpath/*

注意:这个只能嵌入 dirpath 目录下的 ._开头的文件/文件夹。子文件夹下的 ._ 开头的文件/文件夹是不会嵌入。

使用

将前端静态文件打包进 go 程序

嵌入静态文件

//go:embed frontend/out/*
var buildFS embed.FS

输出日志,检查文件是否被正确嵌入

// 打印嵌入的文件系统内容,用于调试
fs.WalkDir(buildFS, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() {
log.Printf("嵌入文件系统中有: %s", path)
}
return nil
})

静态文件服务

// 创建一个子文件系统,将嵌入的文件系统 buildFS 中的 frontend/out 目录作为新文件系统的根目录
contentFS, err := fs.Sub(buildFS, "frontend/out")
if err != nil {
log.Fatalf("无法获取嵌入的前端文件: %v", err)
}

// 创建一个文件服务处理器
fileServer := http.FileServer(http.FS(contentFS))

// 为所有路径提供服务
r.PathPrefix("/").Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 打印请求路径,帮助调试
log.Printf("请求文件: %s", r.URL.Path)
fileServer.ServeHTTP(w, r)
}))

Linux Shell 常用命令

· 阅读需 9 分钟

du

1、查看指定文件夹的大小

-h 以K,M,G为单位,提高信息的可读性。
-s 仅显示总计。

du -sh dir_path

2. 查询当前文件夹总大小

du -sh .

3. 查看当前文件夹所有子项的大小

du -sh *

df

df (disk filesystem)用于显示文件系统的磁盘空间使用情况.

查看 block 使用情况

df -h

查看 inode 使用情况

df -i

alias

alias gg='gg'
alias cls='clear'
alias ls='ls -alFh --ignore=. --ignore=.. --color=auto --group-directories-first'
alias acme.sh=~/.acme.sh/acme.sh

ln

ln -s source_file target_file

nginx

ln -s /wangzhy/cloud-conf/nginx/etc/nginx/conf /etc/nginx/conf
ln -s /wangzhy/.config/nginx_ip_conf /etc/nginx/ip-conf

把程序添加到 /usr/bin 下面

ln -s /source_path/source_name /usr/bin/target_name 

find

find 语法

find [path] [expression]

path:是要查找的目录路径,可以是一个目录或文件名,也可以是多个路径,多个路径之间用空格分隔,如果未指定路径,则默认为当前目录。

expression:是可选参数,用于指定查找的条件,可以是文件名、文件类型、文件大小等等。

-name pattern:按文件名查找,支持使用通配符 * 和 ?。

pattern 用引号括起来,例如

# 查找所有的 pdf 文件
find / -name '*.pdf*'

-type type:按文件类型查找,可以是 f(普通文件)、d(目录)、l(符号链接)等。

查看大于 100M 的文件

find / -type f -size +100M

-size [+-]size[cwbkMG]:按文件大小查找,支持使用 + 或 - 表示大于或小于指定大小,单位可以是 c(字节)、w(字数)、b(块数)、k(KB)、M(MB)或 G(GB)。

-mtime days:按修改时间查找,支持使用 + 或 - 表示在指定天数前或后,days 是一个整数表示天数。

-user username:按文件所有者查找。

-group groupname:按文件所属组查找。

find 命令中用于时间的参数如下:

-amin n:查找在 n 分钟内被访问过的文件。

-atime n:查找在 n*24 小时内被访问过的文件。

-cmin n:查找在 n 分钟内状态发生变化的文件(例如权限)。

-ctime n:查找在 n*24 小时内状态发生变化的文件(例如权限)。

-mmin n:查找在 n 分钟内被修改过的文件。

-mtime n:查找在 n*24 小时内被修改过的文件。

在这些参数中,n 可以是一个正数、负数或零。正数表示在指定的时间内修改或访问过的文件,负数表示在指定的时间之前修改或访问过的文件,零表示在当前时间点上修改或访问过的文件。

+n : n 天之前修改的文件 -n : n 天内修改的文件 n : n 天前修改的文件(第 n 天前的当天)

find 使用案例

  1. 在脚本中使用

find /opt/oracle/oradata/ORCLCDB/oracle_exp_dir -mtime +1 -name "WIKI_USER*" - rm -f exec {} \;

  1. 在 shell 使用

find /opt/oracle/oradata/ORCLCDB/oracle_exp_dir -mtime +1 -name "WIKI_USER*" - rm -f exec {} +

  1. 查找多种后缀的文件
find ~ \( -name "*.zip" -o -name "*.log" -o -name "*.txt" -o -name "*.jar" -o -name "*.json" -o -name "*.xls" -o -name "*.xlsx" -o -name "*.doc" -o -name "*.docx" \)
  1. 查找大于 10M 的 zip 文件
find /path/to/search -type f -name "*.zip" -size +10M -exec du -h {} + > ~/Desktop/a.txt

watch

每隔 1s 查询一下本目录下的,以 sql 结尾的文件的大小,单位是 MB。

watch -n 1 -d ls -lh --block-size=M *.sql

vim

1、vim 显示隐藏行号

# 显示行号
set number
set nu
# 隐藏行号
set nonumber
set nonu

2、vim 删除以 XX 开头的行

# 按 esc 进入命令输入模式
# 删除以 XX 开头的行
:g/^XX/d

tee

tee 的主要功能是从标准输入读取数据,然后将数据写入标准输出(即输出到终端上面)和文件。

echo 'hello world' | tee -a xxx.txt

tee>> 的区别?

tee 可以一次写入多个文件,并且可以在终端显示写入的内容。 >> 是静默的写入。

tar 压缩/解压文件

# 压缩
tar -zcvf test.tar.gz test
# 解压
tar -zxvf file.tar.gz

五选一

-c 压缩 -x 解压 -t 查看内容 -r 追加文件 -u 更新原压缩包的文件

可选

-z gzip -j bz2 -Z compress -v 显示过程 -O 标准输出

必选

-f 使用档案名字,这个参数是最后一个参数,后面只能接档案名。

systemd

Ubuntu 16.04 不再支持 rc.local 了。开机自启只能通过 systemd 来实现。

# 查看systemd 版本
systemctl --version
# 查看服务是否启用
systemctl is-enabled SERVICE_NAME

scp

[root@VM-16-10-centos ~]# scp
usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]
[-l limit] [-o ssh_option] [-P port] [-S program]
[[user@]host1:]file1 ... [[user@]host2:]file2

scp 命令传输文件

# 复制远程机器上的 xxx.zip 到本地
scp cloud:/wangzhy/xxx.zip ~/backups/
# 复制远程机器上的文件夹到本地
# -r 表示递归复制整个目录
scp -r cloud:/wangzhy ~/backup/
  • -C:开启压缩传输

passwd

1、设置简单的密码

# 方法一
echo '123456' | passwd --stdin username
# 方法二
echo username:password | chpasswd

2、删除用户的登录密码

passwd -d root

3、清除 last、lastb 的信息

# 清除登录记录
echo > /var/log/wtmp # last
echo > /var/log/btmp # lastb

查看 linux 版本信息

# 查看架构信息
uname -a
# 查看发行版本信息
cat /etc/os-release
# 输出机器的体系结构
arch

ntp

同步操作系统的时间

# 安装 ntp
yum -y install ntp
# 同步时间,这个只是修改操作系统的时间,重启之后就会失效
ntpdate -u cn.pool.ntp.org
# 查看当前硬件时间
clock -r
# 把当前操作系统的时间写入硬件中
clock -w

curl

curl -L 参数的作用是让 curl 在遇到 HTTP 3xx 状态码(重定向)时,自动跟随新的位置(URL)

curl -L www.sina.com

使用 -k--insecure 参数让 curl 忽略 SSL 证书验证

curl -k https://wangzhy.com 

下载文件

-o 需要指定文件名

-O 使用 URL 中的文件名

brew

安装 xcode 才能升级 brew

xcode-select --install

brew 升级

brew update
brew upgrade

brew 查看安装的软件

brew list

brew 卸载软件

brew uninstall software_name

查看磁盘读写总量

brew install smartmontools

smartctl -a /dev/disk2

安装 svn

brew install subversion

svn help

grep

egrep 是 grep 的增强版本,支持更多的正则表达式

-v 告诉 egrep ,不现实匹配的行,即反向匹配。

egrep -v "^*#|^$" filebeat.yml

wget

-i 下载本地或外部文件中的 URL,如: wget -i file.txt

-O 将输出写入指定文件。 sh -c "$(wget https://xxx.sh -O -)", 末尾的 - 表示标准输出(stdout) 这句命令的作用是,通过 sh 执行 https://xxx.sh 脚本。

-b 后台下载。

-d 调试模式。

基于Docker和Nginx搭建HTTPS Git服务器

· 阅读需 3 分钟

使用到的工具:

  1. Docker Compose
  2. Nginx
  3. Git
  4. fcgiwrap

Docker Compose 配置

services:
nginx:
restart: always
container_name: nginx
user: root
image: nginx
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- 80:80
- 443:443
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/mine.types:/etc/nginx/mine.types
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/html:/etc/nginx/html
- ./nginx/screen:/etc/nginx/screen
- ./nginx/logs:/etc/nginx/logs
# 重要:
- /run/fcgiwrap.socket:/var/run/fcgiwrap.socket
# 这里不需要映射进去,因为 fcgiwrap 是运行在宿主机里面的
# - "/usr/lib/git-core/:/usr/libexec/git-core/:ro"
environment:
- NGINX_PORT=80
- TZ=Asia/Shanghai
privileged: true

Nginx 配置

# 443 端口
server {
listen 443 ssl;
server_name git.wangzhy.com;
ssl_certificate /etc/nginx/ssl/wangzhy.com_ecc/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/wangzhy.com_ecc/wangzhy.com.key;

ssl_session_cache shared:git_ssl_cache:10m;
ssl_session_timeout 10m;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
charset utf-8;
default_type text/plain;

include /etc/nginx/ip-conf/whitelist.conf;
deny all;

root /etc/nginx/html;

# 通过 https 请求 git
location ~ (/.*)$ {
# 使用 Basic 认证
# auth_basic "Restricted";
# auth_basic_user_file /etc/nginx/passwd;

# FastCGI 参数
include fastcgi_params;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
# 因为 fastcgi 是通过 docker 将宿主机的程序映射过来的,fastcgi 实际是运行在宿主机的,所以在这里要是有宿主机的地址。
fastcgi_param SCRIPT_FILENAME "/usr/lib/git-core/git-http-backend";
fastcgi_param GIT_HTTP_EXPORT_ALL "";
# git 库在服务器上的根目录
fastcgi_param GIT_PROJECT_ROOT /wangzhy/gitrepo;
fastcgi_param PATH_INFO $1;
# 将认证用户信息传递给 fastcgi 程序
# fastcgi_param REMOTE_USER $remote_user;
# 将允许客户端 post 的最大值调整为 100 兆
}

error_page 400 402 403 404 500 502 503 504 /50x.html;
location = /50x.html {
}
}

Git 配置

允许远程访问 git 仓库:

git config --system http.receivepack true
git config --system http.uploadpack true

fcgiwrap 配置

安装

apt update && apt install fcgiwrap

启动

systemctl start fcgiwrap
systemctl enable fcgiwrap

问题处理

权限问题

  1. error: remote unpack failed: unable to create temporary object directory

检查下面文件、文件夹的权限

  • /run/fcgiwrap.socket
  • /usr/lib/git-core/git-http-backend
  • ps aux | grep fcgiwrap
  • Docker Compose Nginx 的主线线的用户
  • xxx.git 文件夹的权限,一般要求是 chmod -R 755 xxx.git
  1. fatal: unable to access 'https://xxxx/.git/': The requested URL returned error: 403

检查 xxx.git/config 文件,查看是否配置了 http.receivepack true

修改运行 fcgiwrap 的用户

系统默认是 www-data,如果需要修改成其他用户,比如 nginx,可以使用下面命令:

systemctl edit --full fcgiwrap.service

Nginx 反向代理 WebSocket 服务

· 阅读需 1 分钟
# 反向代理
server {
server_name domain.com;
listen 443 ssl;

# 通过 acme.sh 进行签署,具体见:https://blog.wangzhy.com/ssl#acmesh-%E7%AD%BE%E7%BD%B2%E8%AF%81%E4%B9%A6
ssl_certificate /etc/nginx/ssl/xxxx/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/xxxx/domain.com.key;

# SSL 增强配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;


location / {
proxy_pass http://host.docker.internal:9001;
# 基础代理头设置
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# 连接数限制
proxy_http_version 1.1; # 明确指定HTTP1.1
proxy_buffers 8 16k; # 缓冲区优化
proxy_buffer_size 32k;

# WebSocket 专用设置
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

# 调整超时时间(秒)
proxy_read_timeout 86400; # 保持长连接
proxy_send_timeout 86400;
}
}

解决Nginx多SSL配置中未匹配域名的默认回退问题

· 阅读需 2 分钟

问题现象

我给 nginx 配置了多个server,分别设置了 ssl 代理,但是当其中一个服务未启动是,会访问到第一个 server 的内容。

server {
listen 443 ssl;
server_name a.domain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://xxxx:10010;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 443 ssl;
server_name b.domain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://xxxx:10086;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

当 10086 端口的服务未启动时,访问 https://b.domain.com 时,会出现 10010端口的服务的内容。 也就是 nginx 只会访问第一个 server 的内容。经过一番查找,发现是因为没有设置 default_server

解决方案

添加一个下面的 server 配置

server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
return 444; # 立即关闭连接
}

问题剖析

Nginx 请求匹配机制

  1. 端口监听优先级:Nginx优先匹配 listen指令相同的配置
  2. 域名精确匹配:在相同监听端口中,server_name最精确匹配的配置生效
  3. 默认回退机制:当没有匹配的server_name 时,自动选择:
    • 第一个定义的 server 块(未显式声明 default_server 时)
    • 标记为default_server的配置块

问题根源

当后端服务不可达时,Nginx 的请求处理流程:

  1. 客户端请求 b.domain.com
  2. Nginx正确匹配到 b.domain.com 的 server 块
  3. 尝试代理到http://xxxx:10086时发现连接失败
  4. 错误配置导致匹配降级,回退到默认 server 块(第一个定义的 a.domain.com

环境变量替换

· 阅读需 1 分钟

可以在 Dockerfile、docker-compose.yml 中使用环境变量 替换${}。它允许你在构建镜像时动态地设置变量的值。

${} 语法

基本形式: ${VAR}

environment:
- APP_PORT=${APP_PORT}

默认值: ${VAR:-default}

HOST_PORT 未设置时,采用默认值 8080

ports:
- "${HOST_PORT:-8080}:80"

强制: ${VAR:?error}

DATA_PATH 如果未设置,会报错并退出。

volumes:
- "${DATA_PATH:?DATA_PATH must be set}:/data"

组合: ${VAR:-${DEFAULT_VALUE}}

environment:
- DB_HOST=${DB_HOST:-${FALLBACK_DB_HOST}}

环境变量设置

1. Shell 环境变量

export APP_PORT=8080

2. .env 文件

docker-compose 会自动加载项目根目录下的 .env 文件。

可以通过 --env-file 指定 .env 文件。 例如:

docker compose --env-file custom.env up -d

3. 命令行通过 -e --env 参数指定

Dockerfile

docker -e APP_PORT=8080 run myapp

docker-Compose.yml

docker compose up -d  --env APP_PORT=8080

初始化结构体指针

· 阅读需 2 分钟
type User struct {
ID int
Name string
}

直接结构体指针(字面量写法) 🔔推荐

使用 & 符号直接创建结构体指针,并初始化字段. 推荐使用

优点:

  1. 字段与值显式对应,清晰直观
  2. 自动推导类型,减少冗余代码
  3. 字段顺序无关,结构体修改时更安全
u := &User{
ID: 1,
Name: "Alice",
}

先 new 后赋值

// 1. 分配内存,返回一个指向零值的指针
u := new(User)

// 2. 手动赋值字段
u.ID = 1
u.Name = "Alice"

按顺序初始化 不推荐

user := &User{2, "Alice"}

缺点:

  1. 必须严格匹配字段顺序,调整结构体字段顺序会导致初始化错误
  2. 可读性差,尤其当字段数量多或类型相似时容易出错

Go语言时间处理

· 阅读需 2 分钟

时间格式化基础

自定义格式输出

Go使用特定时间戳(2006-01-02 15:04:05)作为布局字符串:

t := time.Now()
fmt.Println(t.Format("2006-01-02 15:04:05")) // 2023-10-08 14:30:45
fmt.Println(t.Format("2006/01/02 15:04:05")) // 2023/10/08 14:30:45
fmt.Println(t.Format("2006.01.02 15:04:05")) // 2023.10.08 14:30:45
fmt.Println(t.Format("2006年01月02日 15时04分05秒")) // 2023年10月08日 14时30分45秒

预定义标准格式

Go内置多种标准格式,直接使用time包常量:

fmt.Println(time.Now().Format(time.RFC3339))  // 2023-10-08T14:30:45+08:00
fmt.Println(time.Now().Format(time.RFC1123)) // Mon, 08 Oct 2023 14:30:45 CST

字符串解析实践

yyyyMM格式解析

处理无分隔符的日期字符串时需注意布局字符串格式:

func parseYearMonth(str string) (int, time.Month) {
t, err := time.Parse("200601", str) // 布局字符串必须使用"200601"
if err != nil {
log.Fatal("解析失败:", err)
}
return t.Year(), t.Month()
}

// 使用示例
year, month := parseYearMonth("202310")
fmt.Printf("%d年%s月", year, month) // 2023年October月

Go语言JSON结构体标签

· 阅读需 3 分钟

结构体标签详解

type User struct {
ID int `json:"id"` // 字段重命名(服务端返回"id"字段)
Name string `json:"name"` // 保持字段原名
Email string `json:"email,omitempty"` // 空值时自动隐藏(避免空字符串污染JSON)
Secret string `json:"-"` // 彻底隐藏敏感字段(如密码)
Created time.Time `json:"created_at"` // 时间类型自动格式化为RFC3339字符串
}

序列化最佳实践

// 带缩进的友好格式输出
data, _ := json.MarshalIndent(user, "", " ")
/*
{
"id": 1,
"name": "Alice",
"created_at": "2023-09-15T10:00:00Z"
}
*/

// 原始紧凑格式(适合网络传输)
binaryData, _ := json.Marshal(user) // []byte类型数据

反序列化注意事项

var newUser User
err := json.Unmarshal(data, &newUser)

// 处理日期字段(需显式转换)
type CustomTime struct {
time.Time
}
func (ct *CustomTime) UnmarshalJSON(b []byte) error {
// 自定义日期解析逻辑...
}

常见问题排查

  1. 字段丢失:检查结构体是否导出(首字母大写)
  2. 时间格式错误:使用time.RFC3339格式字符串
  3. 零值问题:结合omitempty与指针类型*string
  4. 循环引用:避免结构体嵌套循环

高级技巧

// 动态字段处理
type FlexibleStruct struct {
Extra map[string]interface{} `json:"-"` // 收集未定义字段
}

// 条件序列化
func (u User) MarshalJSON() ([]byte, error) {
// 自定义序列化逻辑...
}

动态类型处理

// 解析未知结构 JSON
data := []byte(`{"name":"Bob","age":30}`)
var result map[string]interface{}
json.Unmarshal(data, &result)
fmt.Println(result["name"].(string))

// 部分解析(使用匿名结构)
var partial struct {
Name string `json:"name"`
}
json.Unmarshal(data, &partial)

自定义序列化

// 实现 json.Marshaler 接口
type CustomTime time.Time

func (ct CustomTime) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"%s"`, time.Time(ct).Format("2006/01/02"))), nil
}

// 实现 json.Unmarshaler 接口
func (ct *CustomTime) UnmarshalJSON(data []byte) error {
// 自定义解析逻辑...
}

处理 HTML 转义

buf := new(bytes.Buffer)
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false) // 禁用 &, <, > 转义
enc.Encode(data)

TypeScript interface 关键字介绍

· 阅读需 4 分钟

interface 用于定义对象类型函数签名类结构等。

基础语法

interface User {
name: string;
age: number;
isAdmin?: boolean; // 可选属性(Optional)
readonly id: number; // 只读属性
}
  • ? 表示非必须
  • readonly 表示只读

函数类型

  1. 定义了一个函数签名,参数是 source keyword,返回值是 boolean 类型。
  2. 声明了一个 search 常亮,并赋值了一个箭头函数。
interface SearchFunc {
(source: string, keyword: string): boolean;
}

// src, kw 名称不需要与函数类型定义中一致,但参数类型需要一致。
const search: SearchFunc = (src, kw) => src.includes(kw);

使用场景

约束回调函数

interface ClickHandler {
(event: MouseEvent, context: HTMLElement): void;
}

const handleClick: ClickHandler = (e, element) => {
console.log("Clicked on:", element.tagName);
};

统一 API 请求函数

interface ApiRequest {
(url: string, params: Record<string, string>): Promise<Response>;
}

const fetchData: ApiRequest = async (path, data) => {
return await fetch(path, { body: JSON.stringify(data) });
};

高阶函数参数

function higherOrderFunc(callback: SearchFunc) {
callback("Hello", "H"); // 调用时必须传入符合签名的函数
}

higherOrderFunc((s, k) => s.startsWith(k));

可索引类型

// 数组类型
interface StringArray {
[index: number]: string; // 索引为数字,值为字符串
}

// 字典类型
interface NumberDictionary {
[key: string]: number; // 键为字符串,值为数字
length: number; // 明确声明的属性必须符合索引签名
}

使用案例:

// 用索引获取数组元素
interface StudentArray {
[index: number]: string;
}

let studentArr: StudentArray = ["Bob", "Fred"];
let student1: string = studentArr[0]; // 'Bob'
let student2: string = studentArr[1]; // 'Fred'

// 用索引获取对象属性
interface StudentObject {
[key: string]: number;
}

let studentObj: StudentObject = {};
studentObj['Bob'] = 1;
studentObj['Fred'] = 2; // { Bob: 1, Fred: 2 }

继承与扩展

interface Animal {
name: string;
}

interface Dog extends Animal {
breed: string;

bark(): void;
}

// 多重继承
interface SuperDog extends Dog {
fly(): void;
}

类实现接口

interface ClockInterface {
currentTime: Date;

setTime(d: Date): void;
}

class Clock implements ClockInterface {
currentTime: Date = new Date();

setTime(d: Date) {
this.currentTime = d;
}
}

混合类型

interface Counter {
(start: number): string; // 函数签名
interval: number; // 属性
reset(): void; // 方法
}

// 实现示例
function createCounter(): Counter {
let counter = function (start: number) {
} as Counter;
counter.interval = 5;
counter.reset = () => {
};
return counter;
}

接口合并

interface Box {
width: number;
}

interface Box {
height: number;
}

// 合并后等价于:
interface Box {
width: number;
height: number;
}

动态扩展接口

interface Window {
myCustomProp: string; // 扩展全局 Window 类型
}

泛型接口

interface ApiResponse<T> {
code: number;
data: T;
message?: string;
}

// 使用
const userResponse: ApiResponse<User> = {
code: 200,
data: {name: "Alice", age: 30}
};