2016-08-18 55 views
5

我們正在尋找一種使用Object.assign的類型安全方法。但是,我們似乎無法使其工作。Object.assign的安全輸入

爲了表示我們的問題,我將使用copyFields方法從Generics文檔

function copyFields<T extends U, U>(target: T, source: U): T { 
    for (let id in source) { 
     target[id] = source[id]; 
    } 
    return target; 
} 

function makesrc(): Source { return {b: 1, c: "a"}} 

interface Source { 
    a?: "a"|"b", 
    b: number, 
    c: "a" | "b" 
} 

我想要的發動機,以防止我從創建未申報的性質

/*1*/copyFields(makesrc(), {d: "d"}); //gives an error 
/*2*/copyFields(makesrc(), {a: "d"}); //gives an error 
/*3*/copyFields(makesrc(), {c: "d"}); //should give an error, but doesn't because "a"|"b" is a valid subtype of string. 

//I don't want to specify all the source properties 
/*4*/copyFields(makesrc(), {b: 2}); //will not give me an error 
/*5*/copyFields(makesrc(), {a: "b"}); //should not give an error, but does because string? is not a valid subtype of string 

我們已經試圖解決這個明確提供複製字段的類型請致電 ,但我們無法找到將使所有示例都能正常工作的調用。

例如: 使5個工作你可以稱之爲copyFields這樣的:

/*5'*/copyFields<Source,{a?:"a"|"b"}>(makesrc(), {a: "b"}); 

但後續的源類型的改變(例如去掉了「B」選項)現在將不再導致鍵入錯誤

有沒有人知道一種方法使這項工作?

+1

看看'&'類型的操作。 – 2016-08-18 08:42:38

+0

BTW我們(劉若英是我的同事)發現,流不支持這種使用情況https://tryflow.org/?code=ZGVjbGFyZSBpbnRlcmZhY2UgU291cmNlIHsKICAgIGE/OiAiYSIgfCAiYiIsCiAgICBiOiBudW1iZXIsCiAgICBjOiAiYSIgfCAiYiIKfQpmdW5jdGlvbiBtYWtlc3JjKCkgOiBTb3VyY2UgeyByZXR1cm4ge2I6IDEsIGM6ICJhIn19CgovKjEqL09iamVjdC5hc3NpZ24obWFrZXNyYygpLCB7ZDogImQifSk7Ci8qMiovT2JqZWN0LmFzc2lnbihtYWtlc3JjKCksIHthOiAiZCJ9KTsKLyozKi9PYmplY3QuYXNzaWduKG1ha2VzcmMoKSwge2M6ICJkIn0pOwoKLyo0Ki9PYmplY3QuYXNzaWduKG1ha2VzcmMoKSwge2I6IDJ9KTsKLyo1Ki9PYmplY3QuYXNzaWduKG1ha2VzcmMoKSwge2E6ICJiIn0pOw== – Jauco

+0

@torazaburo不會作出所需的實例失敗 – Jauco

回答

1

打字稿2.1.4至救援!

Playground link

interface Data { 
    a?: "a"|"b", 
    b: number, 
    c: "a" | "b" 
} 

function copyFields<T>(target: T, source: Readonly<Partial<T>>): T { 
    for (let id in source) { 
     target[id] = source[id]; 
    } 
    return target; 
} 

function makesrc(): Data { return {b: 1, c: "a"}} 

/*1*/copyFields(makesrc(), {d: "d"}); //gives an error 
/*2*/copyFields(makesrc(), {a: "d"}); //gives an error 
/*3*/copyFields(makesrc(), {c: "d"}); //gives an error 

//I don't want to specify all the source properties 
/*4*/copyFields(makesrc(), {b: 2}); //will not give me an error 
/*5*/copyFields(makesrc(), {a: "b"}); //will not give me an error 
1

我能想到的最佳解決方法是定義第二個接口(我稱之爲SourceParts),它與Source完全相同,但所有成員都是可選的。

function copyFields<T extends U, U>(target: T, source: U): T { 
    for (let id in source) { 
     target[id] = source[id]; 
    } 
    return target; 
} 

function makesrc(): Source { return {b: 1, c: "a"}} 

interface Source { 
    a?: "a"|"b", 
    b: number, 
    c: "a" | "b" 
} 

interface SourceParts { 
    a?: "a"|"b", 
    b?: number, 
    c?: "a" | "b" 
} 

/*1*/copyFields<Source, SourceParts>(makesrc(), {d: "d"}); //gives an error 
/*2*/copyFields<Source, SourceParts>(makesrc(), {a: "d"}); //gives an error 
/*3*/copyFields<Source, SourceParts>(makesrc(), {c: "d"}); //gives an error 

//I don't want to specify all the source properties 
/*4*/copyFields<Source, SourceParts>(makesrc(), {b: 2}); //will not give me an error 
/*5*/copyFields<Source, SourceParts>(makesrc(), {a: "b"}); //will not give me an error 

這裏是在Typescript Playground

+0

這個解決方案確實發生在我們身上,但是對於我們的用例來說,它需要大量的雙模板。 – renevanderark

+0

是的,這至少將兩個類型定義放在一起,所以你很有可能會更新它們。 – Jauco

-1

我有這樣的功能:

/** 
    * Take every field of fields and put them override them in the complete object 
    * NOTE: this API is a bit reverse of extend because of the way generic constraints work in TypeScript 
    */ 
    const updateFields = <T>(fields: T) => <U extends T>(complete: U): U => { 
     let result = <U>{}; 
     for (let id in complete) { 
      result[id] = complete[id]; 
     } 
     for (let id in fields) { 
      result[id] = fields[id]; 
     } 
     return result; 
    } 

用法:

updateFields({a:456})({a:123,b:123}) // okay 
updateFields({a:456})({b:123}) // Error 

我在不同的上下文中提到過此功能

更多:https://stackoverflow.com/a/32490644/390330

PS:事情會變得更好的JavaScript一次獲得此上演3:https://github.com/Microsoft/TypeScript/issues/2103

+0

好的解決方案,但是,我們的願望是(如評論中所述):「我不想指定所有源屬性」 – renevanderark

+1

@renevanderark不必'updateFields({a:456})({a :123,b:123})''只指定'a'(源文件同時包含'a'和'b') – basarat

+1

對不起。這個功能和我們的功能是一樣的,但是有咖喱。我在操場上嘗試過(鏈接太長而無法粘貼),並且在相同的測試中失敗(3&5) – Jauco