Skip to content

Add typings #201

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

Merged
merged 8 commits into from
Feb 27, 2018
Merged

Add typings #201

merged 8 commits into from
Feb 27, 2018

Conversation

rikuba
Copy link
Contributor

@rikuba rikuba commented Feb 23, 2018

ref #187

@coveralls
Copy link

coveralls commented Feb 23, 2018

Coverage Status

Coverage remained the same at ?% when pulling caeaa0a on rikuba:add-typings into b80507d on WebReflection:master.

@WebReflection
Copy link
Owner

Hey @rikuba this is pretty AWESOME, thanks !!!

You just reminded me I had in mind to try with some nonsense like the following one (I really have no idea how to describe types in TypeScript, sorry)

declare class Component {
  html?: IWiredTag;
  svg?: IWiredTag;
  render(...args:any[]): IWiredTag;
  state: object;
  get defaultState(): object;
  setState(state:object, doNotRender?:boolean): Component;
  handleEvent(event:Event): void;
}

declare class Wire {
  constructor(childNodes: [HTMLElement|Text, ...HTMLElement[] | ...Text[], HTMLElement|Text]);
  insert(): DocumentFragment;
  remove(): HTMLElement|Text;
}

declare function hyperHTML(node: HTMLElement | SVGElement): IBoundTag;
declare function hyperHTML(identity?: object): IWiredTag;
declare function hyperHTML(identity: object, type?:'html|svg'): IWiredTag;
declare function hyperHTML(identity: object, id?:':any-id'): IWiredTag;
declare function hyperHTML(identity: object, type_id?:'html:any-id'): IWiredTag;

interface hyperHTML {
  bind(node: HTMLElement | SVGElement): IBoundTag;
  define(intent: string, callback: Function):void;
  wire(identity?: object): IWiredTag;
  wire(identity: object, type?:'html|svg'): IWiredTag;
  wire(identity: object, id?:':any-id'): IWiredTag;
  wire(identity: object, type_id?:'html:any-id'): IWiredTag;
  hyper(node: HTMLElement | SVGElement): IBoundTag;
  hyper(identity?: object): IWiredTag;
  hyper(identity: object, type?:'html|svg'): IWiredTag;
  hyper(identity: object, id?:':any-id'): IWiredTag;
  hyper(identity: object, type_id?:'html:any-id'): IWiredTag;
}

export interface IBoundTag {
  (template:string[], ...interpolations:any[]): HTMLElement | SVGElement | Text | Wire;
}

export interface IWiredTag {
  (template:string[], ...interpolations:any[]): HTMLElement | SVGElement | Text | Wire;
}

You have definitively a better way to describe this library but I wonder if:

  • you could remove hyperHTML.diff. I know it's exported but using a different engine is something I don't feel to comfortable documenting yet because it'd be a support nightmare since it's complicated (and 99.9% users won't ever need to do that)
  • you could find a way to better describe overloads with type and / or id like I've tried to do with my ugly attempt
  • could somehow define defaultState as a getter (others are lazy properties so it's fine)
  • could somehow define BoundTemplateFunction and WiredTemplateFunction
  • if possible, define that both template functions once executed return Element | Wire (you see the class there) but Wire is actually internal, not really exposed so ... maybe Element would be enough

Thanks a lot for this collaboration, please ask me anything you want !!!

@rikuba
Copy link
Contributor Author

rikuba commented Feb 23, 2018

Thank you for your reply.
I made some changes according to your advice.
But I could not fix a few things.

  • It seems that cannot define a getter in the declaration file, so I made Component::defaultState readonly
  • Since TypeScript does not support flexible restricted strings, I left type_id argument as string type
  • I feel that return type of WiredTemplateFunction should not be Element. When actually it is Wire but its type is displayed as Element, there may be some people trying to pass it to appendChild (and runtime error occured). So I left it as any

Please edit and commit freely.

@WebReflection
Copy link
Owner

Hi @rikuba , thanks again. Funny TypeScript has no way to define getters on the class level!!!

Anyway, the wired function returns Element most of the time but if there are multiple nodes at the same level It returns a specialised Wire instance.

You can pass the regular return as appendChild argument, you need to use .insert() to pass the Wire instance thought.

Maybe I should offer a valueOf() shortcut to insert() to those willing to append child returned values right away.

Thoughts ?

@rikuba
Copy link
Contributor Author

rikuba commented Feb 23, 2018

I felt that to offer Wire::valueOf is good idea. But Object::valueOf is defined as valueOf(): object; (in TypeScript lib), so return type of Element::valueOf is also object. Therefore, It becomes a slightly strange declaration (ValuableElement in below / there may be better name).

play

type TemplateFunction<T> = (template: TemplateStringsArray, ...values: any[]) => T;
type WiredTemplateFunction = TemplateFunction<ValuableElement | Wire>;

declare class ValuableElement extends Element {
  valueOf(): Element;
}

declare class Wire {
  valueOf(): DocumentFragment;
  // other properties should not be public?
}

export declare function wire(identity?: object | null, type?: 'html' | 'svg'): WiredTemplateFunction;
export declare function wire(identity?: object | null, type_id?: string): WiredTemplateFunction;

//

const elementOrWire = wire({})`<i></i><i></i>`; // this type is (ValuableElement | Wire)
const node = elementOrWire.valueOf(); // this type is (Element | DocumentFragment)
document.body.appendChild(node); // ok

@WebReflection
Copy link
Owner

WebReflection commented Feb 24, 2018

Hi @rikuba, I think for now we can keep any or use Element | Wire.

I like the seamless experience through valueOf but there is some subtle difference between a Wire and a node.

Example, like you said one might expect to append a Wire like that, but trying to remove it will throw errors because it's a fragment, not the list of nodes.

I rather have type WiredTemplateFunction = TemplateFunction<Element | Wire>; and explain what s a wire, when it's a wire, and how to insert or remove nodes.

These two are indeed the only methods in the Wire, and are used internally already.

declare class Wire {
  constructor(childNodes:Element[]);
  insert(): DocumentFragment;
  remove(): Element;
}

The childNodes is also an own property (there are others but not worth documenting) it's assigned during construction and it's granted to be a list of at least 2 Element, or more.

Inserting 2 or more elements requires a fragment, which is returned by insert(), removing them is optimised internally but the first, childNodes[0] Element, is returned so that the parent has to remove just one live node through regular methods.

TL;DR

const wired = wire()`<i/><i/>`; // 2 adjacent <i> elements
document.body.appendChild(wired.insert());
document.body.removeChild(wired.remove());

One last little thing
I am planning to change setState with the other PR about declarative components. It will accept a second, optional, boolean argument, which default is true.

If specified as false, t will not invoke the render method.

The method will also return this for chainability reasons, to simplify the usage in conjunction with declarative components.

Can you please put these info in too? Thank you!

edit just to clarify even more

setState(state: Partial<T> | ((this: this, state: T) => Partial<T>), render?: true): Component;

not sure when you return this you should just use the constructor as type or if there is an explicit self sort of thing.

@WebReflection
Copy link
Owner

Please don't hate me, one more thing:

Component.for(context:object, id?:any):Component is a public static method that returns an instance weakly related to context and optional id which could be even a primitive.

It's last part of the Component refactoring I'd love to type/describe too.

Then I'll merge, I promise 😃

hyper: typeof hyper;
wire: typeof wire;

// hyper(null, 'html')`HTML`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello,

first, thank you for documenting all this :) 👍 That will be very helpful!

I have a question about how to document things in TS. Guess the comments with simple double slash // are considered simple comments and comments with / ** text **/ right before a declaration are considered useful comments.

Example, if I change this line to something like this:

  /** hyper(null, 'html')`HTML` **/
  (identity: null | undefined, type?: 'html' | 'svg'): WiredTemplateFunction;

My editor says:
image

And that could be the first step on documenting each class, fields and fn. 😉

Cheers 🍺

Copy link
Owner

@WebReflection WebReflection Feb 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's pretty cool but I think you don't need double stars (indeed there is an extra star showing off after the comment) and apparently markdown is enabled so that backtick transforms `HTML` into code HTML.

I'd say for now let's just ship types, then I'll try to describe as best as I can the rest.

I don't want to bother anyone too much about this already awesome PR, cheers!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When this is merged, I'd be happy to contribute to the docs as well ;)

@WebReflection WebReflection merged commit 9a0b147 into WebReflection:master Feb 27, 2018
@WebReflection
Copy link
Owner

this went live into 2.6 with minor tweaks and I don't even know if I've done it right:
https://github.com/WebReflection/hyperHTML/blob/master/index.d.ts#L5-L13

Thank you for the patience and the effort, next up: document all of it properly !!!

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

Successfully merging this pull request may close these issues.

4 participants