TypeScript模板字面量類型高級用法示例代碼
一、模板字面量類型是什么
模板字面量類型是 TypeScript 4.1 引入的強大特性,它允許你在類型層面使用類似 JavaScript 模板字符串的語法。通過這個特性,你可以基于已有的字符串類型構(gòu)造出新的字符串類型,實現(xiàn)精確的類型推導(dǎo)和約束。
在介紹具體用法之前,讓我們先理解模板字面量類型的基本語法:
type Greeting = `Hello, ${string}`;
const greeting1: Greeting = 'Hello, World'; // ? 正確
const greeting2: Greeting = 'Hi, there'; // ? 錯誤,不匹配模式Greeting 類型表示所有以「Hello, 」開頭的字符串。只有符合這個模式的字符串才能賦值給 Greeting 類型的變量。
二、模板字面量類型的基本用法
2.1 字符串拼接類型
模板字面量類型最基礎(chǔ)的用法是拼接字符串:
type Part1 = 'Hello';
type Part2 = 'World';
type Greeting = `${Part1} ${Part2}`;
// Greeting 類型為 "Hello World"2.2 使用聯(lián)合類型
模板字面量類型可以與聯(lián)合類型配合使用,生成所有可能的組合:
type Direction = 'top' | 'bottom' | 'left' | 'right';
type MarginProperty = `margin${Capitalize<Direction>}`;
// MarginProperty 類型為 "marginTop" | "marginBottom" | "marginLeft" | "marginRight"
type BorderProperty = `border${Capitalize<Direction>}-width`;
// BorderProperty 類型為 "borderTop-width" | "borderBottom-width" | ...注意這里使用了 TypeScript 內(nèi)置的 Capitalize 工具類型,它可以將字符串類型的首字母轉(zhuǎn)為大寫。
2.3 內(nèi)置工具類型
TypeScript 提供了幾個與模板字面量類型配合使用的內(nèi)置工具類型:
Uppercase<StringType>:將字符串轉(zhuǎn)為大寫Lowercase<StringType>:將字符串轉(zhuǎn)為小寫Capitalize<StringType>:將首字母轉(zhuǎn)為大寫Uncapitalize<StringType>:將首字母轉(zhuǎn)為小寫
type EventName = 'click' | 'hover' | 'focus';
type UpperEventName = `${Uppercase<EventName>}`;
// "CLICK" | "HOVER" | "FOCUS"
type HandlerName = `on${Capitalize<EventName>}`;
// "onClick" | "onHover" | "onFocus"三、實戰(zhàn):CSS 屬性類型生成
讓我們通過一個實際場景來深入理解模板字面量類型的用法。假設(shè)我們需要為 CSS 屬性生成對應(yīng)的類型定義:
// 定義所有可能的 CSS 屬性前綴
type CssPropertyPrefix = '' | 'webkit' | 'moz' | 'ms' | 'o';
// 定義屬性名
type CssPropertyName =
| 'transform' | 'transition' | 'animation'
| 'flex' | 'grid';
// 生成完整的 CSS 屬性名
type CssProperty = `${CssPropertyPrefix}${Capitalize<CssPropertyName>}`;
// 結(jié)果:"" | "WebkitTransform" | "MozTransform" | "MsTransform" | "OTransform" | ...更復(fù)雜的例子,生成帶值的 CSS 屬性類型:
type CssValue = 'auto' | '0' | '100%' | '1px' | '2px';
type CssPropertyWithValue = `${CssProperty} ${CssValue}`;
// 例如:"transform auto" | "transform 0" | "WebkitTransform 100%" | ...四、模板字面量類型與infer
模板字面量類型最強大的用法是配合 infer 關(guān)鍵字進行模式匹配和類型提?。?/p>
4.1 提取字符串中的特定部分
// 提取事件名稱
type ExtractEventName<T> = T extends `on${infer Name}` ? Name : never;
type ClickEvent = ExtractEventName<'onClick'>; // "Click"
type HoverEvent = ExtractEventName<'onHover'>; // "Hover"
type CustomEvent = ExtractEventName<'onCustom'>; // "Custom"4.2 提取路徑中的文件名
type ExtractFileName<T> = T extends `${string}/${infer Name}` ? Name : T;
type FileName1 = ExtractFileName<'/src/components/Button.tsx'>; // "Button.tsx"
type FileName2 = ExtractFileName<'index.ts'>; // "index.ts"4.3 提取駝峰命名并轉(zhuǎn)換
type CamelToKebab<T extends string> =
T extends `${infer First}${infer Rest}`
? First extends Uppercase<First>
? `-${Lowercase<First>}${CamelToKebab<Rest>}`
: `${First}${CamelToKebab<Rest>}`
: T;
type KebabCase = CamelToKebab<'backgroundColor'>; // "background-color"
type KebabCase2 = CamelToKebab<'borderRadius'>; // "border-radius"4.4 提取并轉(zhuǎn)換 URL 參數(shù)
type ExtractQueryParams<T extends string> =
T extends `${string}?${infer Query}`
? Query extends `${infer Key}=${infer Value}&${infer Rest}`
? { [K in Key]: Value } & ExtractQueryParams<`?${Rest}`>
: Query extends `${infer Key}=${infer Value}`
? { [K in Key]: Value }
: {}
: {};
type Params = ExtractQueryParams<'/api/users?id=1&name=jack&age=25'>;
// { id: "1" } & { name: "jack" } & { age: "25" }五、模板字面量類型在React中的應(yīng)用
5.1 組件 Props 類型推導(dǎo)
type Size = 'small' | 'medium' | 'large';
type Color = 'primary' | 'secondary' | 'danger';
type ButtonVariant = `${Size}-${Color}`;
// "small-primary" | "small-secondary" | "small-danger" | ...
interface ButtonProps {
variant: ButtonVariant;
onClick: `handle${Capitalize<ButtonVariant>}`;
// 例如:handleSmallPrimary | handleSmallSecondary | ...
}
function createHandler(variant: ButtonVariant): ButtonProps['onClick'] {
return `handle${variant}` as ButtonProps['onClick'];
}5.2 狀態(tài)更新函數(shù)類型
type Setter<T extends string> = `set${Capitalize<T>}`;
interface State {
name: string;
age: number;
email: string;
}
type StateSetters = {
[K in keyof State as Setter<string & K>]: (value: State[K]) => void
};
// Setter<"name"> = "setName"
// 結(jié)果類型:{ setName: (value: string) => void; setAge: (value: number) => void; ... }5.3 路由參數(shù)類型
type RouteParams<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? { [K in Param]: string } & RouteParams<`/${Rest}`>
: T extends `${string}:${infer Param}`
? { [K in Param]: string }
: {};
type BlogRoute = RouteParams<'/blog/:id/:slug'>;
// { id: string } & { slug: string }
type UserRoute = RouteParams<'/users/:id'>;
// { id: string }六、模板字面量類型與映射類型
模板字面量類型可以與映射類型結(jié)合,批量生成相關(guān)的類型:
type APIResponse = {
code: number;
message: string;
data: unknown;
};
type APIAction = 'login' | 'register' | 'logout' | 'profile';
type APIEndpoint = `/${APIAction}`;
type APIResponses = {
[K in APIAction as `${K}${string}`]: APIResponse;
};
// 結(jié)果:
// {
// login: APIResponse;
// loginSuccess: APIResponse;
// loginFailed: APIResponse;
// register: APIResponse;
// ...
// }更實用的例子,生成表單驗證規(guī)則類型:
type ValidationRule = 'required' | 'minLength' | 'maxLength' | 'pattern';
type FieldName = 'email' | 'password' | 'username' | 'phone';
type ValidationConfig = {
[F in FieldName as `${F}${Capitalize<ValidationRule>}`]:
F extends 'email'
? { pattern: RegExp } | { required: true }
: F extends 'password'
? { minLength: number } | { required: true }
: { required: true } | { minLength: number; maxLength: number }
};
// emailRequired: { pattern: RegExp } | { required: true }
// passwordMinLength: { minLength: number } | { required: true }
// usernameRequired: { required: true } | { minLength: number; maxLength: number }七、模板字面量類型的性能考慮
模板字面量類型在編譯時計算,過度復(fù)雜的類型推導(dǎo)可能會影響 TypeScript 的編譯性能。以下是一些優(yōu)化建議:
首先,避免在模板字面量類型中使用過深的遞歸。深度遞歸的類型推導(dǎo)會顯著增加類型檢查的時間。
// 不推薦:深層遞歸
type DeepCamelToKebab<T extends string, Acc extends string = ''> =
T extends `${infer First}${infer Rest}`
? First extends Uppercase<First>
? DeepCamelToKebab<Rest, `${Acc}-${Lowercase<First>}`>
: DeepCamelToKebab<Rest, `${Acc}${First}`>
: Acc;
// 推薦:限制遞歸深度或使用條件類型簡化
type SimpleCamelToKebab<T extends string> =
T extends `${infer A}${infer B}`
? A extends Uppercase<A>
? `-${Lowercase<A>}${SimpleCamelToKebab<B>}`
: `${A}${SimpleCamelToKebab<B>}`
: T;其次,使用 extends 約束來縮小類型范圍,減少類型計算量:
// 不推薦:無約束的類型推導(dǎo)
type ExtractFromString<T> = T extends `${string}${infer Rest}` ? Rest : T;
// 推薦:添加約束
type ExtractFromString<T extends string> =
T extends `${string}${infer Rest}` ? Rest : T;第三,對于復(fù)雜的類型計算,考慮使用類型別名緩存結(jié)果:
// 緩存計算結(jié)果
type CachedKebabCase<T extends string> = KebabCaseMap[T];
// 預(yù)先計算映射
type KebabCaseMap = {
backgroundColor: 'background-color';
borderRadius: 'border-radius';
color: 'color';
// ... 更多預(yù)計算的類型
};八、模板字面量類型的調(diào)試技巧
復(fù)雜的模板字面量類型往往難以調(diào)試。以下是幾個實用的調(diào)試技巧:
第一個技巧是使用中間類型別名來分解復(fù)雜類型:
// 原始復(fù)雜類型
type ComplexType = `${Prefix}${Capitalize<Name>}${Suffix}`;
// 分解為多個中間類型
type Step1 = Capitalize<Name>;
type Step2 = `${Prefix}${Step1}`;
type Step3 = `${Step2}${Suffix}`;第二個技巧是使用條件類型產(chǎn)生錯誤信息:
// 通過錯誤信息查看實際類型 type Debug<T> = T extends string ? never : T; type Result = Debug<YourComplexType>; // 錯誤信息會顯示實際類型
第三個技巧是使用 typeof 和模板字面量類型結(jié)合:
const config = {
apiUrl: 'https://api.example.com',
version: 'v1',
} as const;
type Endpoint = `${typeof config.apiUrl}/${typeof config.version}/${string}`;
// "https://api.example.com/v1/${string}"九、常見問題與解決方案
9.1 模板字面量類型不生效
確保你使用的是 TypeScript 4.1 或更高版本。在較舊的 TypeScript 版本中,模板字面量類型可能不被支持。
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020"],
"typescriptVersion": ">=4.1.0"
}
}9.2 聯(lián)合類型在模板字面量中的行為
當(dāng)模板字面量類型中的某部分是一個聯(lián)合類型時,結(jié)果會是所有可能組合的聯(lián)合:
type A = 'x' | 'y';
type B = '1' | '2';
type C = `${A}${B}`;
// "x1" | "x2" | "y1" | "y2"9.3 大小寫轉(zhuǎn)換工具類型
TypeScript 的內(nèi)置大小寫轉(zhuǎn)換工具類型只對 ASCII 字符有效。如果你需要處理其他語言的字符,可能需要自定義實現(xiàn):
type UppercaseFirst<T extends string> =
T extends `${infer First}${infer Rest}`
? `${Uppercase<First>}${Rest}`
: T;
type LowercaseFirst<T extends string> =
T extends `${infer First}${infer Rest}`
? `${Lowercase<First>}${Rest}`
: T;十、總結(jié)
模板字面量類型是 TypeScript 類型系統(tǒng)中最強大的特性之一。它不僅可以讓你的類型定義更加精確和類型安全,還能在編譯時捕獲許多潛在的類型錯誤。通過掌握模板字面量類型,你可以寫出更加健壯、可維護的 TypeScript 代碼。
到此這篇關(guān)于TypeScript模板字面量類型高級用法的文章就介紹到這了,更多相關(guān)TS模板字面量類型高級用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript中iframe實現(xiàn)局部刷新的幾種方法匯總
Iframe是一種嵌入網(wǎng)頁的框架形式,Web頁面可以通過更改嵌入的部分,達到部分內(nèi)容刷新,通過本文和大家一起學(xué)習(xí)iframe實現(xiàn)局部刷新的幾種方法匯總,對iframe局部刷新相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧2016-01-01
layer.open回調(diào)獲取彈出層參數(shù)的實現(xiàn)方法
今天小編就為大家分享一篇layer.open回調(diào)獲取彈出層參數(shù)的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09
ES6(ECMAScript 6)新特性之模板字符串用法分析
這篇文章主要介紹了ES6(ECMAScript 6)新特性之模板字符串用法,簡單介紹了ES6模板字符串的概念、功能并結(jié)合實例形式分析了ES6模板字符串的用法,需要的朋友可以參考下2017-04-04
JavaScript實現(xiàn)為指定對象添加多個事件處理程序的方法
這篇文章主要介紹了JavaScript實現(xiàn)為指定對象添加多個事件處理程序的方法,可實現(xiàn)讓指定對象處理多個事件的功能,具有一定參考借鑒價值,需要的朋友可以參考下2015-04-04
原生Javascript封裝的一個AJAX函數(shù)分享
這篇文章主要介紹了原生Javascript封裝的一個AJAX函數(shù)分享,本文是實際項目中提取出來的,簡單易用,需要的朋友可以參考下2014-10-10

