-
Notifications
You must be signed in to change notification settings - Fork 781
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
Add Typing (DTS file) #311
Conversation
Codecov Report
@@ Coverage Diff @@
## master #311 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 2 2
Lines 148 148
Branches 46 46
=====================================
Hits 148 148 Continue to review full report at Codecov.
|
hyperapp.d.ts
Outdated
data?: Data, | ||
...children: VElement<{}>[] | ||
): VNode<Data> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Swizz Why is this repeated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Swizz Is there any way to declare that using the same definition? If it's impossible, then can we leave it out? The fact that children can be arguments
is an implementation detail needed by JSX, but users should definitely not use h
like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am looking for a way to express children could be an array ov VElements or the rest of args could be too.
In Snabbdom-pragma, I am using the following
type Children = VNode<{}>[] | VNode<{}> | string | number
type CircularChildren = Children | Children[]
export function h<Data>(
tag: Component<Data> | string,
data?: Data,
...children: CircularChildren
): VNode<Data>
CircularChildren
was about a quick fix regarding :
const arr = [
[<div>Hello1.1</div>, <div>Hello1.2</div>],
[<div>Hello2.1</div>, <div>Hello2.2</div>]
]
const vnode = <div> { arr } </div>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Swizz Can we just leave out the ...children
? h() supports ...children just because of JSX. If JSX didn't exist, h() would be always Array<VirtualNode>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more thing.
Can I use h()
with a number as children ?
plus: do you prefer the Array<Data>
notation instead of the Data[]
one ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know we could use both, but let's go on with Array<Data>
.
Can I use h() with a number as children ?
Yes, you can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only in h()
or we can tell a VirtualElement
could be either a VirtualNode
, a string
, or a number
.
The last one will affect :
h()
children parameterComponent
childrenVirtualNode
children
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hum.. Array<Data>
could be a little bit confusing.
h(
children: Array<VirtualElement<{}>>
)
h(
children: VirtualElement<{}>[]
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Swizz Why don't you just repeat VirtualNode | string
everywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work @Swizz! Can we work on renaming a few things? I was thinking instead of And add types for Note that actions allow nested actions so in flow:
Does that make sense? We'll also need an
Maybe also a definition for
|
hyperapp.d.ts
Outdated
|
||
// VNode part | ||
|
||
export interface VNode<Data> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Swizz Why does VNode<Data>
mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to @pspeter3 DTS for Picodom, I am pretty sure this is to allow to define the Props interface of a vnode. And to help for JSX 🎉
(VS Code is awesome regarding TypeScript)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, so this is only useful if the user defines an interface for the VirtualNode data? Hmm. Nice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes using :
const link = h('a', {})
Will do not raise anything. Thats why we are using VNode<{}>
as default. Instead of any
.
hyperapp.d.ts
Outdated
children: VElement<{}>[] | ||
} | ||
|
||
export type VElement<Data> = VNode<Data> | string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Swizz Is this possible:
export interface VNode<Data> {
tag: string
data?: Data
children: VNode<{}>[]
} | string
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You want a VNode to be either a VNode or a string. I never heard it was possible. Union is only possible on type.
I guess, it is right to use a type.
A VirtualNode is a VirtualNode
and a VirtualElement
could be either a VirtualNode
or a string
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just trying to remove the VirtualElement type, which is just confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not if you are thinking like the last sentence.
You cant tell a VirtualNode
could be a VirtualNode
or a string
, isnt it ?
But its easy to reason about a VirtualNode
and a VirtualElement
that could be either a VirtualNode
or a string
.
The VirtualNode
is the vdom thing. And the VirtualElement
is in most case a children. And a children could really be another VirtualNode
or a string
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, if it's always the children, why not call it VirtualNodeChild
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Swizz ☝️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now it is used as child, but I dont think it is good to limit it at this role by naming.
I do really think VirtualElement
is good. @pspeter3, did an awesome job there.
All credits goes to @pspeter3, seriously
Naming is open to discussion. I am just a bit curious, why is this a concern here and not in Picodom ?
Dont worry about it. As the DTS will grow faster, I want to move side by side. Now I want to discuss about VNode, JSX, and the app basis. Once done, I will move forward. |
Sounds good.
I didn't say picodom anywhere. EDIT: I do plan to change it in picodom as well. |
@jbucaran A giant plus DTS will be help to yield and suggest about types for VS Code user and atom-typescript user. So I plan to add JSDoc too. |
hyperapp.d.ts
Outdated
export function h<Data>( | ||
tag: Component<Data> | string, | ||
data?: Data, | ||
children?: VirtualElement<{}>[] | VirtualElement<{}> | number |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Swizz Can you make this just:
VirtualElement<{}>[]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
People using h
should use its best form (string, data, array of virtualnode).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without it TSC will yield an exception if I want to use h()
that way
h('div', null, h('span', null, 'Hi.'))
This simple thing will not be possible to
h('div', null, 'Hi.')
// ask for
h('div', null, ['Hi.'])
Do you really want to remove features by typing ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Swizz You should not use it that way IMO.
@lukejacksonn What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a compromise, one option is to add a JSDoc @deprecated comment to steer people away from this function, saying it only exists for JSX support and to use the array version for better perf.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andrewiggins Is the function info in vscode intellisense picked up from JSDoc comments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont know how this is a problem in fact. Performance ?
You are saying h('div', null, 'Hi.')
is less performant than h('div', null, ['Hi.'])
?
And what if I still want to use this feature like way, I will get typing/editor error/warning even if the function allow the use of it ?
I think this is very effortless to types it. And let users to choose you prefered way to do as both exist.
Let's write a performance warning into the doc and refer it in the JSdoc.
/** Description...
* @see {@link http://github.com/hyperapp/hyperapp/... | Performance issue}
*/
h(....
Discussed changes pushed, plus JSDoc ! Let's go to the Step 2 ! |
hyperapp.d.ts
Outdated
|
||
/** @namespace [Virtual DOM] */ | ||
|
||
/** The Virtual DOM representation of an Element |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In all the examples I've seen (and on the JSDoc website itself) it appears the first line of a multi-line doc comment goes on the line after /**
. It appears JavaDoc (which I assume JSDoc is inspired from) follows the same styling convention.
Not a big deal, but just thought I'd point it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is my first time with JSDoc to be honest. I have let VSCode guide me with
export interface VirtualNode<Data> { | ||
tag: string | ||
data: Data | ||
children: VirtualNodeChild<{} | null>[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reasoning for adding | null
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In strict mode null
need to be ensured, this is prerequisite if you are using : h('tag', null)
node
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could add a comment about that
hyperapp.d.ts
Outdated
(data?: Data, ...children: VirtualNodeChild<{} | null>[]): VirtualNode<Data> | ||
} | ||
|
||
/** The soft way to create a Virtual Node |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does "soft" mean? I would just state that h creates VirtualNodes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was looking for a way to indicate "you can create VirtualNode
without h
too, but using h
is the prefered way".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Virtual Node Factory?
hyperapp.d.ts
Outdated
*/ | ||
export type VirtualNodeChild<Data> = VirtualNode<Data> | string | ||
|
||
/** A Component is a function that return a custom Virtual Node |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd put this comment inside the interface, on top of the method definition, and add param documentation. It provides better intellisense in VSCode
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, I did it for Emit
.
hyperapp.d.ts
Outdated
* @memberOf [Virtual DOM] | ||
*/ | ||
export interface Component<Data> { | ||
(data?: Data, ...children: VirtualNodeChild<{} | null>[]): VirtualNode<Data> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the children args are spread out like this. I believe you just get a direct Array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Already fixed I missed to push this
hyperapp.d.ts
Outdated
|
||
/** @namespace [Application] */ | ||
|
||
export interface Emit<Data, Events> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we swap the order of these generic parameters to match the order of the function parameters?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to make a Generic parameter optional ? Because we need to give generic a type even if data is optional 😕
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup! You can specify a default type, which makes that generic param optional. For example:
interface SomeInterface<T, U=any>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome thank you !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a general rule, prefer {}
to any
. Using any
allows the consumer to call any property/method for U
vs {}
will allow the consumer to call none.
To maintain the quality of these typings over time I recommend we add some typings tests to the tests/ folder similar to what I did for picodom. It also makes it clearer to understand how to use the typings appropriately |
Thank you for your review @andrewiggins. Your help is a giant plus 👍 To be honest this is 50% laziness. But are you okay to write tests ? |
Absolutely! I'll copy what I started in picodom and expand it to include hyperapp features |
children?: | ||
| VirtualNodeChild<{} | null>[] | ||
| VirtualNodeChild<{} | null> | ||
| number |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are numbers converted to strings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, a h
children can be a number, and number will be converted to a string in the VirtualNode
representation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
h also supports array of numbers (all converted to strings) should it then be:
children?: Array<number | VirtualNodeChild<{} | null>> | VirtualNodeChild<{} | null> | number
After we merge this, doing one for the router would be great. |
DTS typing of Hyperapp is a Masterpiece. I think typing the Router will be a lot more easy. I am a little bit busy these days. I hope I'll push the step 2, within a week. |
@pspeter3 Is there a way to ensure all api function to implements ... ? export interface API<State, Actions, DataIn, DataOut> {
(state: State, actions: Actions, data: DataIn): DataOut
} |
@Swizz what API functions are you talking about? |
How can I install and try this? |
Quickest way : In Hyperapp repo git checkout -b Swizz-fresh-typing master
git pull git://github.com/Swizz/hyperapp.git fresh-typing
npm link Or clone my repository git clone -b fresh-typing git://github.com/Swizz/hyperapp.git hyperapp
cd hyperapp
npm link In you demo project, instead of installing hyperapp release use your local one
|
Thanks @Swizz but I'd rather not use links, as these wouldn't work for anyone else cloning the project. (would they?) According to npm docs, this should work:
It even displays the expected hash |
To be honest, I always using in current dev DTS that way. |
They would not work. Using the reference path is an outdated way to tell the compiler about type definitions. You can now using the typings key in tsconfig. |
@Swizz Can you please update this with the new API? :) |
Now synced with 0.12.0 🎉 |
@Swizz What else do we need to merge this? |
hyperapp.d.ts
Outdated
|
||
export function app<State, Actions, Events>(app: {}): Emit<{} | null, Events> | ||
|
||
/** @namespace [JSX] */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is there JSX typing in here? I didn't think we implement any of that within Hyperapp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's for the TypeScript compiler. It uses that to type check JSX when generating the equivalent JavaScript
hyperapp.d.ts
Outdated
export type ActionsResult<State> = | ||
| Partial<State> | ||
| Promise<Partial<State>> | ||
| Function // /!\ TODO: Type the thunk Function |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mission Control: I believe we need a Thunk type.
hyperapp.d.ts
Outdated
} | ||
|
||
export interface View<State, Actions> { | ||
(state: State, actions: Actions): VirtualNode<{}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might want to add a comment describing this.
api.md will be useless after #311.
api.md will be useless after #311.
api.md will be useless after #311.
- Remove api.md and display type information next to topic. - Improve docs. - See also #311.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind, Emit copy pasta 😆
You can now, please, review the View and Actions part |
state?: State | ||
actions?: Actions | ||
events?: Events | ||
view?: View<State, Actions> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Swizz what about root and mixins?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is only minimal, for testing state
, actions
, events
and view
. Application typing will come later
hyperapp.d.ts
Outdated
export interface Events< | ||
State extends Hyperapp.State, | ||
Actions extends Hyperapp.Actions<State> | ||
> {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Swizz What about an event third data
argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not arguments but the generics. Here is no interface for Events yet
hyperapp.d.ts
Outdated
| void | ||
|
||
|
||
export type ActionsCallers< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For naming I'll let you choose
What can I help with here? |
* | ||
* @memberOf [Virtual DOM] | ||
*/ | ||
(data: Data, children: VirtualNodeChild<{} | null>[]): VirtualNode<Data> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be a good idea! We'd need to change it in app and h too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be complete, Angular refers to it as inputs
, although in Angular's case, it's impossible for an input not to also become state inside the component after it's received.
I'm also for renaming, especially since in Vue.js data
refers to state inside the component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Give the names you want to. I am really bad at naming stuff. I followed the API.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Swizz Looks good, but what about the tests? We can't merge without tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andrewiggins Are you always among us? Ready to help me about tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm here :) Checkout what I did in jorgebucaran/superfine#19 if you want a simple example of some tests. In summary, add some typescript files that demonstrates how you would expect the types to be used, and add a call to tsc
in the test npm script to make sure they compile correctly.
@Swizz Is this ready? I'm ready to take over now. 😄 |
There is a lot of commits... |
8010168
to
2db944f
Compare
🎆 |
Merged, merged, merged! 💥 🎉 |
- Remove api.md and display type information next to topic. - Improve docs. - See also #311.
This follows and closes #279
The first attemps of a DTS file was made with a lot of stuffs in minds.
I chosen to Revoke completely my last attempt, to propose a more sanitized one.
The main goal of this PR is now to move with iteration. To easily discuss about declarations.
First, I propose you to talk about the basics, and the VNode,
h()
stuffs.This declaration file, is 98% based on the Picodom one by @pspeter3 because the Vdom system is (almost) the same.
Here I have only overloaded
h()
and added JSX support. jorgebucaran/superfine#17 (comment)I have also added the
Emit
interface andapp
function as starting point.Bonus : Emit use @andrewiggins awesome `keyof Events` :
Steps