typescript - Typescript如何使用接口的属性来约束函数参数
问题描述
大家好,我正在尝试找到一种方法来约束函数参数,因此它只需要将“字符串”约束到接口属性,就像我在函数验证字段中所做的那样:
注意:这只是使问题更简单的定义打字稿代码。
索引.d.ts
export interface FieldErrors {
errors: Array<FieldError>;
}
export type FieldsErrors<TForm> = {
[k in keyof TForm]: FieldErrors;
}
export interface ValidateFieldsCallback<TForm> { (fieldsErrors: FieldsErrors<TForm>, values: TForm): void; };
export interface FormShape<TForm> {
getFieldValue(fieldName: 'documentNumber' | 'userName'): void; // HOW TO FIX THIS
validateFields(callback: ValidateFieldsCallback<TForm>): void;
validateFields(fieldsNames: Array<string>, callback: ValidateFieldsCallback<TForm>): void;
}
例子.ts
interface SignupForm {
documentNumber: number;
userName: string;
}
const testForm = <FormShape<SignupForm>>{};
testForm.validateFields((values) => {
console.log(values.documentNumber); // OK
console.log(values.userName); // OK
console.log(values.other); // ERROR
});
// THIS SHOULD BE FIXED
const documentNumber = testForm.getFieldValue('documentNumber');
如您所见,我可以约束validateFields回调的参数fieldsErrors类型,但我需要修复函数getFieldValue以仅接受基于接口属性的“正确”字段名称,并且还返回正确的基于类型关于接口类型也不为空。
任何帮助将不胜感激。
解决方案
您keyof T
在映射类型中使用,但keyof T
它本身是一种类型,可以在接受类型的任何上下文中使用。keyof T
表示 type 的所有键的联合T
,这似乎正是您要寻找的。
要使返回类型与字段的类型相同,您需要添加一个类型参数getFieldValue
并使用类型查询来返回该字段的类型。
export interface FormShape<TForm> {
getFieldValue<K extends keyof TForm>(fieldName: K): TForm[K]; // Accepts only keys of TForm and returns the value of teh field
validateFields(callback: ValidateFieldsCallback<TForm>): void;
validateFields(fieldsNames: Array<string>, callback: ValidateFieldsCallback<TForm>): void;
}
关于阵列,可能有几种方法。如果您只想要简单的数组,您可以将 fieldname 设为数组。
export interface FormShape<TForm> {
getFieldValue<K extends keyof TForm>(...fieldName: K[]): TForm[K][] ;
validateFields(callback: ValidateFieldsCallback<TForm>): void;
validateFields(fieldsNames: Array<string>, callback: ValidateFieldsCallback<TForm>): void;
}
这确实有一个缺点,即您在每个索引上都失去了类型安全性,理想情况下,我们希望接收一个元组并返回一个元组。为此,我们可以添加几个重载:
export interface FormShape<TForm> {
getFieldValue<K extends keyof TForm, K1 extends keyof TForm, K2 extends keyof TForm>(fieldName: [K, K1, K2]): [TForm[K], TForm[K1], TForm[K2]];
getFieldValue<K extends keyof TForm, K1 extends keyof TForm>(fieldName: [K, K1]): [TForm[K], TForm[K1]];
getFieldValue<K extends keyof TForm>(fieldName: [K]): [TForm[K]];
validateFields(callback: ValidateFieldsCallback<TForm>): void;
validateFields(fieldsNames: Array<string>, callback: ValidateFieldsCallback<TForm>): void;
}
另外一个不错的选择是不返回数组,而是返回一个包含作为参数传入的键的对象:
export interface FormShape<TForm> {
getFieldValues<K extends keyof TForm>(...fieldName: K[]): { [P in K]: TForm[P] }; // Accepts only keys of TForm and returns the value of teh field
validateFields(callback: ValidateFieldsCallback<TForm>): void;
validateFields(fieldsNames: Array<string>, callback: ValidateFieldsCallback<TForm>): void;
}
//Usage
const documentNumber = testForm.getFieldValues('documentNumber', 'userName'); //{documentNumber: number;userName: string;}