【问题标题】:Typescript how to constrain function parameter with the properties of an interfaceTypescript如何使用接口的属性来约束函数参数
【发布时间】:2018-12-29 08:25:59
【问题描述】:

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

注意:这只是为了让问题更简单的定义打字稿代码。

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 仅接受基于接口属性的“正确”字段名称,并根据接口类型返回正确的类型,也不为 void。

任何帮助将不胜感激。

【问题讨论】:

  • 请贴代码而不是图片!
  • @TitianCernicova-Dragomir 完成! :)
  • 仅供将来参考,如果将其简化为更小的示例会很好;示例中的许多代码与所提出的问题并不真正相关。

标签: typescript ecmascript-6


【解决方案1】:

您在映射类型中使用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;}

【讨论】:

  • 好吧,但是如何将返回类型 VOID 更改为与界面上的属性相同?
  • @DavidNoreña Wops,错过了,现在是早上 6 点在我的脖子上的树林咖啡还没有开始 :) 修复
  • 早上 6 点,但你仍然摇滚!太棒了,非常感谢!
  • 如果我想添加一个 getFieldValues(fieldsNames) 可以是一个数组怎么办?
  • @DavidNoreña 我很快就会添加一个数组版本
猜你喜欢
  • 2021-07-04
  • 1970-01-01
  • 1970-01-01
  • 2015-12-20
  • 2015-12-29
  • 2022-10-21
  • 1970-01-01
  • 2021-02-03
  • 1970-01-01
相关资源
最近更新 更多