我有一個棘手的 TypeScript 問題。
假設我有一個具有道具大小的圖示元件。大小可以是「2」、「4」、「6」。我將這些值映射到預先定義的順風類。
所以我這樣輸入
type SizeValues = '2' | '4' | '6'; function Icon({size = '4'}: {size: SizeValues}) { const sizeMap = { '2': 'w-2 h-2', '4': 'w-4 h-4', '6': 'w-6 h-6', }; return <span className={sizeMap[size]}>My icon goes here</span> } <Icon size="sm" />
一切都很好。但是,如果我想根據我的螢幕尺寸有不同的尺寸怎麼辦?所以我想嘗試擁有順風順水的良好語法。
因此,我將 Icon 元件重寫為以下內容:
type SizeValues = ??? function Icon({size = '4'}: {size: SizeValues}) { const sizeMap = { '2': 'w-2 h-2', '4': 'w-4 h-4', '6': 'w-6 h-6', 'md:2': 'md:w-2 md:h-2', 'md:4': 'md:w-4 md:h-4', 'md:6': 'md:w-6 md:h-6', 'lg:2': 'lg:w-2 lg:h-2', 'lg:4': 'lg:w-4 lg:h-4', 'lg:6': 'lg:w-6 lg:h-6', }; return <span className={size.split(' ').map(s => sizeMap[s]).join(' ').trim()}>My icon goes here</span> } <Icon size="2 md:4 lg:6" />
這很好用,但是我該如何輸入呢?我讀到 TypeScript 將來會支援正規表示式。這會讓事情變得更容易,但是現在可以輸入這個嗎?
這不是一個真正的組件,所以請不要給我如何改進它的好建議。我只是想知道如何輸入我的 size 屬性,以便它按照我編碼的方式工作。
P粉5093831502024-01-11 10:57:21
首先,我們需要將 sizeMap
提取到全域範圍內,並且 const assert 讓編譯器知道這是不可變常數並限制它擴大類型:
const sizeMap = { '2': 'w-2 h-2', '4': 'w-4 h-4', '6': 'w-6 h-6', 'md:2': 'md:w-2 md:h-2', 'md:4': 'md:w-4 md:h-4', 'md:6': 'md:w-6 md:h-6', 'lg:2': 'lg:w-2 lg:h-2', 'lg:4': 'lg:w-4 lg:h-4', 'lg:6': 'lg:w-6 lg:h-6', } as const;
接下來,我們需要取得 sizeMap
的鍵的類型:
type SizeMap = typeof sizeMap; type SizeMapKeys = keyof SizeMap;
實作:
我們將建立一個接受字串的類型,如果字串有效則傳回字串;否則,傳回never
。
虛擬程式碼:
讓類型接受T
- 要驗證的字串,Original
- 原始字串,AlreadyUsed
- 已使用鍵的並集。
如果T
是空字串
返回原始
否則,如果T
以大小映射(ClassName
) 的鍵開頭,不包括AlreadyUsed
,後跟一個空格和剩餘的字串(休息
)。
遞歸呼叫此類型,將Rest
作為字串傳遞以驗證Original
,並將AlreadyUsed
與ClassName< /code>#添加到其中。
Else if T
是尺寸映射的鍵,不包含 AlreadyUsed
原始
否則從不
實作:
type _SizeValue< T extends string, Original extends string = T, AlreadyUsed extends string = never > = T extends "" ? Original : T extends `${infer ClassName extends Exclude< SizeMapKeys, AlreadyUsed >} ${infer Rest extends string}` ? _SizeValue<Rest, Original, AlreadyUsed | ClassName> : T extends Exclude<SizeMapKeys, AlreadyUsed> ? Original : never;
我們必須在 Item
中新增一個通用參數來表示大小
。
function Icon<T extends string | undefined>({ size, }: { size: _SizeValue<T>; }) { return null; }
由於size
在元件中是可選的,因此我們將在SizeValue
周圍添加一個包裝器,它將把string | undefined
到string
並將其傳遞給_SizeValue
,此外我們將為大小添加一個預設值:
type SizeValue<T extends string | undefined> = _SizeValue<NonNullable<T>>; function Icon<T extends string | undefined>({ size = "2", }: { size?: SizeValue<T> | "2"; }) { return null; }
用法:
<Icon size="2" />; <Icon size="md:2" />; <Icon size="md:2 md:6" />; <Icon size="md:2 md:6 lg:6" />; // expected error <Icon size="md:2 md:6 lg:5" />; // no duplicates allowed <Icon size="2 2" />;#