2017-05-03 148 views
1

我想弄清楚如何创建一个函数接受一个泛型类型是一个对象与键和值,并返回一个类型具有相同的接口,但修改该对象的值。这一点很难用简单的英语解释,所以这里有一个例子:转换类型脚本中的通用接口类型

下面是有一个通用的类型和包装了一个价值

class Wrap<A> { 
    constructor(public value: A) {} 
} 

我希望创建一个需要两个功能wrapunwrap一个人为的类一个对象并确保所有的值都被打包或解包。下面是功能香草JS:

function wrap(obj) { 
    const result = {} 
    Object.keys(obj).forEach(key => { 
     if (obj[key] instanceof Wrap) { 
      result[key] = obj[key] 
     } else { 
      result[key] = new Wrap(obj[key]) 
     } 
    }) 
    return result 
} 

function unwrap(obj) { 
    const result = {} 
    Object.keys(obj).forEach(key => { 
     if (obj[key] instanceof Wrap) { 
      result[key] = obj[key].value 
     } else { 
      result[key] = obj[key] 
     } 
    }) 
    return result 
} 

我试图找出如何为这些函数创建类型签名,同时保持尽可能多的类型安全越好。例如,如果输入类型是一个接口,我想输出接口,以及:

interface Ex1Input { 
    a: number 
    b: string 
} 

interface Ex1Output { 
    a: Wrap<number> 
    b: Wrap<string> 
} 

interface Ex2Input { 
    a: number 
    b: Wrap<string> 
} 

interface Ex2Output { 
    a: Wrap<number> 
    b: Wrap<string> 
} 

我不想输入和输出的任意{[key: string]: Wrap<any>}因为我希望保持接口的类型安全。

同样与unwrap功能:

interface Ex3Input { 
    a: Wrap<number> 
    b: Wrap<string> 
} 

interface Ex3Output { 
    a: number 
    b: string 
} 

interface Ex4Input { 
    a: number 
    b: Wrap<string> 
} 

interface Ex4Output { 
    a: number 
    b: string 
} 

我已经尝试了一些不同的东西......这也许是我已经得到最接近的,但我觉得它还是很远,因为我的工作想。

interface WrapInput { 
    [key: string]: any 
} 

interface WrapOutput extends WrapInput { 
    [key: string]: Wrap<WrapInput[keyof WrapInput]> 
} 

function wrap<T extends WrapInput>(obj: T): WrapOutput { 
    const result: WrapOutput = {} 
    Object.keys(obj).forEach(key => { 
     if (obj[key] instanceof Wrap) { 
      result[key] = obj[key] 
     } else { 
      result[key] = new Wrap(obj[key]) 
     } 
    }) 
    return result 
} 

任何想法?这可能吗?只是重申 - 我对尽可能保持类型安全的解决方案感兴趣。因此,如果我使用wrapEx1Input,我的编辑器中的自动完成功能应该会显示输出上只有两个属性及其特定类型。

回答

1

您可以使用mapped typesMaybeWrappedWrapped在下面的例子)。

class Wrap<Value> { 
    constructor(public value: Value) {} 
} 

type MaybeWrapped<T> = { 
    [K in keyof T]: T[K] | Wrap<T[K]>; 
}; 

type Wrapped<T> = { 
    [K in keyof T]: Wrap<T[K]>; 
}; 

function wrap<T>(arg: MaybeWrapped<T>): Wrapped<T> { 
    // your implementation here 
} 

function unwrap<T>(arg: MaybeWrapped<T>): T { 
    // your implementation here 
} 

declare const input: { 
    a: Wrap<string> 
    b: Date 
}; 

const wrapped = wrap(input); 
wrapped.a.value.length; // wrapped.a is infered as Wrap<string> 
wrapped.b.value.getTime(); // wrapped.b is infered as Wrap<Date> 

const unwrapped = unwrap(input); 
unwrapped.a.length; // unwrapped.a is infered as string 
unwrapped.b.getTime(); // unwrapped.b is infered as Date 
0

你能尝试以下..

function wrap<T>(obj: T): T { 
    const result: any = {} 
    Object.keys(obj).forEach(key => { 
     if ((obj as any)[key] instanceof Wrap) { 
      result[key] = (obj as any)[key] 
     } else { 
      result[key] = new Wrap((obj as any)[key]) 
     } 
    }) 
    return result 
} 


const data = { someData: 1 } 
var test = wrap(data) 
test.someData // the autocomplete in my editor shows someData with intellisense 
+2

感谢您的回答。请描述你的答案,解释你在做什么。考虑坐在提问人员旁边,试图解释如何解决问题,而不是提供答案。 – jjude