2017-05-03 102 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