MithrilJS / mithril.d.ts

Types for mithril.js
MIT License
80 stars 11 forks source link

Update to UMD / Isomorphic style #9

Closed andraaspar closed 7 years ago

andraaspar commented 7 years ago

@spacejack Thank you for the invite!

The style we want to match is described in the TypeScript docs. Also some details in this comment.

This means we'll be splitting type declarations into multiple files, and organize them into a folder structure that matches Mithril's importable module paths. The ultimate goal is to have this on https://github.com/DefinitelyTyped/DefinitelyTyped.

I have found lodash to be an interesting example of this approach. They have all declarations in the index.d.ts, and they have minimal content in subfolders. Example:

index.d.ts

export = _;
export as namespace _;
declare var _: _.LoDashStatic;

declare namespace _ {
    interface LoDashStatic {
        toSafeInteger(value: any): number;
    }
    interface Dictionary<T> {
        [index: string]: T;
    }
}

toSafeInteger/index.d.ts

import { toSafeInteger } from "../index";
export = toSafeInteger;

Example use 1 (script)

var dict: _.Dictionary<string>
_.toSafeInteger(3.2);

Example use 2 (target ES5)

import jQuery from 'jquery-ts'

var dict: _.Dictionary<string>
_.toSafeInteger(3.2); // [ts] '_' refers to a UMD global, but the current file is a module. Consider adding an import instead.

Example use 3 (target ES5)

import jQuery from 'jquery-ts'
import * as _ from 'lodash'

var dict: _.Dictionary<string>
_.toSafeInteger(3.2); // Fixed

Example use 4 (target ES6)

import toSafeInteger = require('lodash/toSafeInteger') // [ts] Import assignment cannot be used when targeting ECMAScript 2015 modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
import * as toSafeInteger from 'lodash/toSafeInteger' // [ts] Module '"c:/Temp/test/node_modules/@types/lodash/toSafeInteger/index"' resolves to a non-module entity and cannot be imported using this construct.
import toSafeInteger from 'lodash/toSafeInteger' // [ts] Module '"c:/Temp/test/node_modules/@types/lodash/toSafeInteger/index"' has no default export.
import { toSafeInteger } from 'lodash/toSafeInteger' // [ts] Module '"c:/Temp/test/node_modules/@types/lodash/toSafeInteger/index"' resolves to a non-module entity and cannot be imported using this construct.

Oops. Maybe this approach is not ES6 compatible?

spacejack commented 7 years ago

@andraaspar Thank you for researching that. I like the lodash approach.

So in our case, hyperscript.d.ts could look like:

import { Hyperscript } from "./";
declare const h: Hyperscript;
export = h;

I think streams should be kept in a sub-folder but I'm not sure what the layout should be. Mithril has not yet decided how to package streams for script usage.

I'm going to be a little too busy to work on this much today, but hopefully will have some time on the weekend. Feel free to try out any module rearrangements if you have some time. :)

spacejack commented 7 years ago

I've added all the submodules to the improved branch and I've updated my old tests to compile with the new types style.

The streams are laid out as they are currently in the mithril repo.

Haven't actually tried using this in a project yet, but I'll try to get to that this week.

andraaspar commented 7 years ago

@spacejack You have applied the right solution. Adding a const to the module fixes the compile for ES6. Great!

spacejack commented 7 years ago

I've updated the readme in that branch with various component type examples.

Thinking about the FactoryComponent... I think we could omit the Vnode.state type from it since the point of it is to allow the closure to hold your state. So the FactoryComponent type would become:

export type FactoryComponent<A> = (vnode: Vnode<A, {}>) => Component<A, {}>

and usage would be:

const c = function c() {
    let count = 0
    return {
        view ({attrs: {title}}) {
            return [
                m('h2', title),
                m('p', 'Count: ' + count)
            ]
        }
    }
} as FactoryComponent<{title: string}>

The factory style is flexible, so you can still have a state type if you need it by using this style.

spacejack commented 7 years ago

I went ahead and made the change to FactoryComponent.

I've also added your tests to the test-api.ts.

andraaspar commented 7 years ago

@spacejack Sorry for the delay in answering, things were getting a bit busy around here.

It's looking good I think... The only weird bit is how we have ./stream/index.d.ts but ./hyperscript.d.ts. Maybe it should be ./stream.d.ts? (Or everything should have its own folder, and named index.d.ts...)

spacejack commented 7 years ago

I guess I thought about it differently because stream must be imported as a separate module (not included on m) unlike the rest of the sub modules.

andraaspar commented 7 years ago

True.

andraaspar commented 7 years ago

@spacejack Can you confirm that when using the improved branch in a different project, you can actually access the interfaces in the Mithril namespace? I have tried many things, on different computers, but it does not seem to work. Example:

import * as m from 'mithril'

var App: Mithril.Comp<{}, {}> = { // [ts] Cannot find namespace 'Mithril'.
    view() {
        return m('div', 'Hello World!')
    }
}
m.mount(document.body, App)

For reference, the lodash library has the same issue.

andraaspar commented 7 years ago

@spacejack Oh. It's all m.Comp now.

spacejack commented 7 years ago

Yeah, the readme in improved branch shows some example usage. So you can also import {Comp} from 'mithril'.