簡體   English   中英

自動完成和參數推斷不能同時在具有 Typescript 泛型的映射類型上工作

[英]Autocompletion and Argument inference not working simultaneously on mapped types with Typescript generics

在下面的代碼( Playground )中,我嘗試同時在personTypeX({...})進行自動完成函數簽名推斷,但我只能實現其中一個,但不能同時實現兩者。

甚至可能嗎?

以下是要求:

  • 如果一個人(= 輸入)有一個firstName ,那么它必須是一個string
  • 如果一個人有一個age ,那么它一定是一個number
  • ...
  • 如果一個人的屬性既不是firstNamelastNameagefavouriteFood也不是getLegCount ,那么該屬性的值本身必須是Person

總共有 3 項附加要求

  • 必須檢測無效輸入(在輸入的任何級別)並在正確的位置標記。 見下文:與personType1()personType2()一起使用
  • 必須在personTypeX({})中提供firstName 、...、 getLegCount中所有鍵的自動完成功能,見下文:目前僅適用於personType2({})
  • 如果鍵(這里只有getLegCount )強制它的值是一個函數,那么函數簽名必須正確推斷如下:目前僅適用於personType1({})
type FixedType = {
  firstName: string,
  lastName: string,
  age: number,
  favouriteFood: string[],
  getLegCount: (numHeads: number) => number,
};

type PersonType<T> = {
  [K in (keyof T | keyof FixedType)]?:
    K extends keyof FixedType
      ? FixedType[K]
      : K extends keyof T
        ? T[K] extends object
          ? PersonType<T[K]>
          : PersonType<{}>
        : never
}

// ---- ---- ---- ---- ----

function personType1<T extends PersonType<T>>(t: T) { // signature version 1
  return t;
}

const person11 = personType1({
  firstName: 'Mark',
  lastName: 'Antony',
  holger: {
    age: 12,
    paul: {
      // <-- type 'a' for autocompletion of 'age' => NOT WORKING
      favouriteFood: ['cheese'],
      firstName: 'Paul',
      getLegCount: (numHeads) => 2, // <-- hover over 'numHeads' => numHeads: number correctly inferred => WORKING
    }
  },
});

// ----

function personType2<T extends PersonType<T>>(t: T & PersonType<T>) { // signature version 2
  return t;
}

const person1 = personType2({
  firstName: 'Mark',
  lastName: 'Antony',
  holger: {
    age: 12,
    paul: {
      // <-- type 'a' for autocompletion of 'age' => WORKING
      favouriteFood: ['cheese'],
      firstName: 'Paul',
      getLegCount: (numHeads) => 2, // <-- hover over 'numHeads' => numHeads: any inferred => NOT WORKING
                                    // that's probably bc it's the most common signature for
                                    //   (property) getLegCount: ((numHeads: any) => number) & ((numHeads: number) => number)
                                    // but I have no clue how to fix this
    }
  },
});

簡而言之,您所要求的目前是不可能的(據我所知)。

這里有很多循環邏輯,這是 TS 的限制(在管理/理解此類代碼方面只是對人類的侮辱)。 我們在泛型中使用泛型有效地規避了它,但我們仍然受到它的限制。 即,據我了解,我們只能選擇一個:

  • 檢索參數的數據類型,驗證並使其成為參數的數據類型( IE.autocompletion
  • 驗證參數是否合適,對自身推斷類型進行轉換( getLegCount: (numHeads) => 2 ),使其成為參數的數據類型(IE.argument inferencing on non-explicit types

這方面的一個例子是當我們回到泛型是如何制作的

function example<T>(t: T)

在這里,我們可以明確定義T以規定t遵循,或者我們可以從t推斷T

當我們做這樣的事情時:

function personType<T extends PersonTypeNew<T>>(t: T){...}

我們正在制作循環邏輯,並且在某些時候 TS 必須選擇Tt (即使它們相互依賴,也會產生不同的結果)。

  • 如果我們推斷參數(並因此鍵入getLegCount: (numHeads) ,我們推斷age什么都不是(因為它不在參數中),並且不需要為age (或任何其他鍵)提供自動完成功能。
  • 否則,默認行為提供自動完成

^ 這在技術上是一個超級超級簡化。 這實際上在簡單類型對象(即非循環)中得到支持,具有稱為部分推理的東西,這將允許自動完成和參數推理。

如果這令人困惑,可以在操場上提供一些示例。



無論如何,我從頭開始重寫它。 支持自動完成和參數推理,但如果后者不存在,則僅支持前者。

type PersonTypeNew<T> = {
  // We have to overwrite any keys, otherwise we could incorrectly infer.
  // Hence Omit<T, keyof FixedType>, we use & Partial<FixedType> for correct autocompletion
  [K in keyof (Omit<T, keyof FixedType> & Partial<FixedType>)]: 
    K extends keyof FixedType
      ? FixedType[K]
      : (Omit<T, keyof FixedType> & Partial<FixedType>)[K] extends Record<string, any>
        ? PersonTypeNew<(Omit<T, keyof FixedType> & Partial<FixedType>)[K]>
        : "Invalid Value!"
}

這還會在第一層添加自動完成(當您不進行推理時)。

沒有自動補全支持會更簡潔(因為我們只是在不交叉兩個對象類型的情況下進行驗證)

type PersonTypeNewNoAutocomplete<T> = {
  [K in keyof T]?: 
    K extends keyof FixedType
      ? T[K] extends FixedType[K]
        ? T[K]
        : FixedType[K]
      : PersonTypeNewNoAutocomplete<T[K]>
}

查看 Playground 示例

有一些未解決的問題在討論這個問題,這將進一步詳細介紹上下文引擎、優先級、循環類型的其他相關主題:

檢查一下,它通過使用Partial<FixedType>來自動完成, K extends keyof FixedType ? FixedType[K] K extends keyof FixedType ? FixedType[K]用於錯誤檢查和嵌套對象的遞歸泛型類型。

操場

代碼:

type TypeCheckedInferedPerson<T> = Partial<FixedType> & PersonType<T>

type PersonType<T extends Partial<FixedType>> = {
  [K in keyof T]: K extends keyof FixedType ? FixedType[K] : TypeCheckedInferedPerson<T[K]> 
}

function personType2<T extends PersonType<T>>(t: TypeCheckedInferedPerson<T>) { 
  return t;
}

結果: 結果

暫無
暫無

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

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