簡體   English   中英

Typescript 復雜類型推斷

[英]Typescript complex type inference

我想為 Rule3 做一個更詳細的類型推斷

export interface BaseOption {
  required?:true;
}

export type Validator = {
  [key in string]?: (
    val: number,
    formData: string,
    callback,
    props
  ) => void | Promise<void | Error>;
};

export interface RequiredFunc {
  required: (message: string) => void;
}

export type CombineOptions<ExtraOption extends Validator> = ExtraOption &
  RequiredFunc;

function Rule<ExtraOption extends Validator>(
  options: ExtraOption | BaseOption
) {
  return {} as ExtraOption extends BaseOption
    ? CombineOptions<ExtraOption>
    : ExtraOption;
}

// Condition1
const rule1 = Rule({
  a() {},
});

rule1.a; // There are code hints in vscode

// Condition2
const rule2 = Rule({ required: true });

rule2.required; // There are code hints in vscode

// Condition3
const rule3 = Rule({
  a(f, b) {},
  required: true,
});

rule3.required; // There are code hints in vscode
rule3.a; // There are no code hints in vscode

如何獲得條件 3 的代碼提示。如果我不手動添加 generics,則條件 3 在 vscode 中不起作用。 我的類型推斷是錯誤的方式嗎?

一般來說,如果您希望編譯器推斷類型參數X ,您應該給它一個X類型的值來推斷它。 在您的Rule function 中,您試圖從 X 類型的options值推斷X extends Validator X | BaseOption X | BaseOption ,結果不是很好。 一旦options可分配給BaseOption ,編譯器就放棄了更具體地推斷X並退回到Validator ,導致rule2rule3的類型為CombineOptions<Validator> ,這是正確的,但不足以滿足您的需求。

如果我們重構使得X本身是一個擴展Validator | BaseOption的類型 Validator | BaseOption並且optionsX類型,那么推理將正常工作並且編譯器不會忘記options的特定屬性:

function Rule<X extends Validator | BaseOption>(options: X): RuleOutput<X> {
    return {} as any // impl
}

現在的問題是弄清楚如何將 output 類型RuleOutput<X>表示為輸入類型X的 function 。 通過閱讀您的RequiredFuncCombineOptions類型,看起來您想要做的是 output X或多或少原樣,除非有一個名為required的鍵,您希望將該屬性類型更改為(message: string) => void 這可以通過帶有條件類型映射類型來表示,以檢查需要密鑰的required

type RuleOutput<X> = {
    [K in keyof X]:
    K extends "required" ? (message: string) => void : X[K]
}

讓我們看看它是否有效:

// Condition1
const rule1 = Rule({
    a() { },
});
/* const rule1: {
    a: () => void;
} */

// Condition2
const rule2 = Rule({ required: true });
/* const rule2: {
    required: (message: string) => void;
} */
    
// Condition3
const rule3 = Rule({
    a(f, b) { },
    required: true,
});
/* const rule3: {
    a: (f: number, b: string) => void;
    required: (message: string) => void;
} */

看起來不錯。 您的rule1與之前的類型相同,現在rule2rule3更具體,並且知道作為options傳入的特定鍵。

Playground 代碼鏈接

暫無
暫無

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

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