Safe Entry of Object.assign

We are looking for a safe way to use Object.assign. However, we cannot make it work.

To show our problem, I will use the copyFields method from the Generics documentation

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" } 

I want the engine to not let me create undeclared properties

 /*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 

We tried to solve this problem by explicitly exposing the types to the copyfields call, but we cannot find a call that will make all the examples work.

For example: To do 5 jobs, you can call copyFields as follows:

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

but subsequent changes to the source type (for example, removing the option "b") now no longer lead to an error like

Does anyone know how to do this?

+5
source share
3 answers

Typescript 2.1.4 to the rescue!

Playground

 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
source

The best workaround I can think of is to define a second interface (I called it SourceParts ), which is exactly the same as Source , except that all members are optional.

 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 

Here he is at the Typescript Playground .

+1
source

I have this function:

  /** * 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; } 

Using:

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

🌹.

More details

I already mentioned this function in a different context: fooobar.com/questions/1254984 / ...

PS: everything will get better as soon as JavaScript gets it to step 3: https://github.com/Microsoft/TypeScript/issues/2103

-1
source

All Articles