如何在 Typescript 中使用另一个对象的值派生对象类型?



我试图创建一个函数,该函数将返回一个通用对象类型,其中包含从传入该函数的参数派生的键。一个简单的例子:

// What do I put here for this to work?
type ObjectB<T> = {};
declare function makeObjectB<T>(a: T): ObjectB<T>;
const objectB = makeObjectB({
name: "foo"
});
console.log(objectB.foo)

typeof objectB的结果如下:

{
foo: string
}

如果下面的例子也可以支持,那就加分了:

// What do I put here for this to work?
type ObjectB<T> = {};
declare function makeObjectB<T>(a: T[]): ObjectB<T[]>;
const objectB = makeObjectB([{ name: "foo" }, { name: "bar" }]);
console.log(objectB.foo)
console.log(objectB.bar)
typeof objectB => { foo: string; bar: string }

当然可以,使用映射类型:

type ObjectB<T extends { name: string }> = {
[K in T["name"]]: string;
};

你会注意到extends { name: string }T之后。这是一个泛型约束,类似于函数形参类型实参。

在映射类型中,我们遍历T["name"]中的每个K并将其映射到string

如果我们有这个:ObjectB<{ name: "foo" }>,T["name"]就是"foo"这个类型的结果是:

{
foo: string;
}
使用这种类型,我们现在可以编写函数签名:
declare function makeObjectB<T extends { name: string }>(a: T): ObjectB<T>;

您还会注意到这里存在相同的泛型约束。这是为了满足TypeScript,makeObjectB中的T可以与ObjectB一起使用。

另外,还可以定义重载:
declare function makeObjectB<T extends ReadonlyArray<{ name: string }>>(a: T): ObjectB<T[number]>;

T现在可以是任何具有指向字符串的name键的对象数组。

这里的另一个不同之处在于使用ObjectB<T[number]>而不是ObjectB<T>。这让我们可以将数组(这里的技术上是元组)中的所有元素作为一个联合,这意味着T["name"]现在类似于"foo" | "bar"

你可以在这里试试这个解决方案。

最新更新