【问题标题】:Object literal which implements an interface which in turn uses a generic method实现接口的对象文字,该接口又使用通用方法
【发布时间】:2019-12-21 09:42:09
【问题描述】:

我有以下自定义类型和接口:

export type MultiMedia = 'image' | 'audio' | 'video';
export type FieldType = 'string' | 'number' | 'boolean' | MultiMedia;

export interface Field {
  name: string,
  label: string,
  type: FieldType,
  validator: <T>(val: T) => boolean,
  bounds: { lower: number; upper: number }
}

export interface Theme {
  title: string,
  logoPath: string,
  tags: string[],
  fields: Field[]
}

由于字段类型各不相同,我已将validator 定义为Field 接口中的通用方法。当我想创建一个实现 Field 接口打字稿抱怨的对象文字时

类型 '(val: string) => boolean' 不可分配给类型 '(val: T) => boolean。

const fields: Field[] = [
  {
    name: "firstName",
    label: "First Name",
    type: "string",
    bounds: { lower: 1, upper: 1 },
    validator: (val: string) => {
     return val.length > 20;
    }
  }

我可以改变Field界面如下解决问题。

export interface Field<T> {
  name: string,
  label: string,
  type: FieldType,
  validator: (val: T) => boolean,
  bounds: { lower: number; upper: number }
}

然后打字稿抱怨Theme接口中的fields属性。

通用类型“字段”需要 1 个类型参数。

【问题讨论】:

  • 如果type"audio",你想让validator 接受什么?

标签: typescript typescript2.0


【解决方案1】:

我认为这里的问题是 Field 基本上是 union,因为您希望它是 要么 string 字段, @ 987654327@ 字段等。我这样做的方法是创建一个通用的SpecificField&lt;F&gt; 接口,表示从每个字段类型F 到其验证器的约束,然后将Field 设置为联合的类型别名所有SpecificField&lt;F&gt; 类型:

首先让我们定义字段映射,以便我们知道验证器应该接受什么

interface FieldMapping {
  image: HTMLImageElement; // guessing
  audio: HTMLAudioElement; // guessing
  video: HTMLVideoElement; // guessing
  string: string;
  number: number;
  boolean: boolean;
}

现在是SpecificField&lt;F&gt; 接口:

export interface SpecificField<F extends FieldType> {
  name: string;
  label: string;
  type: F;
  validator: (val: FieldMapping[F]) => boolean;
  bounds: { lower: number; upper: number };
}

您可以看到SpecificField&lt;string&gt; 将如何拥有type"string" 和一个仅接受string 值的validator。然后我们让Field 成为一个联合,可以这样发生:

type Field = { [F in FieldType]: SpecificField<F> }[FieldType];

这使用mapped type 将每个字段作为属性获取,然后通过looking up 获取所有属性的并集。您可以验证它的计算结果为:

type Field = SpecificField<"string"> | SpecificField<"number"> |
  SpecificField<"boolean"> | SpecificField<"image"> | SpecificField<"audio"> | 
  SpecificField<"video">

然后您可以验证以下是否有效:

const fields: Field[] = [
  {
    name: "firstName",
    label: "First Name",
    type: "string",
    bounds: { lower: 1, upper: 1 },
    validator: (val: string) => {
      return val.length > 20;
    }
  }
];

好的,希望对您有所帮助;祝你好运!

Link to code

【讨论】:

  • 这正是我所需要的。感谢您花时间提供如此详细的答案。
【解决方案2】:

你的最后一个例子可以工作,除了当你使用Field接口时需要声明T的类型(在Theme接口声明和常量fields声明中),而不是当@ 987654325@ 被声明:

export type MultiMedia = 'image' | 'audio' | 'video';
export type FieldType = 'string' | 'number' | 'boolean' | MultiMedia;

export interface Field<T> {
  name: string,
  label: string,
  type: FieldType,
  validator: (val: T) => boolean,
  bounds: { lower: number; upper: number }
}

export interface Theme {
  title: string,
  logoPath: string,
  tags: string[],
  fields: Field<string>[]
}

const fields: Field<string>[] = [
  {
    name: "firstName",
    label: "First Name",
    type: "string",
    bounds: { lower: 1, upper: 1 },
    validator: (val: string) => {
     return val.length > 20;
    }
  }
]

编辑:

另外,如果您想将T 的类型限制为FieldType 之一,那么您可以这样做:

export interface Field<T extends FieldType> {
  name: string,
  label: string,
  type: FieldType,
  validator: (val: T) => boolean,
  bounds: { lower: number; upper: number }
}

【讨论】:

  • 感谢您与我们联系。这样,所有主题字段的类型都是string。正如我之前所说,fields 可以是不同的类型。
  • 没问题!只是想帮忙。那么是否假设传递给验证器的值与type 的类型相同?还是验证器的参数类型完全独立于Field的type属性
  • type 值与 validator 输入的类型无关。实际上validator 参数值可以是stringnumberboolean。这就是为什么我试图将类型作为生成器传递。
  • 如果是这样,那么您可以完全忘记泛型,只需将参数val 声明为类型FieldType
  • T 类型在原始示例中的任何地方都没有使用,除了声明参数的类型。通常泛型用于多个位置以表示多个值属于同一类型。
猜你喜欢
  • 1970-01-01
  • 2010-11-23
  • 1970-01-01
  • 1970-01-01
  • 2013-08-16
  • 2013-08-25
  • 2016-02-01
  • 1970-01-01
  • 2015-05-13
相关资源
最近更新 更多