最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

TypeScript satisfies 操作符完全指南

 更新時(shí)間:2026年05月25日 09:24:29   作者:兆子龍  
本文主要介紹了TypeScript 4.9 引入的 satisfies 操作符,可以同時(shí)獲得類(lèi)型推斷和類(lèi)型驗(yàn)證,提升你的 TypeScript 代碼質(zhì)量,感興趣的可以了解一下

一、背景與問(wèn)題

1.1 傳統(tǒng)類(lèi)型聲明的困境

在 TypeScript 中,我們經(jīng)常面臨一個(gè)兩難選擇:是使用類(lèi)型注解犧牲類(lèi)型推斷,還是保持推斷卻失去類(lèi)型檢查?

// 方案 1:類(lèi)型注解 —— 失去類(lèi)型推斷
const config1: Record<string, string | number> = {
    port: 8080,
    host: "localhost",
    debug: true  // ? 錯(cuò)誤:debug 是 boolean,不是 string | number
};
// TypeScript 會(huì)報(bào)錯(cuò),但我們失去了具體的類(lèi)型信息

// 方案 2:類(lèi)型推斷 —— 失去類(lèi)型驗(yàn)證
const config2 = {
    port: 8080,
    host: "localhost",
    debug: true  // ? 推斷為 { port: number; host: string; debug: boolean }
};
// TypeScript 沒(méi)有報(bào)錯(cuò),但我們無(wú)法確保 config2 符合預(yù)期格式

// 方案 3:雙重標(biāo)注 —— 代碼冗余
const config3: { port: number; host: string } = {
    port: 8080,
    host: "localhost"
};
// 既保證類(lèi)型,又保留推斷,但寫(xiě)起來(lái)麻煩

1.2satisfies的誕生

TypeScript 4.9 引入了 satisfies 操作符來(lái)解決這個(gè)困境:

// 使用 satisfies —— 魚(yú)與熊掌兼得
const config = {
    port: 8080,
    host: "localhost",
    debug: true
} satisfies ConfigType;

// ? 類(lèi)型被驗(yàn)證是否符合 ConfigType
// ? 同時(shí)保留了 config 的具體類(lèi)型推斷({ port: number; host: string; debug: boolean })

1.3 本文目標(biāo)

  1. 理解 satisfies 的核心概念
  2. 掌握各種使用場(chǎng)景
  3. 學(xué)會(huì)配合其他 TypeScript 特性使用
  4. 了解最佳實(shí)踐和注意事項(xiàng)

二、核心概念

2.1 基本語(yǔ)法

// 語(yǔ)法結(jié)構(gòu)
expression satisfies Type

// 實(shí)際示例
type RGB = [red: number, green: number, blue: number];
const palette = {
    red: [255, 0, 0],
    green: "#00ff00",
    blue: [0, 0, 255]
} satisfies Record<string, RGB | string>;

// palette 的類(lèi)型是:
// {
//     red: RGB;
//     green: string;
//     blue: RGB;
// }
// 而不是 Record<string, RGB | string>

2.2 類(lèi)型推斷 vs 類(lèi)型驗(yàn)證

// 場(chǎng)景:定義一個(gè)配置對(duì)象
type Config = {
    port: number;
    host: string;
    protocol: "http" | "https";
};

// 傳統(tǒng)方式 1:類(lèi)型注解
const config1: Config = {
    port: 8080,
    host: "localhost",
    protocol: "http"
};
// ? 類(lèi)型檢查通過(guò)
// ? config1.port 被推斷為 number,但 Config 允許任意 number
//    如果 Config 變成 { port: 80 | 8080 },config1 仍然有效(因?yàn)?number 兼容)

// satisfies 方式
const config2 = {
    port: 8080,
    host: "localhost",
    protocol: "http"
} satisfies Config;
// ? 類(lèi)型檢查通過(guò)
// ? config2.port 是具體的字面量類(lèi)型 8080,而不是 number

2.3 關(guān)鍵特性

特性 1:保留字面量類(lèi)型

type Direction = "north" | "south" | "east" | "west";

const directions1: Direction[] = ["north", "south"];  // string[] 被推斷
const directions2 = ["north", "south"] satisfies Direction[];  // ("north" | "south")[] 被推斷

// 差異:
directions1.push("任意字符串");  // ? 允許(類(lèi)型為 string[])
directions2.push("任意字符串");  // ? 錯(cuò)誤(類(lèi)型為 ("north" | "south")[])

特性 2:聯(lián)合類(lèi)型成員驗(yàn)證

type StringOrNumber = string | number;

const mixed = {
    a: "hello",
    b: 42,
    c: true  // ? 這里會(huì)報(bào)錯(cuò),因?yàn)?c: boolean 不在 StringOrNumber 中
} satisfies Record<string, StringOrNumber>;

特性 3:嵌套對(duì)象驗(yàn)證

type Nested = {
    user: {
        name: string;
        age: number;
    };
    settings: {
        theme: "light" | "dark";
        notifications: boolean;
    };
};

const data = {
    user: {
        name: "張三",
        age: 25
    },
    settings: {
        theme: "dark",
        notifications: true
    }
} satisfies Nested;

// data.user.name 是 string 類(lèi)型
// data.settings.theme 是 "light" | "dark" 類(lèi)型

三、實(shí)戰(zhàn)應(yīng)用場(chǎng)景

3.1 配置對(duì)象驗(yàn)證

// 場(chǎng)景 1:應(yīng)用配置
type AppConfig = {
    server: {
        port: number;
        host: string;
    };
    database: {
        url: string;
        poolSize: number;
    };
    features: string[];
};

const appConfig = {
    server: {
        port: 3000,
        host: "localhost"
    },
    database: {
        url: "postgresql://localhost/mydb",
        poolSize: 10
    },
    features: ["auth", "logging", "analytics"]
} satisfies AppConfig;

// ? 驗(yàn)證通過(guò)
// ? appConfig.server.port 是 number 類(lèi)型(具體值 3000)
// ? appConfig.features 是 string[] 類(lèi)型

// 訪問(wèn)時(shí)獲得完整類(lèi)型提示
appConfig.server.port  // number
appConfig.features[0]  // string

3.2 路由配置

// 場(chǎng)景 2:路由表定義
type Route = {
    path: string;
    component: string;
    meta?: {
        title?: string;
        requiresAuth?: boolean;
    };
};

const routes = {
    home: {
        path: "/",
        component: "HomePage"
    },
    about: {
        path: "/about",
        component: "AboutPage",
        meta: {
            title: "關(guān)于我們"
        }
    },
    login: {
        path: "/login",
        component: "LoginPage",
        meta: {
            requiresAuth: false
        }
    },
    profile: {
        path: "/profile",
        component: "ProfilePage",
        meta: {
            title: "個(gè)人中心",
            requiresAuth: true
        }
    }
} satisfies Record<string, Route>;

// ? 類(lèi)型安全
// ? 可以用 routes.home.path 訪問(wèn)
// ? meta 的可選屬性都被正確處理

// 類(lèi)型推斷示例
type RouteKey = keyof typeof routes;
// type RouteKey = "home" | "about" | "login" | "profile"

routes.home.meta?.requiresAuth  // boolean | undefined

3.3 主題與樣式系統(tǒng)

// 場(chǎng)景 3:設(shè)計(jì)令牌系統(tǒng)
type DesignToken = string | number;

type Theme = {
    colors: Record<string, DesignToken>;
    spacing: Record<string, number>;
    fonts: Record<string, string>;
};

const theme = {
    colors: {
        primary: "#007bff",
        secondary: "#6c757d",
        success: "#28a745",
        error: "#dc3545",
        white: "#ffffff"
    },
    spacing: {
        xs: 4,
        sm: 8,
        md: 16,
        lg: 24,
        xl: 32
    },
    fonts: {
        sans: "system-ui, sans-serif",
        mono: "Monaco, monospace"
    }
} satisfies Theme;

// 使用示例
function createStyles(colorKey: keyof typeof theme.colors) {
    return { color: theme.colors[colorKey] };
}

createStyles("primary");  // ? 正確
createStyles("unknown");  // ? 類(lèi)型錯(cuò)誤

3.4 狀態(tài)機(jī)定義

// 場(chǎng)景 4:有限狀態(tài)機(jī)
type State = "idle" | "loading" | "success" | "error";

type Transition = {
    from: State;
    to: State;
    guard?: (ctx: Context) => boolean;
};

type StateMachine = {
    initial: State;
    transitions: Transition[];
    context: Context;
};

const machine = {
    initial: "idle" as const,
    transitions: [
        { from: "idle", to: "loading" },
        { from: "loading", to: "success" },
        { from: "loading", to: "error" },
        { from: "error", to: "loading" }
    ],
    context: {
        retryCount: 0,
        lastError: null as Error | null
    }
} satisfies StateMachine;

// 類(lèi)型安全的狀態(tài)機(jī)
type StateKey = typeof machine.initial;
// type StateKey = "idle"

3.5 API 響應(yīng)結(jié)構(gòu)

// 場(chǎng)景 5:API 響應(yīng)類(lèi)型
type ApiResponse<T> = {
    data: T;
    status: number;
    message?: string;
};

type UserResponse = {
    data: {
        id: string;
        name: string;
        email: string;
    };
    status: 200;
};

const userResponse = {
    data: {
        id: "user_123",
        name: "張三",
        email: "zhangsan@example.com"
    },
    status: 200
} satisfies ApiResponse<{ id: string; name: string; email: string }>;

// userResponse.data.name 是 string
// userResponse.status 是字面量 200

3.6 插件/擴(kuò)展系統(tǒng)

// 場(chǎng)景 6:插件注冊(cè)表
type Plugin = {
    name: string;
    version: string;
    init: (app: App) => void;
    dependencies?: string[];
};

type PluginRegistry = Record<string, Plugin>;

const registry = {
    auth: {
        name: "Auth Plugin",
        version: "1.0.0",
        init: (app) => {
            console.log("Initializing auth...");
        }
    },
    logger: {
        name: "Logger Plugin",
        version: "2.0.0",
        init: (app) => {
            console.log("Initializing logger...");
        },
        dependencies: ["auth"]  // 依賴(lài) auth 插件
    },
    analytics: {
        name: "Analytics Plugin",
        version: "1.5.0",
        init: (app) => {
            console.log("Initializing analytics...");
        }
    }
} satisfies PluginRegistry;

// registry 是 Record<string, Plugin>
// 同時(shí)保留了每個(gè)插件的具體類(lèi)型

function loadPlugin(name: keyof typeof registry) {
    const plugin = registry[name];
    plugin.init(app);  // 完整類(lèi)型提示
}

四、高級(jí)用法

4.1 結(jié)合類(lèi)型推斷

// 使用 infer 推斷 satisfies 的結(jié)果類(lèi)型
type InferSatisfies<T, U> = T extends satisfies U ? T : never;

// 示例
type Colors = "red" | "green" | "blue";

const myColors = ["red", "green"] satisfies Colors[];
// myColors 的類(lèi)型是 ("red" | "green")[]

// 結(jié)合 typeof
const baseColors = ["red", "green", "blue"] as const;
type BaseColors = typeof baseColors;
// type BaseColors = readonly ["red", "green", "blue"]

const userColors = ["red", "green"] satisfies typeof baseColors;
// userColors 的類(lèi)型是 ("red" | "green")[]

4.2 條件類(lèi)型中的 satisfies

// 根據(jù) satisfies 結(jié)果改變類(lèi)型
type ValidateConfig<T> = T satisfies Record<string, unknown>
    ? { [K in keyof T]: T[K] }
    : never;

// 使用
type Result = ValidateConfig<{ port: number; host: string }>;
// Result = { port: number; host: string }

type InvalidResult = ValidateConfig<"not an object">;
// InvalidResult = never

4.3 多層嵌套驗(yàn)證

// 復(fù)雜的嵌套類(lèi)型
type DeepConfig = {
    level1: {
        level2: {
            level3: {
                value: number;
                label: string;
            }[];
        };
    };
};

const deepConfig = {
    level1: {
        level2: {
            level3: [
                { value: 1, label: "One" },
                { value: 2, label: "Two" }
            ]
        }
    }
} satisfies DeepConfig;

// deepConfig.level1.level2.level3[0].value 是 number
// deepConfig.level1.level2.level3[0].label 是 string

4.4 與 readonly 配合

// 不可變配置
type ImmutableConfig = {
    readonly [key: string]: string | number;
};

const immutableConfig = {
    apiUrl: "https://api.example.com",
    timeout: 5000
} satisfies ImmutableConfig;

// 如果嘗試修改會(huì)報(bào)錯(cuò)
immutableConfig.apiUrl = "other";  // ? 錯(cuò)誤(假設(shè) ApiUrl 不在類(lèi)型中)

4.5 與 as const 配合

// 結(jié)合 as const 實(shí)現(xiàn)完全字面量類(lèi)型
const strictRoutes = {
    home: "/",
    about: "/about",
    contact: "/contact"
} as const satisfies Record<string, string>;

// 所有值都是字面量類(lèi)型
type RoutePath = typeof strictRoutes[keyof typeof strictRoutes];
// type RoutePath = "/" | "/about" | "/contact"

五、與 typeof 的區(qū)別

5.1 基本對(duì)比

const obj = {
    x: 1,
    y: "hello"
};

// typeof 獲取類(lèi)型
type ObjType = typeof obj;
// type ObjType = { x: number; y: string; }

// satisfies 驗(yàn)證并保留推斷
const validated = {
    x: 1,
    y: "hello"
} satisfies { x: number; y: string };
// validated 的類(lèi)型仍然是 { x: number; y: string }

// 關(guān)鍵區(qū)別:typeof 不驗(yàn)證,satisfies 驗(yàn)證

5.2 實(shí)際差異

// 示例 1:超出類(lèi)型的字段
const obj1 = {
    x: 1,
    y: 2,
    z: 3  // 額外的字段
};

type Obj1Type = typeof obj1;
// type Obj1Type = { x: number; y: number; z: number; }
// ? 沒(méi)有錯(cuò)誤,額外字段被保留

const obj2 = {
    x: 1,
    y: 2
} satisfies { x: number; y: string };
// ? 錯(cuò)誤:y: number 不能賦值給 string | number

5.3 組合使用

// 最佳實(shí)踐:先 satisfies 驗(yàn)證,再 typeof 提取類(lèi)型
const validatedConfig = {
    port: 8080,
    host: "localhost",
    debug: false
} satisfies {
    port: number;
    host: string;
    debug?: boolean;
};

// 用 typeof 提取新類(lèi)型
type Config = typeof validatedConfig;
// type Config = {
//     port: number;
//     host: string;
//     debug: boolean | undefined;
// }

// 兩者結(jié)合的優(yōu)勢(shì):
// 1. 驗(yàn)證初始對(duì)象符合預(yù)期結(jié)構(gòu)
// 2. 提取出帶完整推斷的新類(lèi)型供其他地方使用

六、常見(jiàn)問(wèn)題與解決方案

6.1 問(wèn)題 1:可選屬性處理

// 問(wèn)題
type Config = {
    required: string;
    optional?: number;
};

const config = {
    required: "value"
} satisfies Config;
// ? 正確,可選屬性可以省略

// 但如果想訪問(wèn) optional,需要處理 undefined
const opt: number | undefined = config.optional;

6.2 問(wèn)題 2:聯(lián)合類(lèi)型驗(yàn)證

// 問(wèn)題:如何驗(yàn)證值屬于聯(lián)合類(lèi)型
type Color = "red" | "green" | "blue";

const colors = ["red", "green", "purple"] satisfies Color[];
// ? 錯(cuò)誤:purple 不在 Color 中

// 解決方案:使用類(lèi)型謂詞
function isColor(val: string): val is Color {
    return ["red", "green", "blue"].includes(val);
}

function filterColors(arr: string[]): Color[] {
    return arr.filter(isColor);
}

6.3 問(wèn)題 3:類(lèi)屬性驗(yàn)證

// satisfies 不能用于類(lèi)
class Config {
    port = 8080;
}

const config = new Config() satisfies { port: number };
// ? 錯(cuò)誤:satisfies 不能用于類(lèi)實(shí)例

// 解決方案:使用類(lèi)型斷言或 separate 類(lèi)型檢查
const config = new Config();
const checked: { port: number } = config as { port: number };

6.4 問(wèn)題 4:泛型中的 satisfies

// 問(wèn)題:泛型約束
function processConfig<T extends { port: number }>(
    config: T satisfies T
) {
    // 這個(gè)語(yǔ)法不對(duì)
}

// 正確用法
function processConfig<T>(
    config: T satisfies { port: number }
) {
    // config 是 T 類(lèi)型,同時(shí)滿足 { port: number }
}

七、性能考慮

7.1 類(lèi)型檢查開(kāi)銷(xiāo)

// satisfies 在編譯時(shí)進(jìn)行類(lèi)型檢查
// 對(duì)運(yùn)行時(shí)性能沒(méi)有影響

// 但復(fù)雜的多層嵌套 satisfies 可能增加編譯時(shí)間
const veryComplex = {
    // 100+ 嵌套層
} satisfies VeryDeepNestedType;
// 編譯時(shí)間可能增加

7.2 最佳實(shí)踐

// ? 推薦:明確的類(lèi)型定義
type KnownShape = {
    a: string;
    b: number;
};

const good = { a: "x", b: 1 } satisfies KnownShape;

// ? 不推薦:過(guò)度嵌套
const bad = {
    level1: {
        level2: {
            // 更多層...
        }
    }
} satisfies DeepType;

八、遷移指南

8.1 從類(lèi)型斷言遷移

// 舊代碼(使用類(lèi)型斷言)
const config = {
    port: 8080
} as {
    port: number;
};

// 新代碼(使用 satisfies)
const config = {
    port: 8080
} satisfies {
    port: number;
};

// 差異:
// as:不驗(yàn)證類(lèi)型,可能導(dǎo)致意外的類(lèi)型寬化
// satisfies:驗(yàn)證類(lèi)型,同時(shí)保留字面量推斷

8.2 從類(lèi)型注解遷移

// 舊代碼(使用類(lèi)型注解)
const config: {
    port: number;
    host: string;
} = {
    port: 8080,
    host: "localhost"
};

// 新代碼(使用 satisfies)
const config = {
    port: 8080,
    host: "localhost"
} satisfies {
    port: number;
    host: string;
};

// 優(yōu)勢(shì):
// 1. 錯(cuò)誤信息更明確(如果類(lèi)型不匹配,指向具體字段)
// 2. 保留字面量類(lèi)型推斷

九、與其他 TypeScript 特性的對(duì)比

9.1 vs 類(lèi)型別名

type A = string | number;
type B = "a" | "b";

// satisfies 驗(yàn)證值是否符合類(lèi)型
const val = "a" satisfies B;

9.2 vs 類(lèi)型守衛(wèi)

// 類(lèi)型守衛(wèi)需要在運(yùn)行時(shí)檢查
function isConfig(val: unknown): val is Config {
    return typeof val === "object" && val !== null && "port" in val;
}

// satisfies 在編譯時(shí)驗(yàn)證
const config = {} satisfies Config;

9.3 vs 類(lèi)型斷言

// 類(lèi)型斷言(as)不驗(yàn)證
const a = "hello" as number;  // ? 沒(méi)有錯(cuò)誤,但類(lèi)型是錯(cuò)的

// satisfies 驗(yàn)證
const b = "hello" satisfies number;  // ? 錯(cuò)誤:string 不能賦值給 number

十、總結(jié)

10.1 關(guān)鍵要點(diǎn)

  1. satisfies 在編譯時(shí)驗(yàn)證類(lèi)型,同時(shí)保留類(lèi)型推斷
  2. 它解決了類(lèi)型注解和類(lèi)型推斷的兩難困境
  3. 適用于配置對(duì)象、路由表、主題系統(tǒng)等場(chǎng)景
  4. 可以與 typeof、泛型、條件類(lèi)型等配合使用

10.2 使用建議

  • ? 使用 satisfies 驗(yàn)證配置對(duì)象和常量
  • ? 使用 satisfies 替代 as 類(lèi)型斷言
  • ? 使用 satisfies 保留字面量類(lèi)型推斷
  • ? 不要在簡(jiǎn)單場(chǎng)景過(guò)度使用
  • ? 不要用 satisfies 替代運(yùn)行時(shí)驗(yàn)證

10.3 資源推薦

到此這篇關(guān)于TypeScript satisfies 操作符完全指南的文章就介紹到這了,更多相關(guān)TypeScript satisfies 操作符內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

鸡泽县| 信丰县| 亚东县| 延津县| 景东| 稻城县| 定安县| 望谟县| 宜章县| 明水县| 江阴市| 林芝县| 保亭| 盐源县| 曲阜市| 新宾| 陕西省| 湖口县| 凤翔县| 兴和县| 常宁市| 平山县| 呼和浩特市| 呼伦贝尔市| 弥渡县| 积石山| 阿巴嘎旗| 普宁市| 综艺| 磐石市| 晋城| 通州市| 额尔古纳市| 兴安县| 容城县| 金塔县| 禹州市| 百色市| 浪卡子县| 靖边县| 昆山市|