搜尋

首頁  >  問答  >  主體

如何在TypeScript中定義一種類型,該類型是一個字串,只能包含預定義清單中的單字

我有一個棘手的 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粉139351297P粉139351297323 天前497

全部回覆(1)我來回復

  • P粉509383150

    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,並將AlreadyUsedClassName< /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 | undefinedstring 並將其傳遞給_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" />;

    遊樂場

    #

    回覆
    0
  • 取消回覆