https://github.com/angular-redux/tassign
Subset-typed, non-mutating Object.assign
https://github.com/angular-redux/tassign
Last synced: about 2 months ago
JSON representation
Subset-typed, non-mutating Object.assign
- Host: GitHub
- URL: https://github.com/angular-redux/tassign
- Owner: angular-redux
- License: mit
- Created: 2016-11-07T02:05:48.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2016-11-08T13:22:34.000Z (over 8 years ago)
- Last Synced: 2025-04-03T02:38:02.487Z (about 2 months ago)
- Language: JavaScript
- Size: 4.88 KB
- Stars: 23
- Watchers: 2
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# tassign
This is a simple wrapper for fixed-arity, subset-typed, non-mutating `Object.assign`.
[](https://circleci.com/gh/angular-redux/tassign/tree/master)
[](https://www.npmjs.com/package/tassign)
[](https://www.npmjs.com/package/tassign)## Why?
This came about when writing Redux reducers in TypeScript. Since reducers must not
mutate the passed in state, a common pattern is to 'set' store properties using `Object.assign()`:```typescript
interface IMyState {
foo: Number;
}function myReducer(state: IMyState, action: Action) {
switch(action.type) {
case SET_FOO_ACTION:
return Object.assign({}, state, { foo: 3 });
}
return state;
}
```This works well enough. `Object.assign` to an empty object produces a new object with the new value
of `foo`. However I wasn't happy with it from a type-enforcement perspective.The reason is that for a reducer use case, I would like to enforce that the reducer can only set known
properties of `IMyState`.Using the regular, intersection-based `Object.assign` typings, this is perfectly legal:
```typescript
interface IMyState {
foo: Number;
}function myReducer(state: IMyState, action: Action) {
switch(action.type) {
case SET_FOO_ACTION:
// No TS error - I would like there to be.
return Object.assign({}, state, { foo: 3, bar: 'wat' });
}
return state;
}
```Formally, `Object.assign(t:T, u:U)` returns a type which is the _intersection_ of `T & U`, meaning that properties
can be added onto the assignee. This is reasonable in the general case for `Object.assign`, but it's not what
I want in a reducer.In a reducer, I want `assign(t:T, u:U)` to enforce a subset rule: the return value should be an instance of `T`, and only
subsets of `T` are legal values for `U`. In short I want to make sure you can only assign members of `T` to the
object returned by the reducer.So, here's a utility that handles the typings the way I want in a reducer:
```typescript
interface IMyState {
foo: Number;
}function myReducer(state: IMyState, action: Action) {
switch(action.type) {
case SET_FOO_ACTION:
// Returns a new object with the type of the first argument.
// Type of the second argument must be a subset of the type of the first argument.
// Fails: bar is not a member of IMyState.
return tassign(state, { foo: 3, bar: 'wat' });
}
return state;
}
```