-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Strong typed object assigning #7437
Comments
Perhaps you're looking for https://github.com/sebmarkbage/ecmascript-rest-spread let p2 = { ...p1, x: 5 }; //p2 is { x: 5, y: 2 } |
@Arnavion It's a nice feature but there's no type checking in this expression. I'd like to have the full type checking when the x property is renamed. There's a type check between left and right sides of assigning in my proposal. |
My point was that if that feature was in ES, then the TS support for it would include type-checking. Edit: Existing issue is #2103 |
There's always the // Returns shallow copy of a plain object.
function clone<T>(obj: T): T {
var newObj: any = {};
for (var k in obj) newObj[k] = (<any>obj)[k];
return newObj;
}
let p1 = toPoint(1, 2); // p1 is { x: 1, y: 2 }
let p2 = clone(p1); p2.x = 5; // p2 is { x: 5, y: 2 }
let p3 = clone(p1); p3.y = 7; // p3 is { x: 1, y: 7 }
let p4 = clone(p1); p4.z = 5; // Error: Property 'z' does not exist on type 'Point'. |
You actually can define a type-safe signature for the function assign<B, E>(base: B & E, extension: E): B & E {
// return Object.assign(base, extension);
for (let key in extension) {
base[key] = extension[key];
}
return base;
}
interface IA {
a: number;
b: number;
c: number;
}
let a: IA = {
a: 1,
b: 2,
c: 3
};
assign(a, {
b: 2,
c: 3
}); // Compiles.
assign(a, {
d: 4
}); // Fails to compile.
assign(a, {
c: 'test'
}); // Fails to compile. The magic trick is that TypeScript is actually checking the type of your This signature should at least enforce that all extension fields exist on your base object across your project.The bad news is that it will not automatically rename fields in the right places, but it will at least identify the places impacted by a rename. |
@ThomasMichon Looks nice but the assign function returns not the same type result as expected. Seems like a bug. Here's an example: function copy<T>(value: T): T {
let result = {}
for (let prop in value) result[prop] = value[prop]
return <T>result
}
function assign<B, E>(target: B & E, ...source: E[]): B & E {
let result = copy(target)
source.forEach(x => {
for (let prop in x) result[prop] = x[prop]
})
return result
}
interface User {
name: string
age: number
address: {
house: number
street: string
}
}
function toUser(name: string, age: number, street: string, house: number): User {
return {
name, age, address: { house, street }
}
}
function printUser(user: User) {
console.log(user)
}
let u1 = toUser("u1", 18, "s1", 1)
let u2 = assign(u1, { name: "u2" })
printUser(u2) //error, the type {} & { name: string }, expected User |
Thanks to TypeScript 1.8, here's a fix: function assign<B, E, T extends B & E>(base: T, extension: E): T {
for (let key in extension) {
base[key] = extension[key];
}
return base;
} This enforces that return type matches that of the input, but that |
@ThomasMichon Oh. That's cool. |
@ThomasMichon's proposal does address the original issue. the behavior of Object.assign, as spec'ed, is different from this though, it allows you to extend the object. so using a non-existing type operator assign<T, S1, S2>(target: T, source1: S1, source2: S2): S2 extends S1 extends T; |
All of the definitions of interface IFoo {
data: number | string;
}
const a: IFoo = {data: 5};
assign(a, {data: 6}); Generating:
The problem disappears if the |
@ThomasMichon The assign function still doesn't work, I am using TypeScript 1.8.10. |
This is a very common usage in 2015/2016 where people are now trying to do immutable object/array updates on top of JSON-like mutable data structures. typescript is useful at typing data accesses, but so far can't handle immutable JSON updates at all. @ThomasMichon 's snippet only works with very simple examples. It fails with type unions, etc. |
This seems to be getting increasingly relevant now in 2017. |
Forgot about this thread! https://github.com/AlexGalays/immupdate#update-nested-property Hope it helps. |
This proposal is mostly influenced by F# object expression syntax.
There's no ability in TypeScript to create a new object based on another one with new property values and a full type checking.
ES6 introduced the Object.assign function but it has a design flaw that allows a developer to ignore an object interface shape and even break it when the interface changes.
There could be a special syntax for the object assigning I have a naive proposal for.
Let there be a special keyword assign. Below the fragments show how it could work.
Simple example with a plain interface:
More complex example with nesting:
The right side object interface of assigning must be always checked against the left side object interface. If the right side interface partially(or fully) matches the left side interface then an assigning expression is valid otherwise not.
The text was updated successfully, but these errors were encountered: