簡體   English   中英

如何在 TypeScript 中創建 RequireOnlyOne 嵌套類型?

[英]How to create a RequireOnlyOne nested type in TypeScript?

我經常看到這個代碼片段:

type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
    [K in Keys]-?:
        Required<Pick<T, K>>
        & Partial<Record<Exclude<Keys, K>, undefined>>
}[Keys]

在這里你可以找到我提出的問題。

它有效,但是,我有這樣的結構:

export interface MenuItems {
  firstLevel: {
    secondLevel: ['one', 'two']
  };
  anotherFirstLevel: {
    anotherSecondLevel: ['one', 'two'],
    oneMoreSecondLevel: null
  };
}

我需要為第一級和第二級應用RequireOnlyOne ,但我無法確定RequireOnlyOne類型的更改內容,因此它適用於每個 firstLevel 鍵,但也適用於 secondLevel 鍵。 就像現在一樣,我只能選擇一個 firstLevel,但可以選擇該 firstLevel 的多個 secondLevel。

我還嘗試用一個對象組合一個新類型,該對象的鍵可以是RequireOnlyOne<keyof MenuItems>和一個也使用RequireOnlyOne作為值的值,但無法實現。

我想要的示例,將所需類型稱為customType

const workingObject: customType = {
  firstLevel: { // Just one property of the first level
    secondLevel: ['one', 'two'] // Just one property of the second level
  };
}

const errorObject: customType = {
  firstLevel: {
    secondLevel: ['one', 'two']
  };
  anotherFirstLevel: { // Should not work as I am including 2 properties for the first level
    anotherSecondLevel: ['one', 'two']
  };
}

const anotherErrorObject: customType = {
  anotherFirstLevel: {
    anotherSecondLevel: ['one', 'two'],
    oneMoreSecondLevel: null // Should not work neither as I am including 2 properties for second level
  };
}

如果對象具有多個第一級屬性和/或多個第二級屬性,則該類型應引發錯誤。 使用建議的RequireOnlyOne類型,我可以實現這一點,但僅適用於第一級,但我需要第一級第二級的相同效果。

有任何想法嗎?

IMO 這是一個非常復雜的問題,我建議尋找替代方案並重構代碼。

否則,您可能會發現這很有幫助:

type RequireOnlyOneUnion<T, Keys extends KeysOfUnion<T> = KeysOfUnion<T>> = Pick<T, Exclude<keyof T, Keys>> &
  {
    [K in Keys]-?: Required<Pick<T, K>> & Partial<Record<Exclude<Keys, K>, undefined>>;
  }[Keys];

export interface MenuItems {
  firstLevel: {
    secondLevel: ["one", "two"];
  };
  anotherFirstLevel: {
    anotherSecondLevel: ["one", "two"];
    oneMoreSecondLevel: null;
  };
}

type KeysOfUnion<T> = T extends T ? keyof T : never;

// ok
const a: RequireOnlyOneUnion<MenuItems[keyof typeof x], KeysOfUnion<MenuItems[keyof typeof x]>> = {
  anotherSecondLevel: ["one", "two"],
};
// error
const b: RequireOnlyOneUnion<MenuItems[keyof typeof x], KeysOfUnion<MenuItems[keyof typeof x]>> = {
  secondLevel: ["one", "two"],
  anotherSecondLevel: ["one", "two"],
};

我不知道如何更改RequireOnlyOne類型,但我知道如何創建新類型。

export interface MenuItems {
  firstLevel: {
    secondLevel: ['one', 'two']
  };
  anotherFirstLevel: {
    anotherSecondLevel: ['one', 'two']
    oneMoreSecondLevel: null
  };
}

type Primitives = string | number | boolean | null | undefined | bigint | symbol

type UnionKeys<T> = T extends T ? keyof T : never;
// credits goes to https://stackoverflow.com/questions/65805600/type-union-not-checking-for-excess-properties#answer-65805753
type StrictUnionHelper<T, TAll> =
  T extends any
  ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;

type StrictUnion<T> = StrictUnionHelper<T, T>

type Transform<Obj, Keys extends keyof Obj = keyof Obj, Result = never> =
  StrictUnion<
    Keys extends string ? { // #1
      [Key in Keys]:
      Key extends keyof Obj
      ? (Obj[Key] extends Primitives
        ? Obj[Key]
        : (Obj[Key] extends any[]
          ? Obj[Key]
          : Transform<Obj[Key], keyof Obj[Key], Obj[Key]>)
      )
      : never
    } : Result>


type CustomType = Transform<MenuItems>

const workingObject: CustomType = {
  firstLevel: { // Just one property of the first level
    secondLevel: ['one', 'two'] // Just one property of the second level
  },
}


const errorObject: CustomType = {
  firstLevel: {
    secondLevel: ['one', 'two']
  },
  anotherFirstLevel: { // Should not work as I am including 2 properties for the first level
    anotherSecondLevel: ['one', 'two']
  },
}

const anotherErrorObject: CustomType= {
  anotherFirstLevel: {
    anotherSecondLevel: ['one', 'two'],
    oneMoreSecondLevel: null // Should not work neither as I am including 2 properties for second level
  },
}

Transform - 是一種主要的實用程序類型。 遞歸迭代鍵。 1# Keys extends string - 這一行確保Keys is distributet 這意味着該行之后的整個代碼將應用於每個鍵。 請參閱文檔了解更多信息。

我還添加了Obj[Key] extends any[] - 因為你不想(我想)遍歷數組鍵。

操場

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM