简体   繁体   English

带有多个动态键的 Typescript 接口

[英]Typescript Interface With Multiple Dynamic Keys

I have an application that queries a MongoDB database, but is currently only capable of querying a single field in the document:我有一个查询 MongoDB 数据库的应用程序,但目前只能查询文档中的单个字段:

export interface IKeyValue {
    [property : string] : any ;
}

export interface IFilter {
    [property : string] : IKeyValue;
}

const filter : IFilter = {
    "Name" : { $eq : "Bob" }
}

What I want to do is allow IFilter to allow more than one property:我想要做的是允许IFilter允许多个属性:

const filter : IFilter = {
    "Name" : { $eq : "Bob" },
    "Age"  : { $gte : 21 }
}

How can I create a Typescript interface that will allow more than one dynamic key, without reverting to using object or any as the type for IFilter ?如何创建一个允许多个动态键的 Typescript 接口,而无需恢复使用objectany作为IFilter的类型?

How I understand your need is:我如何理解您的需求是:

  • you have some entity, lets say User你有一些实体,比方说用户
  • you want to create a generic Filter type which can be used for different entities你想创建一个通用的 Filter 类型,它可以用于不同的实体
  • generic Filter type should be type safe for every entity通用过滤器类型对于每个实体都应该是类型安全的

First of all lets define possible rules we need to filter首先让我们定义我们需要过滤的可能规则

type Rule<T> = { $eq: T } | { $gte: T } // append any other rules you need

I have taken from your example two rules - $eq and $gte but by |我从您的示例中获取了两个规则 - $eq$gte ,但是通过| you can add others you need.你可以添加其他你需要的。

Second lets define generic Filter type其次让我们定义通用Filter类型

type Filter<Obj> = {
    [K in keyof Obj]: Rule<Obj[K]>
}

Our type says - I should have all keys of given object, and for every key I should define a rule which works on the same type as this property has.我们的类型说 - 我应该拥有给定对象的所有键,并且我应该为每个键定义一个规则,该规则适用于与该属性具有的相同类型。 So for property a: string the rule needs to be or {$eq: string} or {$gte: string} .因此,对于属性a: string ,规则需要是或{$eq: string}{$gte: string}

Lets see an example:让我们看一个例子:

// example filter for the User type
type User = {
    id: number;
    name: string;
}

// create an instance of the filter for the User entity
const userFilter: Filter<User> = {
    id: { $gte: 1 }, // rule needs to have value as number
    name: {$eq: '2'} // rule needs to have value as string
}

// what if we want to filter by only part of the interface - use Pick utility type
const userFilter2: Filter<Pick<User, 'name'>> = {
    name: {$eq: '2'} // only name is needed to be provided
}

Type Filter is fully type safe, by creating instance of this type we need to define proper keys and proper rules for these keys.类型Filter是完全类型安全的,通过创建这种类型的实例,我们需要为这些键定义适当的键和适当的规则。

As an addition you can conditionally say which rules are possible for which types.作为补充,您可以有条件地说明哪些规则适用于哪些类型。 Lets say $gte can be applicable only for numbers, but not for other types.可以说$gte只能适用于数字,但不适用于其他类型。 You can do so by:你可以这样做:

type Rule<T> = T extends number ? { $eq: T } | { $gte: T } : { $eq: T }

Above definition will prevent from using $gte for anything other than number.上面的定义将阻止将$gte用于数字以外的任何内容。

What I want to do is allow IFilter to allow more than one property我想做的是让 IFilter 允许多个属性

IFilter is a type with a string index signature and already allows multiple properties with string name type and IKeyValue value type. IFilter是一种具有string 索引签名的类型,并且已经允许具有string名称类型和IKeyValue值类型的多个属性。 It is just that the IDE/compiler cannot assist with auto completion, as it cannot know, what is inside due to the dynamic way the properties can be added .只是 IDE/编译器不能协助自动完成,因为它不知道,由于可以添加属性的动态方式,里面是什么。

For example your code would be even valid for unknown property names:例如,您的代码甚至对未知的属性名称也有效:

const filterValue = filter["helloWorld"] // IKeyValue
const keyValue = filter["hello"]["World"] // any

I am not sure, what is the best solution here.我不确定,这里最好的解决方案是什么。 If there is a set of fix (potentially optional) property names for IFilter like Name or Age , maybe a hybrid type with index signature and known properties can be created :如果IFilter有一组修复(可能是可选的)属性名称,例如NameAge ,则可以创建具有索引签名和已知属性的混合类型:

export interface IFilter {
  [property: string]: IKeyValue | undefined;
  Name?: IKeyValue;
  Age?: IKeyValue;
}

const filter : IFilter = {
  "Name": { $eq: "Bob" } // code assist for Name/Age properties
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM