Vue3+TypeScript 常用代碼示例總結(jié)
一、TypeScript 內(nèi)置工具類型
1.1Partial? 的作用
Partial<T>是 TypeScript 內(nèi)置的工具類型,它可以將類型 T的所有屬性變?yōu)榭蛇x。
// Partial 的實現(xiàn)原理(簡化版)
type Partial<T> = {
[P in keyof T]?: T[P];
};
// 使用示例
interface Theme {
primaryColor: string;
secondaryColor: string;
fontSize: number;
}
// 正常 Theme 類型,所有屬性都是必需的
const theme1: Theme = {
primaryColor: '#1890ff',
secondaryColor: '#52c41a',
fontSize: 14
};
// ? 所有屬性都必須提供
// 使用 Partial<Theme> 后,所有屬性都變?yōu)榭蛇x
const theme2: Partial<Theme> = {
primaryColor: '#1890ff'
// secondaryColor 和 fontSize 可以不提供
};
// ? 可以只提供部分屬性
// 在函數(shù)參數(shù)中使用 Partial
function updateTheme(theme: Partial<Theme>): void {
// 可以只更新部分屬性
// theme 可能只包含 primaryColor,或只包含 fontSize,或都包含
}
// 調(diào)用示例
updateTheme({ primaryColor: '#ff4d4f' }); // ? 只更新一個屬性
updateTheme({ fontSize: 16 }); // ? 只更新另一個屬性
updateTheme({}); // ? 傳遞空對象也可以1.2 其他常用工具類型
// 1. Required<T> - 將可選屬性變?yōu)楸靥?
interface User {
id: number;
name?: string; // 可選
email?: string; // 可選
}
type RequiredUser = Required<User>;
// 等價于:
// {
// id: number;
// name: string; // 變?yōu)楸靥?
// email: string; // 變?yōu)楸靥?
// }
// 2. Readonly<T> - 將所有屬性變?yōu)橹蛔x
type ReadonlyUser = Readonly<User>;
// 等價于:
// {
// readonly id: number;
// readonly name?: string;
// readonly email?: string;
// }
// 3. Pick<T, K> - 從 T 中挑選部分屬性
type UserBasicInfo = Pick<User, 'id' | 'name'>;
// 等價于:
// {
// id: number;
// name?: string;
// }
// 4. Omit<T, K> - 從 T 中排除部分屬性
type UserWithoutId = Omit<User, 'id'>;
// 等價于:
// {
// name?: string;
// email?: string;
// }
// 5. Record<K, T> - 創(chuàng)建鍵值對類型
type UserMap = Record<string, User>;
// 等價于:
// {
// [key: string]: User;
// }
// 6. Exclude<T, U> - 從 T 中排除可以賦值給 U 的類型
type T1 = 'a' | 'b' | 'c';
type T2 = 'a';
type Result = Exclude<T1, T2>; // 'b' | 'c'
// 7. Extract<T, U> - 從 T 中提取可以賦值給 U 的類型
type T3 = 'a' | 'b' | 'c';
type T4 = 'a' | 'd';
type Result2 = Extract<T3, T4>; // 'a'
// 8. NonNullable<T> - 排除 null 和 undefined
type T5 = string | number | null | undefined;
type Result3 = NonNullable<T5>; // string | number二、Vue 3 中的類型
2.1ComputedRef? 類型
ComputedRef<T>是 Vue 3 中計算屬性的類型,它是 Ref<T>的子類型。
import { ref, computed, Ref, ComputedRef } from 'vue'
// 1. 基礎(chǔ)使用
const count = ref<number>(0); // Ref<number>
const doubleCount = computed(() => count.value * 2); // ComputedRef<number>
// 2. 顯式類型聲明
const doubleCount2: ComputedRef<number> = computed(() => count.value * 2);
// 3. 帶 getter 和 setter 的計算屬性
const fullName = computed<string>({
get: () => `${firstName.value} ${lastName.value}`,
set: (value: string) => {
const [first, last] = value.split(' ')
firstName.value = first
lastName.value = last || ''
}
});
// 4. 在函數(shù)參數(shù)中使用
function logComputedValue(computedValue: ComputedRef<any>): void {
console.log(computedValue.value);
}
logComputedValue(doubleCount);2.2 Vue 3 常用類型
import {
Ref, // 響應式引用類型
ComputedRef, // 計算屬性類型
UnwrapRef, // 解包響應式類型
MaybeRef, // 可能是 Ref 或普通值
MaybeRefOrGetter, // 可能是 Ref、getter 函數(shù)或普通值
WritableComputedRef, // 可寫的計算屬性
ShallowRef, // 淺層 Ref
ShallowReactive, // 淺層 reactive
ToRefs, // 將 reactive 轉(zhuǎn)換為 refs
ComponentPublicInstance, // 組件實例類型
VNode, // 虛擬節(jié)點類型
Component // 組件類型
} from 'vue'
// 1. Ref 類型
const countRef: Ref<number> = ref(0);
// 2. UnwrapRef - 獲取 Ref 內(nèi)部的類型
type CountType = UnwrapRef<typeof countRef>; // number
// 3. MaybeRef - 接受 Ref 或普通值
function useDouble(value: MaybeRef<number>): ComputedRef<number> {
return computed(() => {
// 判斷是否是 Ref
if (isRef(value)) {
return value.value * 2
}
return value * 2
});
}
// 或者使用 unref 工具函數(shù)
import { unref } from 'vue'
function useDouble2(value: MaybeRef<number>): ComputedRef<number> {
return computed(() => unref(value) * 2);
}
// 4. ToRefs - 將 reactive 轉(zhuǎn)換為多個 ref
interface State {
count: number;
name: string;
}
const state = reactive<State>({ count: 0, name: 'Vue' });
const stateRefs: ToRefs<State> = toRefs(state);
// 現(xiàn)在可以使用 stateRefs.count.value 和 stateRefs.name.value三、實際應用示例
3.1 表單組件示例
import { defineComponent, reactive, computed, toRefs } from 'vue'
// 表單數(shù)據(jù)類型
interface FormData {
username: string
email: string
age: number | null
agree: boolean
}
// 表單驗證規(guī)則類型
type ValidationRule = (value: any) => string | true
interface FormRules {
[key: string]: ValidationRule | ValidationRule[]
}
export default defineComponent({
setup() {
// 表單數(shù)據(jù)
const formData = reactive<FormData>({
username: '',
email: '',
age: null,
agree: false
})
// 表單驗證規(guī)則
const rules: FormRules = {
username: [
(value: string) => !!value || '用戶名不能為空',
(value: string) => value.length >= 3 || '用戶名至少3個字符'
],
email: [
(value: string) => !!value || '郵箱不能為空',
(value: string) => /.+@.+..+/.test(value) || '郵箱格式不正確'
],
age: (value: number | null) => {
if (value === null) return '年齡不能為空'
if (value < 0) return '年齡不能為負數(shù)'
if (value > 150) return '年齡不能超過150歲'
return true
}
}
// 表單驗證狀態(tài)
const errors = reactive<Partial<Record<keyof FormData, string>>>({})
// 驗證單個字段
const validateField = (field: keyof FormData): boolean => {
const value = formData[field]
const rule = rules[field]
if (!rule) {
delete errors[field]
return true
}
const rulesArray = Array.isArray(rule) ? rule : [rule]
for (const validate of rulesArray) {
const result = validate(value)
if (typeof result === 'string') {
errors[field] = result
return false
}
}
delete errors[field]
return true
}
// 驗證整個表單
const validateForm = (): boolean => {
let isValid = true
Object.keys(formData).forEach(field => {
if (!validateField(field as keyof FormData)) {
isValid = false
}
})
return isValid
}
// 提交表單
const submitForm = (): void => {
if (!validateForm()) {
console.log('表單驗證失敗')
return
}
console.log('提交表單:', formData)
// 這里可以調(diào)用 API
}
// 重置表單
const resetForm = (): void => {
Object.assign(formData, {
username: '',
email: '',
age: null,
agree: false
})
Object.keys(errors).forEach(key => {
delete errors[key as keyof typeof errors]
})
}
// 計算屬性:表單是否有效
const isFormValid = computed<boolean>(() => {
return Object.keys(errors).length === 0 &&
formData.username !== '' &&
formData.email !== '' &&
formData.age !== null &&
formData.agree
})
return {
// 使用 toRefs 保持響應性
...toRefs(formData),
errors,
isFormValid,
validateField,
validateForm,
submitForm,
resetForm
}
}
})3.2 使用工具類型的通用函數(shù)
// utils/types.ts
// 自定義工具類型
// 1. 深度可選類型
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// 2. 深度只讀類型
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// 3. 可空類型
type Nullable<T> = T | null | undefined;
// 4. 提取函數(shù)返回類型
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
// 5. 提取函數(shù)參數(shù)類型
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
// 6. 提取 Promise 的返回值類型
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
// 使用示例
interface User {
id: number;
name: string;
profile: {
avatar: string;
bio: string;
};
tags: string[];
}
// 深度可選
type PartialUser = DeepPartial<User>;
// 可以這樣使用:
const user1: PartialUser = {
name: '張三',
profile: {
avatar: 'avatar.jpg'
// bio 可以不提供
}
// tags 可以不提供
};
// 深度只讀
type ReadonlyUser = DeepReadonly<User>;
const user2: ReadonlyUser = {
id: 1,
name: '李四',
profile: {
avatar: 'avatar.jpg',
bio: 'Hello'
},
tags: ['a', 'b']
};
// user2.profile.bio = 'World'; // ? 錯誤:不能修改只讀屬性
// 可空類型
let nullableString: Nullable<string> = 'Hello';
nullableString = null; // ?
nullableString = undefined; // ?3.3 組合式函數(shù)的類型
// composables/useFetch.ts
import { ref, computed, Ref, ComputedRef } from 'vue'
// 請求狀態(tài)類型
type FetchStatus = 'idle' | 'loading' | 'success' | 'error'
// 返回類型
interface UseFetchReturn<T> {
data: Ref<T | null>
error: Ref<string | null>
status: Ref<FetchStatus>
isLoading: ComputedRef<boolean>
isSuccess: ComputedRef<boolean>
isError: ComputedRef<boolean>
execute: (url: string, options?: RequestInit) => Promise<void>
reset: () => void
}
// 選項類型
interface UseFetchOptions {
immediate?: boolean
initialData?: any
}
export function useFetch<T = any>(
initialUrl?: string,
options: UseFetchOptions = {}
): UseFetchReturn<T> {
const { immediate = false, initialData = null } = options
const data = ref<T | null>(initialData) as Ref<T | null>
const error = ref<string | null>(null)
const status = ref<FetchStatus>('idle')
const isLoading = computed(() => status.value === 'loading')
const isSuccess = computed(() => status.value === 'success')
const isError = computed(() => status.value === 'error')
const execute = async (url: string, requestOptions?: RequestInit): Promise<void> => {
status.value = 'loading'
error.value = null
try {
const response = await fetch(url, requestOptions)
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
const result = await response.json()
data.value = result
status.value = 'success'
} catch (err) {
error.value = err instanceof Error ? err.message : '請求失敗'
status.value = 'error'
}
}
const reset = (): void => {
data.value = initialData
error.value = null
status.value = 'idle'
}
// 立即執(zhí)行
if (immediate && initialUrl) {
execute(initialUrl)
}
return {
data,
error,
status,
isLoading,
isSuccess,
isError,
execute,
reset
}
}
// 在組件中使用
import { defineComponent, onMounted } from 'vue'
interface Post {
id: number
title: string
body: string
userId: number
}
export default defineComponent({
setup() {
// 使用 useFetch
const {
data: posts,
error,
isLoading,
isSuccess,
execute
} = useFetch<Post[]>('https://jsonplaceholder.typicode.com/posts')
// 或者延遲執(zhí)行
const {
data: user,
execute: fetchUser
} = useFetch<Post>(undefined, { immediate: false })
onMounted(() => {
// 手動執(zhí)行
fetchUser('https://jsonplaceholder.typicode.com/posts/1')
})
// 重新獲取
const refresh = (): void => {
execute('https://jsonplaceholder.typicode.com/posts')
}
return {
posts,
error,
isLoading,
isSuccess,
refresh
}
}
})四、常見問題解答
Q1: 什么時候用Partial?
A: ? 當你需要創(chuàng)建一個對象,它包含原始類型的一部分屬性時使用。
// 更新用戶信息時,通常只需要更新部分字段
interface User {
id: number
name: string
email: string
age: number
}
function updateUser(userId: number, updates: Partial<User>): void {
// updates 可以只包含 name,或只包含 email,或任意組合
// 但不會包含不存在的屬性
}Q2:ComputedRef和普通Ref有什么區(qū)別?
A: ? 主要區(qū)別:
ComputedRef是只讀的,你不能直接修改它的值ComputedRef的值是通過計算得到的- 你可以為
ComputedRef提供 setter,但通常不建議
// Ref - 可以直接修改
const count = ref(0)
count.value = 1 // ? 可以
// ComputedRef - 默認只讀
const double = computed(() => count.value * 2)
double.value = 4 // ? 錯誤:不能直接修改計算屬性
// 帶 setter 的 ComputedRef
const fullName = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (value) => {
const [first, last] = value.split(' ')
firstName.value = first
lastName.value = last || ''
}
})Q3: 什么時候需要顯式指定類型?
A: ? TypeScript 有類型推斷,但以下情況建議顯式指定:
// 1. 函數(shù)參數(shù)和返回值
function add(a: number, b: number): number {
return a + b
}
// 2. 復雜對象
interface Config {
apiUrl: string
timeout: number
retry: boolean
}
const config: Config = {
apiUrl: '/api',
timeout: 5000,
retry: true
}
// 3. 組件 Props
interface Props {
title: string
count: number
items: Array<{ id: number; name: string }>
}
// 4. API 響應
interface ApiResponse<T = any> {
code: number
data: T
message: string
}
async function fetchUser(id: number): Promise<ApiResponse<User>> {
const response = await fetch(`/api/users/${id}`)
return response.json()
}記住:TypeScript 的核心價值在于類型安全,良好的類型定義能幫助你在編碼階段就發(fā)現(xiàn)潛在的錯誤,提高代碼質(zhì)量和開發(fā)效率。
到此這篇關(guān)于Vue3+TypeScript 常用代碼示例總結(jié)的文章就介紹到這了,更多相關(guān)Vue3 TypeScript示例內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vuex之this.$store.dispatch()與this.$store.commit()的區(qū)別及說明
這篇文章主要介紹了vuex之this.$store.dispatch()與this.$store.commit()的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06
vant之關(guān)于van-list的使用以及一些坑的解決方案
這篇文章主要介紹了vant之關(guān)于van-list的使用以及一些坑的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06
el-select自定義指令實現(xiàn)觸底加載分頁請求options數(shù)據(jù)(完整代碼和接口可直接用)
某些情況下,下拉框需要做觸底加載,發(fā)請求,獲取option的數(shù)據(jù),下面給大家分享el-select自定義指令實現(xiàn)觸底加載分頁請求options數(shù)據(jù)(附上完整代碼和接口可直接用),感興趣的朋友參考下吧2024-02-02
Vue+Express實現(xiàn)登錄注銷功能的實例代碼
這篇文章主要介紹了Vue+Express實現(xiàn)登錄,注銷功能,本文通過實例代碼講解的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-05-05
vue實現(xiàn)動態(tài)給data函數(shù)中的屬性賦值
這篇文章主要介紹了vue實現(xiàn)動態(tài)給data函數(shù)中的屬性賦值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09
如何在Vue3中使用視頻庫Video.js實現(xiàn)視頻播放功能
在Vue3項目中集成Video.js庫,可以創(chuàng)建強大的視頻播放功能,這篇文章主要介紹了如何在Vue3中使用視頻庫Video.js實現(xiàn)視頻播放功能,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-09-09

