import { assert , _ } from "spec.ts" ; import { types , getSnapshot , unprotect , getRoot , getParent , SnapshotOrInstance , cast , SnapshotIn , Instance , castToSnapshot , IType , isStateTreeNode , isFrozenType , TypeOfValue , IAnyType , ModelPrimitive , ModelPropertiesDeclaration , SnapshotOut } from "../../src" ; import { $nonEmptyObject } from "../../src/internal" ; const createTestFactories = ( ) => { const Box = types . model ( { width : 0 , height : 0 } ) ; const Square = types . model ( { width : 0 , height : 0 } ) ; const Cube = types . model ( { width : 0 , height : 0 , depth : 0 } ) ; return { Box , Square , Cube } ; } ; test ( "it should recognize a valid snapshot" , ( ) => { const { Box } = createTestFactories ( ) ; expect ( Box . is ( { width : 1 , height : 2 } ) ) . toEqual ( true ) ; expect ( Box . is ( { width : 1 , height : 2 , depth : 3 } ) ) . toEqual ( true ) ; } ) ; test ( "it should recognize an invalid snapshot" , ( ) => { const { Box } = createTestFactories ( ) ; expect ( Box . is ( { width : "1" , height : "2" } ) ) . toEqual ( false ) ; } ) ; test ( "it should check valid nodes as well" , ( ) => { const { Box } = createTestFactories ( ) ; const doc = Box . create ( ) ; expect ( Box . is ( doc ) ) . toEqual ( true ) ; } ) ; test ( "it should check invalid nodes as well" , ( ) => { const { Box } = createTestFactories ( ) ; const doc = Box . create ( ) ; expect ( types . model ( { anotherAttr : types . number } ) . is ( doc ) ) . toEqual ( false ) ; } ) ; test ( "it should do typescript type inference correctly" , ( ) => { const A = types . model ( { x : types . number , y : types . maybeNull ( types . string ) } ) . views ( ( self ) => ( { get z ( ) : string { return "hi" ; } } ) ) . actions ( ( self ) => { function method ( ) { const x = self . z + self . x + self . y ; anotherMethod ( x ) ; } function anotherMethod ( x ) { } return { method , anotherMethod } ; } ) ; const a = A . create ( { x : 2 , y : "7" } ) ; unprotect ( a ) ; const z = a . x ; a . x = 7 ; const B = types . model ( { sub : types . maybe ( A ) } ) ; const b = B . create ( ) ; unprotect ( b ) ; b . sub = A . create ( { x : 3 } ) ; b . sub . x = 4 ; const d = b . sub . y ; a . y = null ; const zz = a . z ; b . sub . method ( ) ; expect ( true ) . toBe ( true ) ; const snapshot = getSnapshot ( a ) ; const sx = snapshot . x ; const sy = snapshot . y ; expect ( sx ) . toBe ( 7 ) ; expect ( sy ) . toBe ( null ) ; } ) ; test ( "#66 - it should accept superfluous fields" , ( ) => { const Item = types . model ( { id : types . number , name : types . string } ) ; expect ( Item . is ( { } ) ) . toBe ( false ) ; expect ( Item . is ( { id : 3 } ) ) . toBe ( false ) ; expect ( Item . is ( { id : 3 , name : "" } ) ) . toBe ( true ) ; expect ( Item . is ( { id : 3 , name : "" , description : "" } ) ) . toBe ( true ) ; const a = Item . create ( { id : 3 , name : "" , description : "bla" } as any ) ; expect ( ( a as any ) . description ) . toBe ( undefined ) ; } ) ; test ( "#66 - it should not require defaulted fields" , ( ) => { const Item = types . model ( { id : types . number , name : types . optional ( types . string , "boo" ) } ) ; expect ( Item . is ( { } ) ) . toBe ( false ) ; expect ( Item . is ( { id : 3 } ) ) . toBe ( true ) ; expect ( Item . is ( { id : 3 , name : "" } ) ) . toBe ( true ) ; expect ( Item . is ( { id : 3 , name : "" , description : "" } ) ) . toBe ( true ) ; const a = Item . create ( { id : 3 , description : "bla" } as any ) ; expect ( ( a as any ) . description ) . toBe ( undefined ) ; expect ( a . name ) . toBe ( "boo" ) ; } ) ; test ( "#66 - it should be possible to omit defaulted fields" , ( ) => { const Item = types . model ( { id : types . number , name : "boo" } ) ; expect ( Item . is ( { } ) ) . toBe ( false ) ; expect ( Item . is ( { id : 3 } ) ) . toBe ( true ) ; expect ( Item . is ( { id : 3 , name : "" } ) ) . toBe ( true ) ; expect ( Item . is ( { id : 3 , name : "" , description : "" } ) ) . toBe ( true ) ; const a = Item . create ( { id : 3 , description : "bla" } as any ) ; expect ( ( a as any ) . description ) . toBe ( undefined ) ; expect ( a . name ) . toBe ( "boo" ) ; } ) ; test ( "#66 - it should pick the correct type of defaulted fields" , ( ) => { const Item = types . model ( { id : types . number , name : "boo" } ) ; const a = Item . create ( { id : 3 } ) ; unprotect ( a ) ; expect ( a . name ) . toBe ( "boo" ) ; if ( process . env . NODE_ENV !== "production" ) { expect ( ( ) => ( ( a as any ) . name = 3 ) ) . toThrowError ( `[mobx-state-tree] Error while converting \`3\` to \`string\`:\n\n    value \`3\` is not assignable to type: \`string\` (Value is not a string).` ) ; } } ) ; test ( "cannot create factories with null values" , ( ) => { expect ( ( ) => types . model ( { x : null } as any ) ) . toThrow ( ) ; } ) ; test ( "can create factories with maybe primitives" , ( ) => { const F = types . model ( { x : types . maybeNull ( types . string ) } ) ; expect ( F . is ( undefined ) ) . toBe ( false ) ; expect ( F . is ( { } ) ) . toBe ( true ) ; expect ( F . is ( { x : null } ) ) . toBe ( true ) ; expect ( F . is ( { x : "test" } ) ) . toBe ( true ) ; expect ( F . is ( { x : 3 } ) ) . toBe ( false ) ; expect ( F . create ( ) . x ) . toBe ( null ) ; expect ( F . create ( { x : undefined } ) . x ) . toBe ( null ) ; expect ( F . create ( { x : "" } ) . x ) . toBe ( "" ) ; expect ( F . create ( { x : "3" } ) . x ) . toBe ( "3" ) ; } ) ; test ( "it is possible to refer to a type" , ( ) => { const Todo = types . model ( { title : types . string } ) . actions ( ( self ) => { function setTitle ( v ) { } return { setTitle } ; } ) ; function x ( ) { return Todo . create ( { title : "test" } ) ; } const z = x ( ) ; unprotect ( z ) ; z . setTitle ( "bla" ) ; z . title = "bla" ; expect ( true ) . toBe ( true ) ; } ) ; test ( ".Type should not be callable" , ( ) => { const Todo = types . model ( { title : types . string } ) . actions ( ( self ) => { function setTitle ( v ) { } return { setTitle } ; } ) ; expect ( ( ) => Todo . Type ) . toThrow ( ) ; } ) ; test ( ".SnapshotType should not be callable" , ( ) => { const Todo = types . model ( { title : types . string } ) . actions ( ( self ) => { function setTitle ( v ) { } return { setTitle } ; } ) ; expect ( ( ) => Todo . SnapshotType ) . toThrow ( ) ; } ) ; test ( "types instances with compatible snapshots should not be interchangeable" , ( ) => { const A = types . model ( "A" , { } ) . actions ( ( self ) => { function doA ( ) { } return { doA } ; } ) ; const B = types . model ( "B" , { } ) . actions ( ( self ) => { function doB ( ) { } return { doB } ; } ) ; const C = types . model ( "C" , { x : types . maybe ( A ) } ) ; expect ( A . is ( { } ) ) . toBe ( true ) ; expect ( A . is ( B . create ( ) ) ) . toBe ( false ) ; expect ( A . is ( getSnapshot ( B . create ( ) ) ) ) . toBe ( true ) ; const c = C . create ( ) ; unprotect ( c ) ; expect ( ( ) => { c . x = undefined ; } ) . not . toThrow ( ) ; expect ( ( ) => { c . x = cast ( { } ) ; } ) . not . toThrow ( ) ; expect ( ( ) => { c . x = A . create ( ) ; } ) . not . toThrow ( ) ; expect ( ( ) => { ; ( c as any ) . x = B . create ( ) ; } ) . toThrow ( ) ; } ) ; test ( "it handles complex types correctly" , ( ) => { const Todo = types . model ( { title : types . string } ) . actions ( ( self ) => { function setTitle ( v ) { } return { setTitle } ; } ) ; const Store = types . model ( { todos : types . map ( Todo ) } ) . views ( ( self ) => { function getActualAmount ( ) { return self . todos . size ; } return { get amount ( ) { return getActualAmount ( ) ; } , getAmount ( ) { return self . todos . size + getActualAmount ( ) ; } } ; } ) . actions ( ( self ) => { function setAmount ( ) { const x = self . todos . size + self . amount + self . getAmount ( ) ; } return { setAmount } ; } ) ; expect ( true ) . toBe ( true ) ; } ) ; if ( process . env . NODE_ENV !== "production" ) { test ( "it should provide detailed reasons why the value is not appicable" , ( ) => { const Todo = types . model ( { title : types . string } ) . actions ( ( self ) => { function setTitle ( v ) { } return { setTitle } ; } ) ; const Store = types . model ( { todos : types . map ( Todo ) } ) . views ( ( self ) => ( { get amount ( ) { return self . todos . size ; } , getAmount ( ) { return self . todos . size + self . todos . size ; } } ) ) . actions ( ( self ) => { function setAmount ( ) { const x = self . todos . size + self . amount + self . getAmount ( ) ; } return { setAmount } ; } ) ; expect ( ( ) => Store . create ( { todos : { "1" : { title : true , setTitle : "hello" } } , amount : 1 , getAmount : "hello" } as any ) ) . toThrowError ( `[mobx-state-tree] Error while converting \`{"todos":{"1":{"title":true,"setTitle":"hello"}},"amount":1,"getAmount":"hello"}\` to \`AnonymousModel\`:

    at path "/todos/1/title" value \`true\` is not assignable to type: \`string\` (Value is not a string).` ) ; } ) ; } test ( "it should type compose correctly" , ( ) => { const Car = types . model ( { wheels : 3 } ) . actions ( ( self ) => { let connection = null as any as Promise < any > ; function drive ( ) { } function afterCreate ( ) { connection = Promise . resolve ( true ) ; } return { drive , afterCreate } ; } ) ; const Logger = types . model ( { logNode : "test" } ) . actions ( ( self ) => { function log ( msg ) { } return { log } ; } ) ; const LoggableCar = types . compose ( Car , Logger ) ; const x = LoggableCar . create ( { wheels : 3 , logNode : "test" } ) ; x . drive ( ) ; x . log ( "z" ) ; } ) ; test ( "it should extend {pre,post}ProcessSnapshot on compose" , ( ) => { const CompositionTracker = types . model ( { composedOf : types . array ( types . string ) , composedWith : types . array ( types . string ) } ) . preProcessSnapshot ( ( snapshot ) => ( { ... snapshot , composedOf : ( snapshot . composedOf || [ ] ) . concat ( "CompositionTracker" ) } ) ) . postProcessSnapshot ( ( snapshot ) => ( { ... snapshot , composedWith : ( snapshot . composedWith || [ ] ) . concat ( "WagonTracker" ) } ) ) ; const Car = types . model ( { } ) . preProcessSnapshot ( ( snapshot ) => ( { ... snapshot , composedOf : ( ( snapshot as any ) . composedOf || [ ] ) . concat ( "Car" ) } ) ) . postProcessSnapshot ( ( snapshot ) => ( { ... snapshot , composedWith : ( ( snapshot as any ) . composedWith || [ ] ) . concat ( "Wagon" ) } ) ) ; const Logger = types . model ( { } ) . preProcessSnapshot ( ( snapshot ) => ( { ... snapshot , composedOf : ( ( snapshot as any ) . composedOf || [ ] ) . concat ( "CarLogger" ) } ) ) . postProcessSnapshot ( ( snapshot ) => ( { ... snapshot , composedWith : ( ( snapshot as any ) . composedWith || [ ] ) . concat ( "WagonLogger" ) } ) ) ; const LoggableCar = types . compose ( CompositionTracker , Car , Logger ) . props ( { composedOf : types . array ( types . string ) , composedWith : types . array ( types . string ) } ) ; const x = LoggableCar . create ( { } ) ; expect ( x . composedOf ) . toContain ( "CompositionTracker" ) ; expect ( x . composedOf ) . toContain ( "Car" ) ; expect ( x . composedOf ) . toContain ( "CarLogger" ) ; expect ( x . composedOf ) . toEqual ( [ "CompositionTracker" , "Car" , "CarLogger" ] ) ; expect ( getSnapshot ( x ) . composedWith ) . toContain ( "WagonTracker" ) ; expect ( getSnapshot ( x ) . composedWith ) . toContain ( "Wagon" ) ; expect ( getSnapshot ( x ) . composedWith ) . toContain ( "WagonLogger" ) ; expect ( getSnapshot ( x ) . composedWith ) . toEqual ( [ "WagonTracker" , "Wagon" , "WagonLogger" ] ) ; } ) ; test ( "it should extend types correctly" , ( ) => { const Car = types . model ( { wheels : 3 } ) . actions ( ( self ) => { function drive ( ) { } return { drive } ; } ) ; const Logger = types . model ( "Logger" ) . props ( { logNode : "test" } ) . actions ( ( self ) => { let connection ; return { log ( msg ) { } , afterCreate ( ) { connection = Promise . resolve ( true ) ; } } ; } ) ; const LoggableCar = types . compose ( "LoggableCar" , Car , Logger ) ; const x = LoggableCar . create ( { wheels : 3 , logNode : "test" } ) ; x . drive ( ) ; x . log ( "z" ) ; } ) ; test ( "self referring views" , ( ) => { const Car = types . model ( { x : 3 } ) . views ( ( self ) => { const views = { get tripple ( ) { return self . x + views . double ; } , get double ( ) { return self . x * 2 ; } } ; return views ; } ) ; expect ( Car . create ( ) . tripple ) . toBe ( 9 ) ; } ) ; test ( "#922" , ( ) => { expect ( ( ) => { const Stateable = types . model ( "Statable" , { state : types . optional ( types . enumeration ( "state" , [ "initalized" , "pending" , "done" , "error" ] ) , "initalized" ) } ) ; const Client = types . model ( "Client" , { id : types . identifierNumber , name : types . string } ) ; const UserClientList = types . compose ( "UserClientList" , Stateable , types . model ( { items : types . array ( Client ) , month : types . optional ( types . Date , ( ) => { return new Date ( ) ; } ) } ) ) ; const NonExtendedUserClientList = types . model ( "NonExtendedUserClientList" , { items : types . array ( Client ) , month : types . optional ( types . Date , ( ) => { return new Date ( ) ; } ) , state : types . optional ( types . enumeration ( "state" , [ "initalized" , "pending" , "done" , "error" ] ) , "initalized" ) } ) ; const User = types . model ( "User" , { name : types . string , clients : types . optional ( UserClientList , ( ) => UserClientList . create ( { } ) ) } ) ; const NonExtendedUser = types . model ( "User" , { name : types . string , clients : types . optional ( NonExtendedUserClientList , ( ) => NonExtendedUserClientList . create ( { } ) ) } ) ; const you = NonExtendedUser . create ( { name : "you" } ) ; const me = User . create ( { name : "me" } ) ; } ) . not . toThrow ( ) ; } ) ; test ( "#922 - 2" , ( ) => { expect ( ( ) => { types . optional ( types . enumeration ( "state" , [ "init" , "pending" , "done" , "error" ] ) , "init" ) ; } ) . not . toThrow ( ) ; } ) ; test ( "#932" , ( ) => { interface MyInterface { test : string ; } const MyModel = types . model ( "MyModel" , { myField : types . array ( types . frozen < MyInterface > ( ) ) } ) ; const x = MyModel . create ( { myField : [ { test : "stuff" } ] } ) ; const a = x . myField [ 0 ] . test ; } ) ; test ( "932 - 2" , ( ) => { type MyType = string ; const ModelA = types . model ( "ModelA" , { myField : types . maybe ( types . frozen < MyType > ( ) ) } ) ; const x = ModelA . create ( { } ) ; const y = x . myField ; const ModelA2 = types . model ( "ModelA" , { myField : types . frozen < MyType > ( ) } ) ; const x2 = ModelA2 . create ( { myField : "test" } ) ; const y2 = x2 . myField ; } ) ; test ( "#923" , ( ) => { const Foo = types . model ( "Foo" , { name : types . optional ( types . string , "" ) } ) ; const Bar = types . model ( "Bar" , { foos : types . optional ( types . array ( Foo ) , [ ] ) } ) ; types . optional ( types . map ( Bar ) , { } ) ; } ) ; test ( "snapshot type of reference must be string | number" , ( ) => { const M = types . model ( { id : types . identifier , a : "bar" } ) ; const R = types . reference ( M ) ; const S = types . model ( { realM : M , refM : R } ) ; const s = S . create ( { realM : { id : "5" } , refM : "5" } ) ; const sn = getSnapshot ( s . refM ) ; } ) ; test ( "#951" , ( ) => { const C = types . model ( { a : 123 } ) ; const ModelWithC = types . model ( { c : C } ) ; const modelInstance = ModelWithC . create ( { c : C . create ( ) } ) ; const modelRoot1 = getRoot < typeof ModelWithC > ( modelInstance . c ) ; const modelCR1 = modelRoot1 . c ; const modelRoot2 = getRoot < Instance < typeof ModelWithC > > ( modelInstance . c ) ; const modelCR2 = modelRoot2 . c ; const modelParent1 = getParent < typeof ModelWithC > ( modelInstance . c ) ; const modelCP1 = modelParent1 ; const modelParent2 = getParent < Instance < typeof ModelWithC > > ( modelInstance . c ) ; const modelCP2 = modelParent2 ; const ArrayOfC = types . array ( C ) ; const arrayInstance = ArrayOfC . create ( [ C . create ( ) ] ) ; const arrayRoot1 = getRoot < typeof ArrayOfC > ( arrayInstance [ 0 ] ) ; const arrayCR1 = arrayRoot1 [ 0 ] ; const arrayParent1 = getParent < typeof ArrayOfC > ( arrayInstance [ 0 ] ) ; const arrayCP1 = arrayParent1 ; const MapOfC = types . map ( C ) ; const mapInstance = MapOfC . create ( { a : C . create ( ) } ) ; const mapRoot1 = getRoot < typeof MapOfC > ( mapInstance . get ( "a" ) ! ) ; const mapC1 = mapRoot1 . get ( "a" ) ! ; const mapParent1 = getRoot < typeof MapOfC > ( mapInstance . get ( "a" ) ! ) ; const mapCP1 = mapParent1 ; } ) ; test ( "cast and SnapshotOrInstance" , ( ) => { const NumberArray = types . array ( types . number ) ; const NumberMap = types . map ( types . number ) ; const A = types . model ( { n : 123 , n2 : types . number , arr : NumberArray , map : NumberMap } ) . actions ( ( self ) => ( { setN ( nn ) { self . n = cast ( nn ) ; } , setN2 ( nn ) { self . n = cast ( nn ) ; } , setN3 ( nn ) { self . n = cast ( nn ) ; } , setN4 ( nn ) { self . n = cast ( nn ) ; } , setN5 ( ) { self . n = cast ( 5 ) ; } , setArr ( nn ) { self . arr = cast ( nn ) ; } , setArr2 ( nn ) { self . arr = cast ( nn ) ; } , setArr3 ( nn ) { self . arr = cast ( nn ) ; } , setArr31 ( nn ) { self . arr = cast ( nn ) ; } , setArr4 ( ) { self . arr = cast ( [ 2 , 3 , 4 ] ) ; self . arr = cast ( NumberArray . create ( [ 2 , 3 , 4 ] ) ) ; } , setMap ( nn ) { self . map = cast ( nn ) ; } , setMap2 ( nn ) { self . map = cast ( nn ) ; } , setMap3 ( nn ) { self . map = cast ( nn ) ; } , setMap31 ( nn ) { self . map = cast ( nn ) ; } , setMap4 ( ) { self . map = cast ( { a : 2 , b : 3 } ) ; self . map = cast ( NumberMap . create ( { a : 2 , b : 3 } ) ) ; } } ) ) ; const C = types . model ( { a : A , maybeA : types . maybe ( A ) , maybeNullA : types . maybeNull ( A ) } ) . actions ( ( self ) => ( { setA ( na ) { self . a = cast ( na ) ; if ( 0 !== 0 ) { self . maybeA = cast ( na ) ; self . maybeNullA = cast ( na ) ; } } , setA2 ( na ) { self . a = cast ( na ) ; if ( 0 !== 0 ) { self . maybeA = cast ( na ) ; self . maybeNullA = cast ( na ) ; } } , setA3 ( na ) { self . a = cast ( na ) ; if ( 0 !== 0 ) { self . maybeA = cast ( na ) ; self . maybeNullA = cast ( na ) ; } } , setA4 ( na ) { self . a = cast ( na ) ; if ( 0 !== 0 ) { self . maybeA = cast ( na ) ; self . maybeNullA = cast ( na ) ; } } , setA5 ( ) { self . a = cast ( { n2 : 5 } ) ; self . a = cast ( A . create ( { n2 : 5 } ) ) ; if ( 0 !== 0 ) { self . maybeA = cast ( { n2 : 5 } ) ; self . maybeA = cast ( A . create ( { n2 : 5 } ) ) ; self . maybeNullA = cast ( { n2 : 5 } ) ; self . maybeNullA = cast ( A . create ( { n2 : 5 } ) ) ; } } } ) ) ; const c = C . create ( { a : { n2 : 5 } } ) ; unprotect ( c ) ; c . setA ( { n2 : 5 } ) ; c . setA ( A . create ( { n2 : 5 } ) ) ; c . setA2 ( { n2 : 5 } ) ; c . setA2 ( A . create ( { n2 : 5 } ) ) ; c . setA3 ( { n2 : 5 } ) ; c . setA4 ( A . create ( { n2 : 5 } ) ) ; c . setA5 ( ) ; c . a . setN ( 1 ) ; c . a . setN2 ( 1 ) ; c . a . setN3 ( 1 ) ; c . a . setN4 ( 1 ) ; c . a . setN5 ( ) ; c . a . setArr ( [ ] ) ; c . a . setArr ( NumberArray . create ( [ ] ) ) ; c . a . setArr2 ( [ ] ) ; c . a . setArr2 ( NumberArray . create ( [ ] ) ) ; c . a . setArr3 ( [ ] ) ; c . a . setArr3 ( NumberArray . create ( [ ] ) ) ; c . a . setArr4 ( ) ; c . a . setMap ( { a : 2 , b : 3 } ) ; c . a . setMap ( NumberMap . create ( { a : 2 , b : 3 } ) ) ; c . a . setMap2 ( { a : 2 , b : 3 } ) ; c . a . setMap2 ( NumberMap . create ( { a : 2 , b : 3 } ) ) ; c . a . setMap3 ( { a : 2 , b : 3 } ) ; c . a . setMap4 ( ) ; const arr = types . array ( A ) . create ( ) ; unprotect ( arr ) ; arr [ 0 ] = cast ( { n2 : 5 } ) ; const map = types . map ( A ) . create ( ) ; unprotect ( map ) ; map . set ( "a" , cast ( { n2 : 5 } ) ) ; } ) ; test ( "#994" , ( ) => { const Cinema = types . model ( "Cinema" , { id : types . identifier , name : types . maybe ( types . string ) } ) ; const ref = types . reference ( Cinema ) ; } ) ; test ( "castToSnapshot" , ( ) => { const firstModel = types . model ( { brew1 : types . map ( types . number ) } ) ; const secondModel = types . model ( { brew2 : types . map ( firstModel ) } ) . actions ( ( self ) => ( { do ( ) { } } ) ) ; const appMod = types . model ( { aaa : secondModel } ) ; const storeSnapshot = { brew2 : { outside : { brew1 : { inner : 222 } } } } ; const storeInstance = secondModel . create ( storeSnapshot ) ; const storeSnapshotOrInstance1 = secondModel . create ( storeSnapshot ) ; const storeSnapshotOrInstance2 = storeSnapshot ; appMod . create ( { aaa : castToSnapshot ( storeInstance ) } ) ; appMod . create ( { aaa : castToSnapshot ( storeSnapshot ) } ) ; appMod . create ( { aaa : castToSnapshot ( storeSnapshotOrInstance1 ) } ) ; appMod . create ( { aaa : castToSnapshot ( storeSnapshotOrInstance2 ) } ) ; } ) ; test . skip ( "create correctly chooses if the snapshot is needed or not - #920" , ( ) => { const X = types . model ( { test : types . string } ) ; const T = types . model ( { test : types . refinement ( X , ( s ) => s . test . length > 5 ) } ) ; T . create ( { test : { test : "hellothere" } } ) ; const T2 = types . model ( { test : types . maybe ( X ) } ) ; T2 . create ( ) ; T2 . create ( { } ) ; const A = types . model ( { test : "bla" } ) ; A . create ( ) ; A . create ( { } ) ; const B = types . array ( types . string ) ; B . create ( ) ; B . create ( [ "hi" ] ) ; const C = types . map ( types . string ) ; C . create ( ) ; C . create ( { hi : "hi" } ) ; const D = types . number ; D . create ( 5 ) ; const E = types . optional ( types . number , 5 ) ; E . create ( ) ; E . create ( 6 ) ; const F = types . frozen < number > ( ) ; F . create ( 6 ) ; const FF = types . frozen < number | undefined > ( ) ; FF . create ( ) ; FF . create ( undefined ) ; const G = types . frozen ( 5 ) ; G . create ( ) ; G . create ( 6 ) ; const H = types . frozen < any > ( 5 ) ; H . create ( ) ; H . create ( 6 ) ; const I = types . optional ( types . frozen < number > ( ) , 6 ) ; I . create ( ) ; I . create ( 7 ) ; } ) ; test ( "#1117" , ( ) => { const Failsafe = < C , S , T > ( t , handleProblem = ( value , error ) => { console . error ( "Skipping value: typecheck error on" , value ) ; console . error ( error ) ; } ) => types . custom < C , T | null > ( { name : `Failsafe<${ t . name }>` , fromSnapshot ( snapshot ) { try { return t . create ( snapshot ) ; } catch ( e ) { handleProblem ( snapshot , e as any ) ; return null ; } } , toSnapshot ( x ) { if ( isStateTreeNode ( x ) ) return getSnapshot ( x ) ; return x as any as C ; } , isTargetType ( v ) { if ( isFrozenType ( t ) ) { return t . is ( v ) ; } return false ; } , getValidationMessage ( ) { return "" ; } } ) ; } ) ; test ( "MST array type should be assignable to plain array type" , ( ) => { { const Todo = types . model ( { done : false , name : types . string } ) . actions ( ( self ) => ( { toggleDone ( ) { self . done = ! self . done ; } } ) ) ; const TodoArray = types . array ( Todo ) ; const todoArray = TodoArray . create ( [ { done : true , name : "todo1" } , { name : "todo2" } ] ) ; unprotect ( todoArray ) ; const otherTodoArray = todoArray ; otherTodoArray . push ( cast ( { done : false , name : "todo2" } ) ) ; } { const T = types . model ( { a : types . optional ( types . array ( types . number ) , [ ] ) } ) ; const arr = T . create ( ) . a ; } { const T = types . model ( { a : types . optional ( types . array ( types . number ) , [ ] , [ 5 ] ) } ) ; const arr = T . create ( { a : 5 } ) . 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 = T ; } ) ; test ( "#1268" , ( ) => { const Book = types . model ( { id : types . identifier } ) ; const BooksStore = types . model ( { books : types . array ( types . reference ( Book ) ) } ) ; const RootStore = types . model ( { booksStore : BooksStore } ) ; const booksStore = BooksStore . create ( { books : [ ] } ) ; const rootStore = RootStore . create ( { booksStore : castToSnapshot ( booksStore ) } ) ; } ) ; test ( "#1307 optional can be omitted in .create" , ( ) => { const Model1 = types . model ( { name : types . optional ( types . string , "" ) } ) ; const model1 = Model1 . create ( { } ) ; assert ( model1 . name , _ as string ) ; const Model2 = types . model ( { name : "" } ) ; const model2 = Model2 . create ( { } ) ; assert ( model2 . name , _ as string ) ; } ) ; test ( "#1307 custom types failing" , ( ) => { const createCustomType = < ICustomType extends ModelPrimitive | IAnyType > ( { CustomType } ) => { return types . model ( "Example" , { someProp : types . boolean , someType : CustomType } ) . views ( ( self ) => ( { get isSomePropTrue ( ) : boolean { return self . someProp ; } } ) ) ; } ; } ) ; test ( "#1343" , ( ) => { function createTypeA < T extends ModelPropertiesDeclaration > ( t ) { return types . model ( "TypeA" , t ) . views ( ( self ) => ( { get someView ( ) { return null ; } } ) ) ; } function createTypeB < T extends ModelPropertiesDeclaration > ( t ) { return types . model ( "TypeB" , { a : createTypeA ( t ) } ) . views ( ( self ) => ( { get someViewFromA ( ) { return self . a . someView ; } } ) ) ; } } ) ; test ( "#1330" , ( ) => { const ChildStore = types . model ( "ChildStore" , { foo : types . string , bar : types . boolean } ) . views ( ( self ) => ( { get root ( ) : IRootStore { return getRoot < IRootStore > ( self ) ; } } ) ) . actions ( ( self ) => ( { test ( ) { const { childStore } = self . root ; console . log ( childStore . foo ) ; } } ) ) ; interface IRootStore extends Instance < typeof RootStore > { } const RootStore = types . model ( "RootStore" , { childStore : ChildStore , test : "" } ) ; assert ( RootStore . create ( { childStore : { foo : "a" , bar : true } } ) . childStore . root . test , _ as string ) ; } ) ; test ( "maybe / optional type inference verification" , ( ) => { const T = types . model ( { a : types . string , b : "test" , c : types . maybe ( types . string ) , d : types . maybeNull ( types . string ) , e : types . optional ( types . string , "test" ) } ) ; interface ITC extends SnapshotIn < typeof T > { } interface ITS extends SnapshotOut < typeof T > { } assert ( _ as ITC , _ as { [ $nonEmptyObject ] ? : any ; a : string ; b ? : string ; c ? : string | undefined ; d ? : string | null ; e ? : string ; } ) ; assert ( _ as ITS , _ as { [ $nonEmptyObject ] ? : any ; a : string ; b : string ; c : string | undefined ; d : string | null ; e : string ; } ) ; } ) ;