考虑函数 f,其定义如下:
function f<T extends Fields = Fields>(props: Props<T>) {
return null;
}
在该函数中,T代表扩展Fields的泛型类型,Fields定义为:
export type Fields = { [key: string]: unknown };
此外,Props接口定义为:
export interface Props<T extends Fields = Fields> {
fields: Config<T>;
onSubmit?: (values: Values<T>) => void;
}
这里,Props 接受扩展 Fields 的泛型类型 T,它由两个属性组成:fields 和 onSubmit。 fields 属性的类型为 Config
为了提供更多上下文,Config 和 Values 定义如下:
type BaseProps<T> = {
initialValue: T;
hidden?: boolean;
};
export interface TextInput extends BaseProps<string>, TextInputProps {
type: 'text';
}
export interface Checkbox extends BaseProps<boolean> {
type: 'checkbox';
}
type Config<T> = { [K in keyof T]: TextInput | Checkbox };
export type Values<T extends Fields> = {
[K in keyof T]: Config<T>[K]['initialValue'];
};
此处,Config
总而言之,函数 f 需要 Props 类型的 props,其中包含有关表单字段(fields)的信息和一个可选的提交函数(onSubmit)。 fields 属性是使用映射类型 (Config
这里的核心问题是是否有一种方法可以让 Values 自动推断出正确的类型。目前,values.age 类型被推断为
string | boolean
。这种歧义源于 Config 中使用“或”运算符,它允许使用 TextInput 或 Checkbox。
人们关注的不仅仅是技术方面,还包括设计含义。是否存在结构或架构调整可以导致更精确的类型推断?或者这种模糊性是设计中固有的,因此可以接受吗?
本质上,我们正在探索是否有一种方法可以细化类型推断机制,以准确确定values.age的类型。
f({
fields: {
name: {
type: 'text',
initialValue: 'John Doe',
},
age: {
initialValue: true,
type: 'checkbox',
},
},
onSubmit: (values) => {
console.log(values.age);
},
});
我会在这里提供全部内容,方便复制。
type BaseProps<T> = {
initialValue: T;
hidden?: boolean;
};
export interface TextInput extends BaseProps<string> {
type: 'text';
}
export interface Checkbox extends BaseProps<boolean> {
type: 'checkbox';
}
type Config<T> = { [K in keyof T]: TextInput | Checkbox };
export type Fields = { [key: string]: unknown };
export type Values<T extends Fields> = {
[K in keyof T]: Config<T>[K]['initialValue'];
};
export interface Props<T extends Fields = Fields> {
fields: Config<T>;
onSubmit?: (values: Values<T>) => void;
}
function f<T extends Fields = Fields>(props: Props<T>) {
return null;
}
f({
fields: {
name: {
type: 'text',
initialValue: 'John Doe',
},
age: {
initialValue: true,
type: 'checkbox',
},
},
onSubmit: (values) => {
console.log(values.age);
},
});
我尝试了多种方法来推断这一点,但不幸的是,没有一个被证明是成功的。
我发现从头开始更容易:
type Controls = {
checkbox: boolean,
text: string,
}
type Field = {[C in keyof Controls]: {
type: C,
initialValue: Controls[C],
}}[keyof Controls];
function f<F extends {[K in string]: Field}>(props: {
fields: F;
onSubmit: (values: {[K in keyof F]: Controls[F[K]['type']]}) => void;
}) {
}
如果你这样称呼它:
f({
fields: {
name: {
type: 'text',
initialValue: 'John Doe',
},
age: {
type: 'checkbox',
initialValue: true,
},
},
onSubmit: (values) => {
},
});
编译器检查:
type
是已知的控制类型,initialValue
并推断
values
是类型
(parameter) values: {
name: string;
age: boolean;
}
特别注意,值的推断仅查看控件的类型,而不是
initialValue
的类型:我们不希望 age
仅仅因为 true
就推断为 initialValue
已知为 true
。