繁体   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