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.If 中的代码提示 我没有手动添加泛型,条件 3 在 vscode 中不起作用。我的类型推断方式有误吗?
一般来说,如果您希望编译器推断类型参数 X
,您应该给它一个类型 X
的值,以从中推断它。在您的 Rule
函数中,您试图从 X | BaseOption
类型的 options
值推断出 X extends Validator
,但结果并不理想。一旦 options
可分配给 BaseOption
,编译器就放弃更具体地推断 X
,并退回到 Validator
,导致 rule2
和 rule3
为 CombineOptions<Validator>
类型,这是正确的,但不够具体,无法满足您的需求。
如果我们重构 X
本身是扩展 Validator | BaseOption
的类型并且 options
属于 X
类型,那么推理将正常工作并且编译器不会忘记options
:
的具体属性
function Rule<X extends Validator | BaseOption>(options: X): RuleOutput<X> {
return {} as any // impl
}
现在的问题是弄清楚如何将输出类型 RuleOutput<X>
表示为输入类型 X
的函数。通过阅读您的 RequiredFunc
和 CombineOptions
类型,看起来您想要做的是或多或少地输出 X
as-is,除非有一个名为 [=36] 的键=],您想将 属性 类型更改为 (message: string) => void
。这可以通过 mapped type with a conditoinal type 来表示,以检查密钥为 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
与以前的类型相同,现在 rule2
和 rule3
更具体并且知道作为 options
.
传入的特定键
我想为 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.If 中的代码提示 我没有手动添加泛型,条件 3 在 vscode 中不起作用。我的类型推断方式有误吗?
一般来说,如果您希望编译器推断类型参数 X
,您应该给它一个类型 X
的值,以从中推断它。在您的 Rule
函数中,您试图从 X | BaseOption
类型的 options
值推断出 X extends Validator
,但结果并不理想。一旦 options
可分配给 BaseOption
,编译器就放弃更具体地推断 X
,并退回到 Validator
,导致 rule2
和 rule3
为 CombineOptions<Validator>
类型,这是正确的,但不够具体,无法满足您的需求。
如果我们重构 X
本身是扩展 Validator | BaseOption
的类型并且 options
属于 X
类型,那么推理将正常工作并且编译器不会忘记options
:
function Rule<X extends Validator | BaseOption>(options: X): RuleOutput<X> {
return {} as any // impl
}
现在的问题是弄清楚如何将输出类型 RuleOutput<X>
表示为输入类型 X
的函数。通过阅读您的 RequiredFunc
和 CombineOptions
类型,看起来您想要做的是或多或少地输出 X
as-is,除非有一个名为 [=36] 的键=],您想将 属性 类型更改为 (message: string) => void
。这可以通过 mapped type with a conditoinal type 来表示,以检查密钥为 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
与以前的类型相同,现在 rule2
和 rule3
更具体并且知道作为 options
.