具有多个动态键的Typescript接口

时间:2019-11-28 11:12:23

标签: typescript

我有一个查询MongoDB数据库的应用程序,但目前仅能查询文档中的单个字段:

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

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

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

我想做的是允许IFilter允许多个属性:

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

我如何创建一个Typescript接口,该接口将允许多个动态键,而又不将objectany用作IFilter的类型?

2 个答案:

答案 0 :(得分:1)

我对您的需求的了解是:

  • 您有一些实体,可以说User
  • 您要创建可用于不同实体的通用过滤器类型
  • 通用过滤器类型对于每个实体均应为安全类型

首先让我们定义可能需要过滤的规则

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

我从您的示例中提取了两个规则-$eq$gte,但是通过|,您可以添加所需的其他规则。

第二个让我们定义通用的Filter类型

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

我们的类型说-我应该拥有给定对象的所有键,并且我应该为每个键定义一个规则,该规则适用于与此属性相同的类型。因此,对于属性a: string,规则必须为或{$eq: string}{$gte: string}

让我们看一个例子:

// 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
}

类型Filter是完全类型安全的,通过创建该类型的实例,我们需要为这些密钥定义适当的密钥和适当的规则。

此外,您可以有条件地说出哪些规则适用于哪些类型。假设$gte仅适用于数字,不适用于其他类型。您可以通过以下方式实现:

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

以上定义将阻止将$gte用于数字以外的其他任何东西。

答案 1 :(得分:0)

  

我想做的是允许IFilter允许多个属性

IFilter是具有string index signature的类型,并且已经允许使用string名称类型和IKeyValue值类型的多个属性。只是,IDE /编译器无法协助自动完成,因为它不知道dynamic way the properties can be added导致的内容。

例如,您的代码甚至对于未知的属性名称都有效:

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

我不确定,什么是最好的解决方案。如果IFilter有一组固定的(可能是可选的)属性名称,例如NameAge,则具有索引签名和已知属性的混合类型可以是created

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

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