Skip to content
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

TypeScript definitions for 2.0.10 #1016

Merged
merged 16 commits into from
Apr 5, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
"homepage": "https://hyperapp.dev",
"author": "Jorge Bucaran",
"license": "MIT",
"files": [
"*.js*(.map)",
"types/index.d.ts"
],
"keywords": [
"framework",
"hyperapp",
Expand All @@ -21,10 +25,13 @@
"release": "tag=$(npm run --silent which) npm run deploy && cd ./${pkg:+packages/$pkg} && npm publish --access public",
"deploy": "npm test && git commit --all --message $tag && git tag --sign $tag --message $tag && git push && git push --tags",
"which": "node --print \"('$pkg' ? '@$npm_package_name/$pkg@' : '') + require('./${pkg:+packages/$pkg/}package').version\"",
"test": "c8 twist tests/*.js"
"test": "c8 twist tests/*.js",
"dts": "dtslint types"
},
"devDependencies": {
"dtslint": "*",
icylace marked this conversation as resolved.
Show resolved Hide resolved
"twist": "*",
"typescript": "*",
"c8": "*"
}
}
166 changes: 166 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Minimum TypeScript Version: 4.2

// NOTE: `dtslint` needs 4.2 even though these definitions should work with 4.1.

declare module "hyperapp" {
// `app()` initiates a Hyperapp application. `app()` along with runners and
// subscribers are the only places where side effects are allowed.
function app<S>(props: App<S>): void
icylace marked this conversation as resolved.
Show resolved Hide resolved

// `h()` builds a virtual DOM node.
function h<S, T extends string = string>(
// Tags cannot be empty strings.
tag: T extends "" ? never : T,
props: PropList<S>,
children?: VNode<S> | readonly VNode<S>[]
): VDOM<S>

// `memo()` stores a view along with data for it.
function memo<
S,
D extends string | any[] | Record<string, any>
>(
view: View<D>,
data: D
): VDOM<S>

// `text()` creates a virtual DOM node representing plain text.
function text<T, S>(
value: T extends (symbol | ((..._: any[]) => any)) ? never : T,
node?: Node
): VDOM<S>

// ---------------------------------------------------------------------------

// A Hyperapp application instance has an initial state and a base view.
// It must also be mounted over an available DOM element.
type App<S> = Readonly<{
init: StateFormat<S> | Action<S>
icylace marked this conversation as resolved.
Show resolved Hide resolved
view: View<S>
node: Node
subscriptions?: Subscriptions<S>
middleware?: Middleware<S>
icylace marked this conversation as resolved.
Show resolved Hide resolved
}>

// A view builds a virtual DOM node representation of the application state.
type View<S> = (state: State<S>) => VDOM<S>

// The subscriptions function manages a set of subscriptions.
type Subscriptions<S> = (state: State<S>) => Subscription<S>[]

// A subscription represents subscriber activity.
type Subscription<S, D = any> = boolean | undefined | SubscriberDescriptor<S, D> | Unsubscribe

// A subscriber reacts to subscription updates.
type SubscriberDescriptor<S, D> = [Subscriber<S, D>, Payload<D>]
icylace marked this conversation as resolved.
Show resolved Hide resolved
type Subscriber<S, D> = (dispatch: Dispatch<S>, props?: Payload<D>) => void | Unsubscribe

// An unsubscribe function cleans up a canceled subscription.
type Unsubscribe = () => void

// Middleware allows for custom processing during dispatching.
type Middleware<S> = (dispatch: Dispatch<S>) => Dispatch<S>

// ---------------------------------------------------------------------------

// A dispatched action handles an event in the context of the current state.
type Dispatch<S> = (action: Action<S>, props?: Payload<any>) => void

// An action transforms existing state and/or wraps another action.
type Action<S, P = any> = ActionTransform<S, P> | ActionDescriptor<S, P>
icylace marked this conversation as resolved.
Show resolved Hide resolved
type ActionTransform<S, P = any> = (state: State<S>, props?: Payload<P>) => StateFormat<S> | Action<S>
icylace marked this conversation as resolved.
Show resolved Hide resolved
icylace marked this conversation as resolved.
Show resolved Hide resolved
type ActionDescriptor<S, P> = [ActionTransform<S, P>, Payload<P>]

// A transform carries out the transition from one state to another.
type Transform<S, P = any> = (state: StateFormat<S>, props?: Payload<P>) => StateFormat<S>
icylace marked this conversation as resolved.
Show resolved Hide resolved

// State can either be on its own or associated with effects.
type StateFormat<S> = State<S> | StateWithEffects<S>

// Application state is accessible in every view, action, and subscription.
type State<S> = S

// State can be associated with a list of effects to run.
type StateWithEffects<S, D = any> = [State<S>, ...(Effect<S, D> | RunnerDescriptor<S, D>)[]]
icylace marked this conversation as resolved.
Show resolved Hide resolved

// An effect is an abstraction over an impure process.
type Effect<S, D = any> = (..._: any[]) => RunnerDescriptor<S, D>

// A runner is where side effects and any additional dispatching may occur.
type RunnerDescriptor<S, D = any> = [Runner<S, D>, Payload<D>]
icylace marked this conversation as resolved.
Show resolved Hide resolved
type Runner<S, D> = (dispatch: Dispatch<S>, props?: Payload<D>) => void | Promise<void>

// A payload is data given to an action, effect, or subscription.
type Payload<P> = P

// ---------------------------------------------------------------------------

// A virtual DOM node represents an actual DOM element.
type VDOM<S> = {
readonly type: VDOMNodeType
readonly props: PropList<S>
readonly children: VNode<S>[]
node: MaybeNode
readonly tag: Tag<S>
readonly key: Key
memo?: PropList<S>
events?: Record<string, Action<S>>

// `_VDOM` is a guard property which gives us a way to tell `VDOM` objects
// apart from `PropList` object.
_VDOM: true
icylace marked this conversation as resolved.
Show resolved Hide resolved
}

// These are based on actual DOM node types:
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
const enum VDOMNodeType { SSR = 1, Text = 3 }

// A virtual node is a convenience layer over a virtual DOM node.
type VNode<S> = boolean | null | undefined | VDOM<S>

// Actual DOM nodes get manipulated depending on how property patching goes.
type MaybeNode = null | undefined | Node

// A virtual DOM node's taghas metadata relevant to it. Virtual DOM nodes are
// tagged by their type to assist rendering.
type Tag<S> = string | View<S>

// A key can uniquely associate a virtual DOM node with a certain DOM element.
type Key = string | null | undefined

// Virtual DOM properties will often correspond to HTML attributes.
type PropList<S> = Readonly<ElementCreationOptions & EventActions<S> & {
[_: string]: unknown
class?: ClassProp
key?: Key
style?: StyleProp

// By disallowing `_VDOM` we ensure that values having the `VDOM` type are
// not mistaken for also having `PropList`.
_VDOM?: never
}>

// The `class` property represents an HTML class attribute string.
type ClassProp = boolean | string | undefined | Record<string, boolean | undefined> | ClassProp[]

// The `style` property represents inline CSS.
//
// NOTE: This relies on what TypeScript itself recognizes as valid CSS
// properties. Custom properties are not covered as well as any newer
// properties that are not yet recognized by TypeScript. Apparently,
// the only way to accommodate them is to relax the adherence to
// TypeScript's CSS property definitions. The trade-off doesn't
// seem worth it given the chances of using such properties.
// However, you can use type casting if you want to them.
type StyleProp
= { [K in keyof CSSStyleDeclaration]?: CSSStyleDeclaration[K] | null }
// Since strings are indexable we can avoid them by preventing indexing.
& { [_: number]: never }

// Event handlers are implemented using actions.
type EventActions<S> = { [K in keyof EventsMap]?: Action<S, EventsMap[K]> }
type EventsMap
= { [K in keyof HTMLElementEventMap as `on${K}`]: HTMLElementEventMap[K] }
& { [K in keyof WindowEventMap as `on${K}`]: WindowEventMap[K] }
& { onsearch: Event }
}
Loading