以下泛型函数接受一个函数对象,并返回一个具有相同propnames的对象,该对象的值与相应函数的返回值一致。
const combineValues = obj => {
const res = {};
for (const k in obj){
res[k] = obj[k]()
}
return res;
}
const combined = combineValues({
n: () => 1,
s: () => 's'
}); // { n: 1, s: 's' }
尝试在Typescript
中为此函数定义签名和实现const combineValues =
<K extends string> (obj: Record<K, () => any>): Record < K, any > => {
const res = {} as Record<K, any>;
for (const k in obj){
res[k] = obj[k]()
}
return res;
}
const combined = combineValues({
n, s
}) // Record<"n" | "s", any>
但combined
不保留值的原始输入
const combcombineValues = <K extends string, T>(obj: Record<K, () => T>): Record<K, T> => {
const res = {} as Record<K, T>;
for (const k in obj){
res[k] = obj[k]()
}
return res;
}
仅当所有函数道具返回相同类型T
时,才有效
是否可以在Typescript中完全定义它?
答案 0 :(得分:3)
这可以在当前版本的TypeScript(v2.7)中完成而无需等待条件类型,方法是使用inference from mapped types,这是TypeScript的一个功能,其中函数可以推断类似于#34的类型;向后&#34;方向,从输出到输入。
首先让我们定义映射类型Funcs<T>
,它接受一个普通对象并将其转换为一个对象,其属性是返回普通对象的属性类型的所有函数:
type Funcs<T> = { [K in keyof T]: (...args:any[]) => T[K] }
请注意此类型基本上是您想要做的事情的后退。现在让我们输入combineValues()
:
const combineValues = <T>(obj: Funcs<T>): T => {
const res = {} as T; // only change in body
for (const k in obj) {
res[k] = obj[k]()
}
return res;
}
如您所见,将Funcs<T>
作为输入,并返回T
作为输出。让我们看看它是否有效:
const combined = combineValues({
n: () => 1,
s: () => 's'
}); // { n: number, s: string }
那几乎你想要什么。唯一的区别是TypeScript倾向于将() => 1
解释为返回number
的函数,而不是返回文字1
的函数。你可以做些什么来解决这个问题;最简单的(虽然有点重复)是断言返回类型文字:
const combined = combineValues({
n: () => 1 as 1,
s: () => 's' as 's'
}); // { n: 1, s: 's' }
希望有所帮助。祝你好运!
更新:注意到编译器似乎在TypeScript 2.7中将combined
键入为{n: any, s: any}
。幸运的是,当您使用Intellisense检查类型时,似乎只会发生这种情况,正如Microsoft/TypeScript#14041中提到的那样(我认为)。如果您实际使用该值,您将看到它具有正确的类型:
combined.n = 0 // error, 0 is not assignable to 1
combined.s = 0 // error, 0 is not assignable to 's'
所以,我想我会坚持这个答案,虽然Intellisense错误是不幸的。祝你好运!
答案 1 :(得分:2)
您可以使用conditional types在打字稿2.8中实现此目的(在撰写本文时尚未发布2.8,您可以通过npm install -g typescript@next
计划March release)。
// ReturnType<T> below is from 2.8 and is a conditional type
type AllReturnTypes<T extends { [name: string]: (...args: any[]) => any }> = { [P in keyof T]: ReturnType<T[P]> }
const combineValues = <T extends { [name: string]: () => any }>(obj: T): AllReturnTypes<T> => {
const res = {} as AllReturnTypes<T>;
for (const k in obj) {
res[k] = obj[k]()
}
return res;
}
const combined = combineValues({
n: () => 1, s: () => ""
}) // will be of type { n: number, s: string }