簡體   English   中英

如何在不刪除 TypeScript 中的子類型的情況下從聯合類型中刪除更廣泛的類型?

[英]How can I remove a wider type from a union type without removing its subtypes in TypeScript?

使用 Exclude 運算符不起作用。

type test = Exclude<'a'|'b'|string, string>
// produces type test = never

我可以理解為什么“除了字符串”也意味着排除所有字符串文字,但是如何從'a'|'b'|string獲取'a'|'b'

如果需要,請使用最新的 TypeScript。

用例如下:

假設第三方庫定義了這種類型:

export interface JSONSchema4 {
  id?: string
  $ref?: string
  $schema?: string
  title?: string
  description?: string
  default?: JSONSchema4Type
  multipleOf?: number
  maximum?: number
  exclusiveMaximum?: boolean
  minimum?: number
  exclusiveMinimum?: boolean
  maxLength?: number
  minLength?: number
  pattern?: string
  // to allow third party extensions
  [k: string]: any
}

現在,我想要做的是獲得 KNOWN 屬性的聯合:

type KnownProperties = Exclude<keyof JSONSchema4, string|number>

可以理解的是,這會失敗並給出一個空類型。

如果您正在閱讀這篇文章,但我被一輛公共汽車撞到了,那么這個問題的答案可能會在這個 GitHub 線程 中找到。

我在這個 GitHub 線程中@ferdaber那里得到了一個解決方案。

編輯:事實證明,它是由@ajafff 於 1986 年出版的

該解決方案需要 TypeScript 2.8 的條件類型,如下所示:

type KnownKeys<T> = {
  [K in keyof T]: string extends K ? never : number extends K ? never : K
} extends { [_ in keyof T]: infer U } ? U : never;

以下是我的解釋嘗試:

該解決方案基於string擴展string (就像'a' extends string )但string不擴展'a'的事實,對於數字也是如此。 基本上,我們必須將extends視為“進入”

首先它創建一個映射類型,其中對於 T 的每個鍵,值是:

  • 如果字符串擴展鍵(鍵是字符串,不是子類型)=> 從不
  • 如果數字擴展鍵(鍵是數字,不是子類型)=> 從不
  • 否則,實際的字符串鍵

然后,它本質上是 valueof 來獲得所有值的聯合:

type ValuesOf<T> = T extends { [_ in keyof T]: infer U } ? U : never

或者,更准確地說:

interface test {
  req: string
  opt?: string
  [k: string]: any
}
type FirstHalf<T> = {
  [K in keyof T]: string extends K ? never : number extends K ? never : K
}

type ValuesOf<T> = T extends { [_ in keyof T]: infer U } ? U : never
// or equivalently, since T here, and T in FirstHalf have the same keys,
// we can use T from FirstHalf instead:
type SecondHalf<First, T> = First extends { [_ in keyof T]: infer U } ? U : never;

type a = FirstHalf<test>
//Output:
type a = {
    [x: string]: never;
    req: "req";
    opt?: "opt" | undefined;
}
type a2 = ValuesOf<a> //  "req" | "opt" // Success!
type a2b = SecondHalf<a, test> //  "req" | "opt" // Success!

// Substituting, to create a single type definition, we get @ferdaber's solution:
type KnownKeys<T> = {
  [K in keyof T]: string extends K ? never : number extends K ? never : K
} extends { [_ in keyof T]: infer U } ? U : never;
// type b = KnownKeys<test> //  "req" | "opt" // Absolutely glorious!

GitHub 線程中的說明,以防有人在那里提出異議

每個接受的答案: https : //stackoverflow.com/a/51955852/714179 在 TS 4.3.2 這有效:

export type KnownKeys<T> = keyof {
  [K in keyof T as string extends K ? never : number extends K ? never : K]: never
}

暫無
暫無

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

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