首页 文章

Typescript如何使用接口的属性约束函数参数

提问于
浏览
1

大家好我想找到一种约束函数参数的方法,所以它只需要将“字符串”约束到接口属性,就像我在函数验证字段中所做的那样:

Note: 这只是定义打字稿代码,使问题更简单 .

index.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;
}

example.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 以仅接受基于接口属性的"correct"字段名称,并且还返回基于界面类型也无效 .

任何帮助将非常感激 .

1 回答

  • 3

    您在映射类型中使用 keyof T ,但 keyof T 本身就是一种类型,可以在接受类型的任何上下文中使用 . keyof T 表示 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;}
    

相关问题