我试图创建一个函数,该函数将返回一个通用对象类型,其中包含从传入该函数的参数派生的键。一个简单的例子:
// 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"
。
你可以在这里试试这个解决方案。