TypeScript 已经从前端工程师的”加分项”变成”基本盘”。面试时,能够讲清楚类型系统的底层逻辑、而不是只会背语法,才是真正的加分点。本文带你把 TypeScript 吃透,并在 Vue3 中落地。
一、TypeScript 到底是什么
TypeScript 是 JavaScript 的超集,它做的事情其实只有一件:在编译期给 JS 加上静态类型检查,最终编译成纯粹的 JavaScript 运行。
关键点:
- 类型是编译期的概念:运行时不存在的
interface、type、类型注解都会被擦除。 - 类型系统是图灵完备的:TS 的类型操作(条件类型、映射类型、递归)本身就是一门”类型编程语言”,这也是它能做”类型体操”的原因。
二、基础类型与推断
// 基础类型
let count: number = 0
let name: string = 'shanky'
let isDone: boolean = false
// 数组的两种写法
let arr1: number[] = [1, 2, 3]
let arr2: Array<string> = ['a', 'b']
// 元组 Tuple —— 固定长度和类型
let tuple: [string, number] = ['shanky', 28]
// 枚举
enum Status { Pending, Success, Error }
// 联合类型 Union —— "或"
type Id = string | number
// 字面量类型 —— 精确到具体值
type Method = 'GET' | 'POST' | 'PUT' | 'DELETE'
// any / unknown / never / void
let a: any // 逃逸类型检查(慎用)
let u: unknown // 安全的"未知",使用前需要收窄
function neverReturn(): never { throw new Error() } // 永不返回
function noReturn(): void {} // 无返回值
anyvsunknown:any直接放弃类型检查,会把类型”污染”到所有派生值;unknown则强制你先收窄类型再使用,是更安全的选择。团队规范里应当禁用any(ESLintno-explicit-any)。
三、对象类型与接口
// interface —— 描述对象结构,支持声明合并、继承
interface User {
id: number
name: string
age?: number // 可选
readonly createdAt: Date // 只读
}
// type —— 更灵活,支持联合、交叉、映射等所有类型操作
type User = { id: number; name: string }
// 交叉类型 Intersection —— "且"
type A = { a: number }
type B = { b: string }
type C = A & B // 同时拥有 a 和 b
// 类型守卫 —— 运行时收窄
function isString(v: unknown): v is string {
return typeof v === 'string'
}
四、泛型:类型的参数化
泛型是 TS 最核心的能力,它让函数/类/接口可以配合任意类型,同时保持类型安全:
// 泛型函数:identity 接收 T 并原样返回 T
function identity<T>(arg: T): T {
return arg
}
// 泛型约束:保证 T 一定有 length 属性
function logLength<T extends { length: number }>(arg: T): T {
console.log(arg.length)
return arg
}
// 泛型工具类型
type PartialUser = Partial<User> // 所有属性可选
type RequiredUser = Required<User> // 所有属性必填
type ReadonlyUser = Readonly<User> // 所有属性只读
type Picked = Pick<User, 'id' | 'name'> // 选取部分属性
type Omitted = Omit<User, 'age'> // 排除部分属性
type Params = Parameters<typeof fn> // 取函数参数类型
type Return = ReturnType<typeof fn> // 取函数返回值类型
五、条件类型与映射类型(类型编程)
这是面试中的”硬核”考点:
// 条件类型 —— 类三目运算
type IsArray<T> = T extends any[] ? true : false
type R1 = IsArray<number[]> // true
type R2 = IsArray<number> // false
// infer —— 在条件类型中"提取"类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never
type RT = GetReturnType<() => string> // string
// 映射类型 —— 遍历联合类型批量生成
type Nullable<T> = { [K in keyof T]: T[K] | null }
一个经典面试题:手写实现
Partial<T>。type MyPartial<T> = { [K in keyof T]?: T[K] }思路就是”映射类型 + 可选修饰符”,理解后会发现所谓的类型体操并不神秘。
六、在 Vue3 中实战
Vue3 + <script setup> 天然支持 TS,配合泛型让组件更健壮:
<script setup lang="ts">
// defineProps 泛型 —— 类型化 props
interface Props {
title: string
count?: number
onConfirm?: () => void
}
const props = withDefaults(defineProps<Props>(), {
count: 0,
})
// defineEmits 类型化
const emit = defineEmits<{
(e: 'update:count', value: number): void
(e: 'submit', payload: { id: number }): void
}>()
// ref / reactive 泛型
import { ref, reactive } from 'vue'
const list = ref<User[]>([])
const form = reactive<{ name: string; age?: number }>({ name: '' })
// computed 显式类型
const total = computed<number>(() => list.value.reduce((s, u) => s + u.age!, 0))
</script>
在组合式函数(composable)里,泛型尤其好用:
// useRequest 泛型 —— 请求返回什么类型由调用方决定
export function useRequest<T>(fn: () => Promise<T>) {
const data = ref<T | null>(null)
const loading = ref(false)
async function run() {
loading.value = true
try { data.value = await fn() }
finally { loading.value = false }
}
return { data, loading, run }
}
七、总结
面试时,与其说”我会 TS”,不如展示三个层次:
- 会写:基础类型、接口、泛型信手拈来;
- 会解释:能讲清
any/unknown区别、类型擦除、联合/交叉/泛型/条件类型; - 会落地:在 Vue3 组合式 API、复杂业务模型、公共组件库中真正用起来,而不是”为了 TS 而 TS”。
TypeScript 是 Vue3 生态的基石,熟练掌握它,你在团队里就能承担更核心的组件库与架构职责。
