Skip to content

Proper typescript typings #64

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

Closed
mweststrate opened this issue Mar 23, 2017 · 10 comments
Closed

Proper typescript typings #64

mweststrate opened this issue Mar 23, 2017 · 10 comments

Comments

@mweststrate
Copy link
Member

mweststrate commented Mar 23, 2017

createFactory seems to be quite impossible to make strongly typed in Typescript, without mapped types (even when not taking into consideration the partial subtypes of snapshots)

Two sample playgrounds demonstrating some of the problems:

fails to reassign subfields

factories are not functions, but expose (possibly null) create method (note the create!())

Something like: microsoft/TypeScript#13257 could definitely help!

This is the best I could do so far:

type Factory<T> = T & { create?(): T }

function createFactory<T>(base: T): Factory<T> {
    return null as any
}

function primitive<T>(value: T): Factory<T> {
    return null as any
}

const A = createFactory({
    x: primitive(3 as number),
    y: primitive("boe" as string)
})

// factory is invokable
const a = A.create!()

// property can be used as proper type
const z: number = a.x

// property can be assigned to crrectly
a.x = 7

// wrong type cannot be assigned
a.x = "stuff" // Red is ok

// sub factories work
const B = createFactory({
    sub: A
})

const b = B.create!()

// sub fields have proper type
b.sub.x = 4
const d: string = b.sub.y

// sub fields can be reassigned
b.sub = A.create!()
@mweststrate mweststrate changed the title Property typescript typings Proper typescript typings Mar 23, 2017
@mweststrate
Copy link
Member Author

Hmm typewise the most safe approach is to let all factories only return their target type, and introduce a static create methods that instantiates a new instance given a factory (in a typesafe manner):

function createFactory<T>(base: T): T {
    return null as any
}

function primitive<T>(value: T): T {
    return null as any
}

const A = createFactory({
    x: primitive(3 as number),
    y: primitive("boe" as string)
})

function create<T>(f: T): T {
    return null as any
}

// factory is invokable
const a = create(A)

// property can be used as proper type
const z: number = a.x

// property can be assigned to crrectly
a.x = 7

// wrong type cannot be assigned
a.x = "stuff" // Red is ok

// sub factories work
const B = createFactory({
    sub: A
})

const b = create(B)

// sub fields have proper type
b.sub.x = 4
const d: string = b.sub.y

// sub fields can be reassigned
b.sub = create(A)

@mweststrate
Copy link
Member Author

@mattiamanzati
Copy link
Contributor

What about this?
It needs to deprecate creating object by calling the function, and call .create instead.
This way factories becames an object with a create property (and others).

export type IModel = {
    $treenode: any // Actually Node, but that should not be exposed to the public...
} & Object

export interface IFactory<S, T> {
    create(snapshot?: S, env?: Object): T & IModel
    factoryName: string,
    is: ITypeChecker
    isFactory: boolean // TODO: rename to isFactory
    type: IType
}

export interface IType {
    name: string
    is(thing: IModel | any): boolean
    create(snapshot, environment?): any
    factory: IFactory<any, any> // TODO type
    describe(): string
}

export type ITypeChecker = (value: IModel | any) => boolean

export type IBaseModelDefinition<S extends Object, T> = {[K in keyof T]: IFactory<any, T[K]> | T[K]}

export function createFactory<S extends Object, T extends S>(baseModel: IBaseModelDefinition<S, T>): IFactory<S, T>
export function createFactory<S extends Object, T extends S>(name: string, baseModel: IBaseModelDefinition<S, T>): IFactory<S, T>
export function createFactory(arg1, arg2?) {
    return null as any
}

const t_string: IFactory<string, string> = null as any
const t_number: IFactory<number, number> = null as any
const t_primitive = <T>(value: T) => T


const A = createFactory({
    x: t_primitive(3),
    y: t_primitive("boe")
})

// factory is invokable
const a = A.create()

// property can be used as proper type
const z: number = a.x

// property can be assigned to crrectly
a.x = 7

// wrong type cannot be assigned
a.x = "stuff" // Red is ok

// sub factories work
const B = createFactory({
    sub: A
})

const b = B.create()

// sub fields have proper type
b.sub.x = 4
const d: string = b.sub.y

// sub fields can be reassigned
b.sub = A.create()

@mquandalle
Copy link

@mweststrate Is the 0.2 API working with Typescript types?

On the below example I would expect to have access to the x and y properties but I can't get it working:

typescript mobx-state-tree 0.2

@mattiamanzati
Copy link
Contributor

Uhm, which TS version are you using @mquandalle ?

cattura

@mquandalle
Copy link

Currently, [email protected] but I think I tried with the latest stable version, 2.2, and had the same result; I'm trying again right now.

@mquandalle
Copy link

It still doesn't work for me with Typescript 2.2. Here is my yarn.lock file which provide the exact version of all of my dependencies in case there is a problem in that: https://gist.github.com/mquandalle/d4aa3072fe0056fb63ac604b09d68a53

Note that the above screenshot comes from my editor but I get corresponding errors (namely error TS2339: Property 'x' does not exist on type '{} & { $treenode?: MSTAdminisration | undefined; } & { toJSON(): Snapshot<{}>; }'.) in the compiler output, so there is probably a problem with the project dependencies and probably not with VSCode.

@mquandalle
Copy link

Yes

@mquandalle
Copy link

But then again, typescript fails to compile, so I doubt it's an issue with the editor. I see that I have the following error in the console:

Error in /home/mquandalle/react/myproject/node_modules/mobx-state-tree/lib/types/object.d.ts
(1,44): error TS2305: Module '"/home/mquandalle/react/myproject/node_modules/mobx/lib/mobx"' has no exported member 'IAction'.

@mquandalle
Copy link

Ok, the problem was with MobX version. Updating it fixed the issue, sorry for falsy report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants