在 TypeScript 中处理 DOM 操作时,有一整套完善的类型系统。下面我将系统地整理前端开发中最常用的 DOM 元素类型。
🎯 基础 DOM 类型体系
1. 顶层类型
// 所有 DOM 节点的基类
let node: Node = document.createElement('div')
// 所有 HTML 元素的基类
let element: Element = document.querySelector('div')!
// 所有具体的 HTML 元素都继承自 HTMLElement
let htmlElement: HTMLElement = document.createElement('div')
// 文档对象
let doc: Document = document
// 窗口对象
let win: Window = window
📦 常用具体元素类型
1. 输入控件类
// 输入框
let input: HTMLInputElement = document.createElement('input')
input.value = 'hello'
input.checked = true
input.type = 'password'
input.placeholder = '请输入'
input.files // FileList | null
// 文本域
let textarea: HTMLTextAreaElement = document.createElement('textarea')
textarea.value = '多行文本'
textarea.rows = 5
textarea.cols = 30
// 按钮
let button: HTMLButtonElement = document.createElement('button')
button.disabled = true
button.type = 'submit'
// 选择框
let select: HTMLSelectElement = document.createElement('select')
let option: HTMLOptionElement = document.createElement('option')
select.value = 'option1'
select.selectedIndex = 0
select.options // HTMLOptionsCollection
2. 容器和布局类
// 通用块级容器
let div: HTMLDivElement = document.createElement('div')
let span: HTMLSpanElement = document.createElement('span')
let section: HTMLElement = document.createElement('section') // 直接用 HTMLElement
let article: HTMLElement = document.createElement('article')
// 列表
let ul: HTMLUListElement = document.createElement('ul')
let ol: HTMLOListElement = document.createElement('ol')
let li: HTMLLIElement = document.createElement('li')
// 表格相关
let table: HTMLTableElement = document.createElement('table')
let tr: HTMLTableRowElement = document.createElement('tr')
let td: HTMLTableCellElement = document.createElement('td')
let th: HTMLTableCellElement = document.createElement('th')
let tbody: HTMLTableSectionElement = document.createElement('tbody')
let thead: HTMLTableSectionElement = document.createElement('thead')
// 表单
let form: HTMLFormElement = document.createElement('form')
form.action = '/submit'
form.method = 'POST'
form.elements // HTMLFormControlsCollection
3. 媒体类
// 图片
let img: HTMLImageElement = document.createElement('img')
img.src = '/image.jpg'
img.alt = '描述'
img.width = 100
img.height = 100
img.complete // 图片是否加载完成
// 音频
let audio: HTMLAudioElement = document.createElement('audio')
audio.src = '/audio.mp3'
audio.volume = 0.5
audio.play()
audio.pause()
// 视频
let video: HTMLVideoElement = document.createElement('video')
video.src = '/video.mp4'
video.poster = '/poster.jpg'
video.width = 640
video.height = 360
video.playbackRate = 1.5
// 画布
let canvas: HTMLCanvasElement = document.createElement('canvas')
let ctx: CanvasRenderingContext2D | null = canvas.getContext('2d')
4. 链接和元数据类
// 链接
let a: HTMLAnchorElement = document.createElement('a')
a.href = 'https://example.com'
a.target = '_blank'
a.download = 'file.pdf'
// 图片链接
let area: HTMLAreaElement = document.createElement('area')
area.shape = 'rect'
area.coords = '0,0,100,100'
// Meta 信息
let meta: HTMLMetaElement = document.createElement('meta')
meta.name = 'description'
meta.content = '页面描述'
// 链接资源
let link: HTMLLinkElement = document.createElement('link')
link.rel = 'stylesheet'
link.href = '/style.css'
🎨 特定功能的元素类型
1. 进度和度量
// 进度条
let progress: HTMLProgressElement = document.createElement('progress')
progress.value = 50
progress.max = 100
// 度量
let meter: HTMLMeterElement = document.createElement('meter')
meter.value = 0.6
meter.min = 0
meter.max = 1
meter.low = 0.3
meter.high = 0.8
meter.optimum = 0.5
2. 嵌入内容
// iframe
let iframe: HTMLIFrameElement = document.createElement('iframe')
iframe.src = '/other-page.html'
iframe.contentWindow // Window | null
iframe.contentDocument // Document | null
// 嵌入对象
let object: HTMLObjectElement = document.createElement('object')
object.data = '/file.pdf'
object.type = 'application/pdf'
// 嵌入脚本
let script: HTMLScriptElement = document.createElement('script')
script.src = '/main.js'
script.async = true
script.defer = true
3. 表单特有元素
// 单选/复选
let radio: HTMLInputElement = document.createElement('input')
radio.type = 'radio'
radio.name = 'gender'
radio.value = 'male'
radio.checked = true
let checkbox: HTMLInputElement = document.createElement('input')
checkbox.type = 'checkbox'
checkbox.checked = true
checkbox.indeterminate = false
// 文件上传
let fileInput: HTMLInputElement = document.createElement('input')
fileInput.type = 'file'
fileInput.multiple = true
fileInput.accept = 'image/*'
// 隐藏输入
let hidden: HTMLInputElement = document.createElement('input')
hidden.type = 'hidden'
hidden.value = 'secret-data'
// 颜色选择器
let color: HTMLInputElement = document.createElement('input')
color.type = 'color'
color.value = '#ff0000'
// 范围滑块
let range: HTMLInputElement = document.createElement('input')
range.type = 'range'
range.min = 0
range.max = 100
range.step = 5
range.value = '50'
🔍 类型查询和断言
1. 获取 DOM 元素
// 类型断言
const canvas = document.getElementById('canvas') as HTMLCanvasElement
const input = <HTMLInputElement>document.querySelector('input[type="text"]')
// 安全的类型检查
function isInputElement(el: HTMLElement): el is HTMLInputElement {
return el.tagName === 'INPUT'
}
// 使用实例
const element = document.getElementById('myElement')
if (element instanceof HTMLInputElement) {
element.value // TypeScript 知道这是 input
} else if (element instanceof HTMLTextAreaElement) {
element.value // 这也是 input 类型
}
// 更好的方式:使用类型守卫
function getInput(id: string): HTMLInputElement | null {
const el = document.getElementById(id)
return el instanceof HTMLInputElement ? el : null
}
2. 集合类型
// NodeList
const nodes: NodeListOf<HTMLElement> = document.querySelectorAll('.item')
nodes.forEach(node => node.style.color = 'red')
// HTMLCollection
const forms: HTMLCollectionOf<HTMLFormElement> = document.forms
const images: HTMLCollectionOf<HTMLImageElement> = document.images
// 特定类型的集合
const allInputs = document.querySelectorAll<HTMLInputElement>('input')
// allInputs 的类型是 NodeListOf<HTMLInputElement>
// 类型化的集合
interface MyElements {
'username': HTMLInputElement
'submitBtn': HTMLButtonElement
'avatar': HTMLImageElement
}
function getElement<K extends keyof MyElements>(id: K): MyElements[K] | null {
return document.getElementById(id) as MyElements[K] | null
}
⚡ 事件对象类型
1. 常见事件类型
// 鼠标事件
function handleMouse(e: MouseEvent) {
e.clientX, e.clientY // 鼠标坐标
e.button // 按下的鼠标键
e.ctrlKey, e.shiftKey // 修饰键
}
// 键盘事件
function handleKey(e: KeyboardEvent) {
e.key // 按下的键值
e.code // 物理按键代码
e.altKey // 是否按下 Alt
e.repeat // 是否长按重复
}
// 焦点事件
function handleFocus(e: FocusEvent) {
e.relatedTarget // 上一个/下一个焦点元素
}
// 表单事件
function handleSubmit(e: Event) {
e.preventDefault()
const form = e.target as HTMLFormElement
}
// 拖拽事件
function handleDrag(e: DragEvent) {
e.dataTransfer?.setData('text/plain', 'data')
}
// 剪贴板事件
function handlePaste(e: ClipboardEvent) {
const text = e.clipboardData?.getData('text/plain')
}
2. Vue 3 中的事件类型
<script setup lang="ts">
// 原生 DOM 事件
const handleClick = (e: MouseEvent) => {
console.log(e.clientX)
}
// Input 事件
const handleInput = (e: Event) => {
const target = e.target as HTMLInputElement
console.log(target.value)
}
// Change 事件
const handleChange = (e: Event) => {
const target = e.target as HTMLSelectElement
console.log(target.value)
}
// 键盘事件
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Enter') {
console.log('回车键按下')
}
}
// 自定义组件事件
interface Emits {
(e: 'update', value: string): void
(e: 'close', reason: 'click' | 'esc'): void
}
const emit = defineEmits<Emits>()
</script>
<template>
<input @click="handleClick">
<input @input="handleInput">
<select @change="handleChange">
<option value="1">选项1</option>
</select>
<input @keydown="handleKeyDown">
</template>
🛠️ 实用工具类型
1. 内置的 DOM 工具类型
// 获取元素属性的类型
type InputValue = HTMLInputElement['value'] // string
type ButtonType = HTMLButtonElement['type'] // 'button' | 'submit' | 'reset'
type ElementTag = HTMLElement['tagName'] // string
// Partial 应用于 DOM 配置
interface CanvasConfig {
width: number
height: number
context: CanvasRenderingContext2D
}
const config: Partial<CanvasConfig> = { width: 800 }
// 只读 DOM 引用
const readonlyElement: Readonly<HTMLElement> = document.body
// readonlyElement.innerHTML = '' // ❌ 错误
2. 自定义 DOM 类型
// 自定义数据属性
interface DatasetMap {
userId: string
role: 'admin' | 'user'
theme: 'light' | 'dark'
}
function setDataset<T extends HTMLElement>(
el: T,
data: Partial<{ [K in keyof DatasetMap]: string }>
) {
Object.entries(data).forEach(([key, value]) => {
el.dataset[key] = value
})
}
const div = document.createElement('div')
setDataset(div, { userId: '123', role: 'admin' })
// div.dataset.userId 有类型提示
// 带状态的自定义元素
interface StatefulElement<T> extends HTMLElement {
state: T
setState: (newState: Partial<T>) => void
}
interface ButtonState {
loading: boolean
count: number
}
const btn = document.createElement('button') as StatefulElement<ButtonState>
btn.state = { loading: false, count: 0 }
btn.setState({ loading: true })
📝 实际开发模式
1. 工厂函数模式
// 类型安全的元素创建
function createElement<K extends keyof HTMLElementTagNameMap>(
tagName: K,
props?: Partial<HTMLElementTagNameMap[K]>
): HTMLElementTagNameMap[K] {
const el = document.createElement(tagName)
if (props) {
Object.assign(el, props)
}
return el
}
// 使用示例
const button = createElement('button', {
textContent: '点击',
disabled: false,
className: 'btn-primary'
})
const input = createElement('input', {
type: 'email',
placeholder: '输入邮箱',
value: ''
})
2. 类型守卫工具
// DOM 类型守卫集合
const domGuards = {
isInput: (el: Element | null): el is HTMLInputElement =>
el?.tagName === 'INPUT',
isButton: (el: Element | null): el is HTMLButtonElement =>
el?.tagName === 'BUTTON',
isSelect: (el: Element | null): el is HTMLSelectElement =>
el?.tagName === 'SELECT',
isTextArea: (el: Element | null): el is HTMLTextAreaElement =>
el?.tagName === 'TEXTAREA',
isForm: (el: Element | null): el is HTMLFormElement =>
el?.tagName === 'FORM'
}
// 使用
const element = document.getElementById('myInput')
if (domGuards.isInput(element)) {
element.value = '类型安全'
}
3. Vue 3 中的 DOM 模板引用
<script setup lang="ts">
import { ref, onMounted } from 'vue'
// 基本元素引用
const inputRef = ref<HTMLInputElement | null>(null)
const divRef = ref<HTMLDivElement | null>(null)
const canvasRef = ref<HTMLCanvasElement | null>(null)
// 多个元素引用
const itemRefs = ref<HTMLElement[]>([])
// 组件引用
import Modal from './Modal.vue'
const modalRef = ref<InstanceType<typeof Modal> | null>(null)
onMounted(() => {
// 类型安全的方法调用
inputRef.value?.focus()
canvasRef.value?.getContext('2d')
modalRef.value?.open()
})
</script>
<template>
<input ref="inputRef" type="text">
<div ref="divRef" class="container"></div>
<canvas ref="canvasRef"></canvas>
<!-- 多个元素 -->
<div
v-for="item in 5"
:ref="el => itemRefs.push(el as HTMLElement)"
:key="item"
>
Item {{ item }}
</div>
<Modal ref="modalRef" />
</template>
📊 类型层次结构
EventTarget (所有事件目标的基类)
├── Node (所有 DOM 节点的基类)
│ ├── Element (所有元素的基类)
│ │ ├── HTMLElement (HTML 元素的基类)
│ │ │ ├── HTMLInputElement
│ │ │ ├── HTMLButtonElement
│ │ │ ├── HTMLDivElement
│ │ │ └── ...
│ │ └── SVGElement (SVG 元素)
│ ├── Text (文本节点)
│ └── Comment (注释节点)
├── Window (窗口对象)
└── Document (文档对象)
掌握这些 DOM 类型,可以让你在处理浏览器 API 时获得完整的类型支持和智能提示,减少运行时错误。