在前端開發中,JSON.parse(JSON.stringify(obj)) 常被用來實現簡單的深拷貝。然而,這套「組合拳」在 TypeScript 環境下究竟是如何被推導的?解析後的資料又該如何維持型別安全?
本文將從 JavaScript 的 JSON 轉譯機制談起,並延伸至 TypeScript 泛型處理,最後橫向比較 React、Vue 與 Angular 在搭配 TypeScript 時的語法異同與架構設計。
JSON.stringify 會將 JavaScript 記憶體中的物件序列化為純文字字串;JSON.parse 則解析該字串並在記憶體中建立一個全新的物件,藉此斷開傳址引用(Reference)。
const rawUser = { id: 101, name: "Alice", role: "admin" };
// 組合拳深拷貝
const clonedUser = JSON.parse(JSON.stringify(rawUser));
<T>在 TypeScript 中,JSON.parse() 的預設傳回型別為 any,這會破壞型別檢查。為了讓解析後的物件保有嚴格型別,我們通常會封裝一個帶有泛型 <T> 的安全解析函式:
function safeJsonParse<T>(jsonString: string): T | null {
try {
return JSON.parse(jsonString) as T;
} catch (error) {
console.error("JSON 解析失敗", error);
return null;
}
}
// 定義介面
interface User {
id: number;
name: string;
role: string;
}
// 搭配泛型使用,解析後會自動推導為 User 型別
const user = safeJsonParse<User>('{"id":101,"name":"Alice","role":"admin"}');
當我們將 TypeScript 套用到現代三大前端框架時,雖然底層都是強型別語言,但因各框架的設計哲學不同,在型別定義與開發體驗上有著顯著差異。
| 比較維度 | React | Vue (v3 Component) | Angular |
|---|---|---|---|
| TS 整合深度 | 外掛式 (JSX / TSX) | 漸進式 (<script setup lang="ts">) |
原生深度綁定 (Class-Based) |
| 狀態/屬性推導 | useState<T>, FC<Props> |
defineProps<Props>(), ref<T> |
Class 屬性裝飾器 (@Input()) |
| 樣板型別檢查 | 全靠 TSX 原生 JSX 檢查 | Vue Language Tools (Volar) 虛擬 AST 解析 | Angular Compiler (ngc) 專屬樣板編譯 |
| 依賴注入與介面 | Hooks / Context API | provide / inject (需搭配 InjectionKey<T>) |
原生依賴注入 (DI with Decorators) |
React 將 HTML 視為 JavaScript 的延伸(JSX),因此 TSX 的型別檢查完全由 TypeScript 原生編譯器完成,無需額外編譯工具。
import React, { useState } from 'react';
interface UserProps {
initialName: string;
}
export const UserCard: React.FC<UserProps> = ({ initialName }) => {
const [name, setName] = useState<string>(initialName);
return (
<div>
<h3>User: {name}</h3>
<button onClick={() => setName("Bob")}>Update</button>
</div>
);
};
<script setup lang="ts">)Vue 採用單檔案組件(SFC),在 Vue 3 中引入了編譯時巨集(Compiler Macros)如 defineProps,讓 SFC 能以極簡語法實現嚴格型別推導。
<script setup lang="ts">
import { ref } from 'vue';
interface Props {
initialName: string;
}
// 編譯時型別聲明
const props = defineProps<Props>();
const name = ref<string>(props.initialName);
const updateName = () => {
name.value = "Bob";
};
</script>
<template>
<div>
<h3>User: {{ name }}</h3>
<button @click="updateName">Update</button>
</div>
</template>
Angular 從誕生之初就是圍繞 TypeScript 打造,採用純面嚮物件(OOP)與裝飾器(Decorators)架構,狀態與邏輯皆封裝於 TypeScript Class 中。
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `
<div>
<h3>User: {{ name }}</h3>
<button (click)="updateName()">Update</button>
</div>
`
})
export class UserCardComponent {
@Input() initialName!: string;
name: string = '';
ngOnInit() {
this.name = this.initialName;
}
updateName(): void {
this.name = 'Bob';
}
}
掌握 TypeScript 泛型與三大框架的型別機制,能大幅降低運行時期的錯誤,提升應用程式的強健度。