我有一个具有以下结构的对象:
const items = {
a: { create: () => "hello" },
b: { create: () => "hello", delete: () => "goodbye" },
c: { delete: () => "goodbye" },
}
我想为
items
的所有键创建类型,这些键具有不同的方法。所以:
type CreateItems = 'a' | 'b'
type DeleteItems = 'b' | 'c'
如何从
CreateItems
对象生成 DeleteItems
和 items
,而不是对类型进行硬编码?
这是如何做到这一点的示例(游乐场链接):
const items = {
a: { create: () => "hello" },
b: { create: () => "hello", delete: () => "goodbye" },
c: { delete: () => "goodbye" },
};
type MyType<T, Name extends string> = {
[key in keyof T]: T[key] extends { [key in Name]: () => string } ? key : never;
}[keyof T];
type CreateItems = MyType<typeof items, "create">; // "a" | "b"
type DeleteItems = MyType<typeof items, "delete">; // "c" | "d"
为了解释,让我们一次看一下:
type MyType<T, Name extends string> =
这正在创建一个泛型类型。泛型有两个参数可以控制它:
T
是我们要查看的对象,以及 Name
是我们关心的属性名称。
{
[key in keyof T]: // ...
}
这部分正在制作映射类型。
items
有一个 a
、b
和 c
,并且该对象也将具有这三个。
T[key] extends { [key in Name]: () => string } ? key : never;
这个条件将检查该对象是否具有我们正在寻找的属性。例如,如果
Name
是 create
,那么该对象必须看起来像 { create: () => string }
,或者从其扩展的东西(即,允许额外的属性)。如果匹配,我们将值设置为等于键,否则我们将值设置为 never。
结合到目前为止的步骤将生成一个如下所示的类型(如果 Name == “create”):
{
a: "a",
b: "b",
c: never
}
还有最后一篇:
[keyof T]
这将使我们刚刚创建的对象类型中存在的所有值进行并集。这是“a”、“b”和
never
,但永远不会被忽略。