Skip to content

Commit

Permalink
revamped a bit the type system
Browse files Browse the repository at this point in the history
  • Loading branch information
xaviergonz committed Mar 19, 2019
1 parent ce2d105 commit 9fbcdd5
Show file tree
Hide file tree
Showing 31 changed files with 369 additions and 406 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -1348,12 +1348,13 @@ What about compile time? You can use TypeScript interfaces to perform those chec

Good news! You don't need to write it twice!

There are three kinds of types available, plus one helper type:
There are four kinds of types available, plus one helper type:

- `Instance<typeof TYPE>` or `Instance<typeof VARIABLE>` is the node instance type. (Legacy form is `typeof MODEL.Type`).
- `SnapshotIn<typeof TYPE>` or `SnapshotIn<typeof VARIABLE>` is the input (creation) snapshot type. (Legacy form is `typeof MODEL.CreationType`).
- `SnapshotOut<typeof TYPE>` or `SnapshotOut<typeof VARIABLE>` is the output (creation) snapshot type. (Legacy form is `typeof MODEL.SnapshotType`).
- `SnapshotOrInstance<typeof TYPE>` or `SnapshotOrInstance<typeof VARIABLE>` is `SnapshotIn<T> | Instance<T>`. This type is useful when you want to declare an input parameter that is able consume both types.
- `TypeOfValue<typeof VARIABLE>` gets the original type for the given instance. Note that this only works for complex values (models, arrays, maps...) but not for simple values (number, string, boolean, string, undefined).

```typescript
const Todo = types
Expand Down
1 change: 1 addition & 0 deletions changelog.md
@@ -1,3 +1,4 @@
- Added `TypeOfValue<typeof variable>` to extract the type of a complex (non primitive) variable in Typescript.
- Fixed some Typescript issues with optional arrays [#1218](https://github.com/mobxjs/mobx-state-tree/issues/1218) through [#1229](https://github.com/mobxjs/mobx-state-tree/pull/1229) by [@xaviergonz](https://github.com/xaviergonz)
- Added `getNodeId` to get the internal unique node id for an instance [#1168](https://github.com/mobxjs/mobx-state-tree/issues/1168) through [#1225](https://github.com/mobxjs/mobx-state-tree/pull/1225) by [@xaviergonz](https://github.com/xaviergonz)
- Fixed nodes being `pop`/`shift`/`splice` from an array not getting properly destroyed through [#1205](https://github.com/mobxjs/mobx-state-tree/pull/1205) by [@xaviergonz](https://github.com/xaviergonz). Not that this means that in order to access the returned dead nodes data without getting a liveliness error/warning then the returned dead nodes have to be either cloned (`clone`) or their snapshots (`getSnapshot`) have to be used first.
Expand Down
154 changes: 57 additions & 97 deletions docs/API/README.md

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions docs/API/interfaces/ianymodeltype.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions docs/API/interfaces/imodeltype.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions docs/API/interfaces/isimpletype.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions docs/API/interfaces/isnapshotprocessor.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions docs/API/interfaces/itype.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 26 additions & 1 deletion packages/mobx-state-tree/__tests__/core/type-system.test.ts
Expand Up @@ -11,7 +11,8 @@ import {
castToSnapshot,
IType,
isStateTreeNode,
isFrozenType
isFrozenType,
TypeOfValue
} from "../../src"

const createTestFactories = () => {
Expand Down Expand Up @@ -990,3 +991,27 @@ test("MST array type should be assignable to plain array type", () => {
}).a
}
})

test("can get snapshot from submodel (submodel is IStateNodeTree", () => {
const T = types.model({
a: types.model({ x: 5 })
})
const t = T.create({ a: {} })
const sn = getSnapshot(t.a).x
})

test("can extract type from complex objects", () => {
const T = types.maybe(
types.model({
a: types.model({
x: 5
})
})
)
const t = T.create({
a: {}
})!

type OriginalType = TypeOfValue<typeof t>
const T2: OriginalType = T
})
8 changes: 4 additions & 4 deletions packages/mobx-state-tree/scripts/generate-union-types.js
Expand Up @@ -8,9 +8,9 @@ const preParam = "options: UnionOptions, "
const modelReturnTypeTransform = rt => {
// [['PA', 'PB'], ['OA', 'OB'], ['FCA', 'FCB'], ['FSA', 'FSB']]
// ->
// [['ModelCreationType2<PA, FCA>', 'ModelCreationType2<PB, FCB>>'],
// ['ModelSnapshotType2<PA, FSA>', 'ModelSnapshotType2<PB, FSB>>'],
// ['ModelInstanceType<PA, OA, FCA, FSA>', 'ModelInstanceType<PB, OB, FCB, FSB']]
// [['ModelCreationType2<PA, FCA>', 'ModelCreationType2<PB, FCB>'],
// ['ModelSnapshotType2<PA, FSA>', 'ModelSnapshotType2<PB, FSB>'],
// ['ModelInstanceType<PA, OA>', 'ModelInstanceType<PB, OB>']]
const [props, others, fixedC, fixedS] = rt

const c = [],
Expand All @@ -24,7 +24,7 @@ const modelReturnTypeTransform = rt => {

c.push(`ModelCreationType2<${p}, ${fc}>`)
s.push(`ModelSnapshotType2<${p}, ${fs}>`)
t.push(`ModelInstanceType<${p}, ${o}, ${fc}, ${fs}>`)
t.push(`ModelInstanceType<${p}, ${o}>`)
}
return [c, s, t]
}
Expand Down
36 changes: 21 additions & 15 deletions packages/mobx-state-tree/src/core/mst-operations.ts
@@ -1,6 +1,5 @@
import { isComputedProp, isObservableProp } from "mobx"
import {
ExtractT,
IAnyStateTreeNode,
IType,
IAnyModelType,
Expand All @@ -13,29 +12,28 @@ import {
EMPTY_OBJECT,
fail,
IDisposer,
isType,
resolveNodeByPath,
getRelativePathBetweenNodes,
freeze,
IAnyType,
isModelType,
ModelPrimitive,
ExtractNodeC,
InvalidReferenceError,
normalizeIdentifier,
ReferenceIdentifier,
AnyObjectNode,
AnyModelType,
assertIsType,
assertIsStateTreeNode,
ExtractNodeS,
ExtractNodeT
ExtractC,
TypeOfValue,
ExtractTWithSTN,
ExtractS
} from "../internal"

/** @hidden */
export type TypeOrStateTreeNodeToStateTreeNode<
T extends IAnyType | IAnyStateTreeNode
> = T extends IType<any, any, infer TT> ? TT : T
> = T extends IType<any, any, infer TT> ? TT & IStateTreeNode<T> : T

/**
* Returns the _actual_ type of the given tree node. (Or throws)
Expand Down Expand Up @@ -103,7 +101,7 @@ export function onPatch(
* @returns
*/
export function onSnapshot<S>(
target: IStateTreeNode<any, S, any>,
target: IStateTreeNode<IType<any, S, any>>,
callback: (snapshot: S) => void
): IDisposer {
// check all arguments
Expand Down Expand Up @@ -272,7 +270,7 @@ export function isProtected(target: IAnyStateTreeNode): boolean {
* @param snapshot
* @returns
*/
export function applySnapshot<C>(target: IStateTreeNode<C, any, any>, snapshot: C) {
export function applySnapshot<C>(target: IStateTreeNode<IType<C, any, any>>, snapshot: C) {
// check all arguments
assertIsStateTreeNode(target, 1)

Expand All @@ -287,7 +285,10 @@ export function applySnapshot<C>(target: IStateTreeNode<C, any, any>, snapshot:
* @param applyPostProcess If true (the default) then postProcessSnapshot gets applied.
* @returns
*/
export function getSnapshot<S>(target: IStateTreeNode<any, S, any>, applyPostProcess = true): S {
export function getSnapshot<S>(
target: IStateTreeNode<IType<any, S, any>>,
applyPostProcess = true
): S {
// check all arguments
assertIsStateTreeNode(target, 1)

Expand Down Expand Up @@ -385,7 +386,7 @@ export function hasParentOfType(target: IAnyStateTreeNode, type: IAnyType): bool
export function getParentOfType<IT extends IAnyType>(
target: IAnyStateTreeNode,
type: IT
): ExtractT<IT> {
): ExtractTWithSTN<IT> {
// check all arguments
assertIsStateTreeNode(target, 1)
assertIsType(type, 2)
Expand Down Expand Up @@ -488,7 +489,7 @@ export function resolveIdentifier<IT extends IAnyType>(
type: IT,
target: IAnyStateTreeNode,
identifier: ReferenceIdentifier
): ExtractT<IT> | undefined {
): ExtractTWithSTN<IT> | undefined {
// check all arguments
assertIsType(type, 1)
assertIsStateTreeNode(target, 2)
Expand Down Expand Up @@ -843,9 +844,14 @@ export function getMembers(target: IAnyStateTreeNode): IModelReflectionData {
return reflected
}

export function cast<O extends ModelPrimitive = never>(snapshotOrInstance: O): O
export function cast<O extends string | number | boolean | null | undefined = never>(
snapshotOrInstance: O
): O
export function cast<O = never>(
snapshotOrInstance: ExtractNodeC<O> | ExtractNodeS<O> | ExtractNodeT<O>
snapshotOrInstance:
| ExtractC<TypeOfValue<O>>
| ExtractS<TypeOfValue<O>>
| ExtractTWithSTN<TypeOfValue<O>>
): O
/**
* Casts a node snapshot or instance type to an instance type so it can be assigned to a type instance.
Expand Down Expand Up @@ -909,7 +915,7 @@ export function cast(snapshotOrInstance: any): any {
*/
export function castToSnapshot<I>(
snapshotOrInstance: I
): Extract<I, IAnyStateTreeNode> extends never ? I : ExtractNodeC<I> {
): Extract<I, IAnyStateTreeNode> extends never ? I : ExtractC<TypeOfValue<I>> {
return snapshotOrInstance as any
}

Expand Down
6 changes: 3 additions & 3 deletions packages/mobx-state-tree/src/core/node/identifier-cache.ts
Expand Up @@ -5,9 +5,9 @@ import {
mobxShallow,
AnyObjectNode,
ExtractS,
ExtractT,
ExtractC,
IAnyComplexType
IAnyComplexType,
ExtractTWithoutSTN
} from "../../internal"

let identifierCacheId = 0
Expand Down Expand Up @@ -105,7 +105,7 @@ export class IdentifierCache {
resolve<IT extends IAnyComplexType>(
type: IT,
identifier: string
): ObjectNode<ExtractC<IT>, ExtractS<IT>, ExtractT<IT>> | null {
): ObjectNode<ExtractC<IT>, ExtractS<IT>, ExtractTWithoutSTN<IT>> | null {
const set = this.cache.get(identifier)
if (!set) return null
const matches = set.filter(candidate => type.isAssignableFrom(candidate.type))
Expand Down

0 comments on commit 9fbcdd5

Please sign in to comment.