我有一个外部调用非类型化调用,它接受字符串列表作为输入,并返回一个以此字符串作为键的对象。例如
const res = getAccount(['name', 'id'])
将返回 { name: string, id: string}
类型的对象。
有没有办法创建包装器并使调用类型安全?
我尝试过
type Obj<T extends string[]> = {
[K in T[number]]: string;
};
这可以使用非动态参数,例如
type a = Obj<['id']>;
您的包装函数可以如下所示:
function getAccountWrapper<Names extends string[]>(
names: readonly [...Names]
): Record<Names[number], string> {
return getAccount(names);
}
一些使用示例:
const res1 = getAccountWrapper(["name", "id"]);
// ^? const res1: Record<"name" | "id", string>
const names2 = ["name", "id"] as const;
const res2 = getAccountWrapper(names2);
// ^? const res2: Record<"name" | "id", string>
const names3 = ["name", "id"];
const res3 = getAccountWrapper(names3);
// ^? const res3: Record<string, string>
请注意,只有
res1
和 res2
具有已知密钥。这是因为 TypeScript 可以从给定的元组中推断出它们。但是 res3
只是 Record<string, string>
,因为 names3
具有更通用的类型 string[]
而不是元组类型 ["name", "id"]
。
为了清楚起见,请注意
Record<"name" | "id", string>
是 { name: string; id: string; }
,只是形式不同。但你也可以这样编写函数类型:
function getAccountWrapper<Names extends string[]>(
names: readonly [...Names]
): {[Key in Names[number]]: string} {
return getAccount(names);
}
// ...
// Usage:
const res1 = getAccountWrapper(["name", "id"]);
// ^? const res1: { name: string; id: string; }
const names2 = ["name", "id"] as const;
const res2 = getAccountWrapper(names2);
// ^? const res2: { name: string; id: string; }
const names3 = ["name", "id"];
const res3 = getAccountWrapper(names3);
// ^? const res3: { [x: string]: string; }
您也可以直接通过声明文件将这些函数类型中的任何一个应用于现有的
getAccount
函数,但具体细节取决于您获得 getAccount
的方式和位置。