TypeScript 提供了丰富的基础类型,用于变量声明,明确变量的类型,提高代码的可读性和可维护性。
基本类型
let num: number = 10;
let floatNum: number = 3.14;
let str: string = "Hello, TypeScript";
true
和 false
。let isDone: boolean = false;
特殊类型
let anyValue: any = "可以是字符串";
anyValue = 123; // 也可以是数字
function logMessage(): void {
console.log("这是一条日志信息");
}
null
表示有意的空值,undefined
表示变量已声明但未赋值。let nullValue: null = null;
let undefinedValue: undefined = undefined;
参数与返回值类型:
// 加法函数:参数为 number,返回值为 number
function add(a: number, b: number): number {
return a + b;
}
可选参数与默认值:
function greet(name: string, msg?: string): string {
return msg ? `${msg}, ${name}` : `Hello, ${name}`;
}
greet("Alice", "Hi"); // 输出:"Hi, Alice"
interface
)与类型别名(type
)接口:定义对象结构(常用于 Vue 3 的 props
、状态等)
// 定义用户接口
interface User {
id: number;
name: string;
age?: number; // 可选属性
sayHi(): string; // 方法
}
类型别名:复用复杂类型(如联合类型、函数类型)
// 联合类型别名
type StrOrNum = string | number;
// 函数类型别名
type Callback = (data: any) => void;
联合类型(|
):表示取值可以是多种类型之一
let input: string | number;
input = "hello"; // 合法
input = 123; // 合法
交叉类型(&
):表示同时具备多种类型的特性
interface A { name: string; }
interface B { age: number; }
type AB = A & B; // AB 类型同时拥有 name 和 age 属性
const obj: AB = { name: "John", age: 30 };
Generics
)定义通用函数 / 组件,动态指定类型
// 泛型函数:返回参数本身
function identity<T>(arg: T): T {
return arg;
}
const str = identity<string>("Vue3"); // 类型推导为 string
const num = identity<number>(123); // 类型推导为 number
在 Vue 3 中的应用:自定义 Hook 或工具函数
// 通用响应式数据 Hook
function useState<T>(initialValue: T) {
const state = ref(initialValue);
return { state, update: (val: T) => (state.value = val) };
}
const { state, update } = useState<string>("initial");
ref
和 reactive
的类型推断
import { ref, reactive } from "vue";
// ref 自动推断类型为 number
const count = ref(0);
count.value++; // 正确写法
// reactive 需手动指定类型或通过接口推断
interface State {
name: string;
age: number;
}
const state = reactive<State>({ name: "Alice", age: 28 });
setup
函数的类型声明
import { defineComponent } from "vue";
export default defineComponent({
setup() {
// 显式声明返回值类型(可选,但推荐复杂场景使用)
return {
count: ref(0),
} as { count: Ref<number> };
},
});
props
和 emits
的类型安全使用 defineProps
声明 Prop 类型
// 方式1:接口 + defineProps
interface Props {
title: string;
isDisabled?: boolean;
}
export default defineComponent({
props: ["title", "isDisabled"], // 需与接口字段一致
setup(props) {
// props 自动推断为 Props 类型
console.log(props.title); // 类型为 string
return {};
},
});
// 方式2:类型字面量直接声明(推荐简洁场景)
const props = defineProps<{
title: string;
count: number[];
}>();
使用 defineEmits
声明自定义事件类型
// 方式1:枚举定义事件名
enum EventNames {
Click = "click",
Submit = "submit",
}
const emits = defineEmits<{
[EventNames.Click](id: number): void;
[EventNames.Submit](data: object): void;
}>();
// 触发事件时类型检查
emits(EventNames.Click, 123); // 合法
emits(EventNames.Submit, "wrong data"); // 报错:参数类型应为 object
computed
)与监听器(watch
)计算属性的类型推断
import { ref, computed } from "vue";
const count = ref(0);
const doubleCount = computed(() => count.value * 2);
// doubleCount 的类型为 ComputedRef
监听器的类型声明
import { ref, watch } from "vue";
const state = ref({ name: "John", age: 30 });
watch(
state,
(newVal, oldVal) => {
// newVal 和 oldVal 类型均为 { name: string; age: number; }
console.log(newVal.age);
},
{ deep: true }
);
ref
)的类型标注在模板中使用 ref
绑定 DOM 元素或子组件:
在父组件中提供数据:
// parent.vue
import { provide } from "vue";
interface Theme {
color: string;
size: "small" | "large";
}
const theme = reactive<Theme>({ color: "blue", size: "large" });
provide("theme", theme);
在子组件中注入数据:
// child.vue
import { inject } from "vue";
const theme = inject<Theme>("theme");
// theme 类型为 Theme | undefined,需处理非空情况
if (theme) {
console.log(theme.color);
}
路由参数类型推断
// router.ts
import { createRouter, createWebHistory } from "vue-router";
const routes = [
{ path: "/user/:id", component: UserComponent },
];
const router = createRouter({
history: createWebHistory(),
routes,
});
// 在组件中获取参数类型
const route = useRoute();
// route.params.id 自动推断为 string 类型
console.log(route.params.id);
定义 Store 接口
// store/userStore.ts
import { defineStore } from "pinia";
interface UserState {
name: string;
age: number;
}
export const useUserStore = defineStore<"user", UserState, {}, {
setName(name: string): void;
}>("user", {
state: () => ({ name: "Guest", age: 0 }),
actions: {
setName(name: string) {
this.name = name;
},
},
});
// 使用时自动类型提示
const userStore = useUserStore();
userStore.setName("Alice");
创建带类型的 Hook
// hooks/useMousePosition.ts
import { ref, Ref } from "vue";
type MousePosition = { x: number; y: number };
function useMousePosition(): Ref<MousePosition> {
const position = ref<MousePosition>({ x: 0, y: 0 });
onMounted(() => {
window.addEventListener("mousemove", (e) => {
position.value = { x: e.clientX, y: e.clientY };
});
});
return position;
}
// 在组件中使用
const mousePos = useMousePosition();
// mousePos.value 类型为 MousePosition
console.log(mousePos.value.x);
使用 vite
创建 TS 项目:
npm create vite@latest my-vue3-ts-app -- --template vue-ts
cd my-vue3-ts-app
npm install
npm run dev
tsconfig.json
)关键配置(Vue 3 推荐):
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"jsx": "VueJSX", // 支持 JSX 语法(如 Vue 3 模板中的表达式)
"strict": true, // 开启严格类型检查
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
.d.ts
)为 Vue 3 插件或全局变量添加类型声明(如 src/shims-vue.d.ts
):
declare module "vue" {
interface ComponentCustomProperties {
$http: (url: string) => Promise<any>; // 全局属性类型声明
}
}
// 声明 .vue 文件的类型
declare module "*.vue" {
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}
ref
值未初始化
错误:Cannot read property 'value' of null
解决:声明时指定初始值或添加非空断言(!
)
const inputRef = ref<HTMLInputElement>(null);
// 非空断言(确保在使用时已挂载)
inputRef.value!.focus();
Prop 类型不匹配
Type 'string' is not assignable to type 'number'
defineProps
声明与父组件传递的类型是否一致泛型参数缺失
错误:Type 'unknown' is not assignable to type 'string'
解决:显式指定泛型参数
const list = ref([] as string[]); // 显式声明数组元素类型为 string
props
/emits
类型声明。vite
创建 Vue 3 + TS 项目,实现一个带表单验证的组件(结合 ref
、computed
、类型断言)。