All Projects → scurker → Preact And Typescript

scurker / Preact And Typescript

Licence: mit
Some simple patterns for Typescript usage with Preact

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to Preact And Typescript

Preact Render Spy
Render preact components with access to the produced virtual dom for testing.
Stars: ✭ 178 (+40.16%)
Mutual labels:  jsx, preact
Preact Www
📖 Preact documentation website.
Stars: ✭ 272 (+114.17%)
Mutual labels:  documentation, preact
X0
Document & develop React components without breaking a sweat
Stars: ✭ 1,706 (+1243.31%)
Mutual labels:  documentation, jsx
Omil
📝Webpack loader for Omi.js React.js and Rax.js components 基于 Omi.js,React.js 和 Rax.js 单文件组件的webpack模块加载器
Stars: ✭ 140 (+10.24%)
Mutual labels:  jsx, preact
Nerv
A blazing fast React alternative, compatible with IE8 and React 16.
Stars: ✭ 5,409 (+4159.06%)
Mutual labels:  jsx, preact
Omi
Front End Cross-Frameworks Framework - 前端跨框架跨平台框架
Stars: ✭ 12,153 (+9469.29%)
Mutual labels:  jsx, preact
11tyby
Simple 11ty setup using TypeScript, SASS, Preact with partial hydration, and other useful things. Aims to provide the DX of Gatsby, but using 11ty!
Stars: ✭ 38 (-70.08%)
Mutual labels:  preact, jsx
snap-state
State management in a snap 👌
Stars: ✭ 23 (-81.89%)
Mutual labels:  preact, jsx
Vhtml
Render JSX/Hyperscript to HTML strings, without VDOM 🌈
Stars: ✭ 556 (+337.8%)
Mutual labels:  jsx, preact
Awesome Preact
A curated list of amazingly awesome things regarding Preact ecosystem 🌟
Stars: ✭ 546 (+329.92%)
Mutual labels:  jsx, preact
Virtual Dom
关于Vue,React,Preact和Omi等框架源码的解读
Stars: ✭ 139 (+9.45%)
Mutual labels:  jsx, preact
Kit
Tools for developing, documenting, and testing React component libraries
Stars: ✭ 1,201 (+845.67%)
Mutual labels:  documentation, jsx
Redux React Starter
DEPRECATED use the new https://github.com/didierfranc/react-webpack-4
Stars: ✭ 137 (+7.87%)
Mutual labels:  jsx, preact
Preact Markup
⚡️ Render HTML5 as VDOM, with Components as Custom Elements!
Stars: ✭ 167 (+31.5%)
Mutual labels:  jsx, preact
Preact Minimal
🚀 Minimal preact structure
Stars: ✭ 136 (+7.09%)
Mutual labels:  jsx, preact
Styled Components Website
The styled-components website and documentation
Stars: ✭ 513 (+303.94%)
Mutual labels:  documentation, preact
Preact
⚛️ Fast 3kB React alternative with the same modern API. Components & Virtual DOM.
Stars: ✭ 30,527 (+23937.01%)
Mutual labels:  jsx, preact
Dataformsjs
🌟 DataFormsJS 🌟 A minimal JavaScript Framework and standalone React and Web Components for rapid development of high quality websites and single page applications.
Stars: ✭ 95 (-25.2%)
Mutual labels:  jsx, preact
Operator Mono Atom
Free Operator Mono clone for Atom
Stars: ✭ 120 (-5.51%)
Mutual labels:  jsx
Mulesoft Docs
Main MuleSoft documentation repository
Stars: ✭ 122 (-3.94%)
Mutual labels:  documentation

Preact and Typescript

Some simple examples demonstrating how Typescript and Preact can work together. ❤️

Table of Contents

  1. Setup
    1. Install Dependencies
    2. Setup tsconfig
    3. Setup webpack config
  2. Stateful Components
  3. Functional Components
  4. Components With Children
  5. Higher Order Components (HOC)
  6. Extending HTML Attributes
  7. Rendering
  8. Custom Elements / Web Components

Setup

Install Dependencies

$ npm install --save preact typescript webpack ts-loader

tsconfig.json

{
  "compilerOptions": {
    "sourceMap": true,
    "module": "commonjs",
    "target": "es5",
    "jsx": "react",
    "jsxFactory": "h"
  },
  "include": [
    "./src/**/*.tsx",
    "./src/**/*.ts"
  ]
}

It's important to note the usage of jsx: "react" and jsxFactory: "h". There are several options available for jsx that determine how Typescript outputs JSX syntax. By default without jsxFactory: "h", Typescript will convert JSX tags to React.createElement(...) which won't work for Preact. Preact instead uses h as its JSX pragma, which you can read more about at WTF is JSX.

webpack.config.js

var path = require('path');

module.exports = {
  devtool: 'source-map',
  entry: './src/app',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'app.js'
  },
  resolve: {
    extensions: ['.ts', '.tsx']
  },
  module: {
    loaders: [
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        loaders: ['ts-loader']
      }
    ]
  }
}

TODO: document webpack config

Stateful Components

Stateful components are Preact components that use any combination of state and/or lifecycle methods.

import { h, Component } from 'preact';

export interface Props {
  value: string,
  optionalValue?: string
}

export interface State {
  useOptional: boolean
}

export default class StatefulComponent extends Component<Props, State> {

  constructor() {
    super();
    this.state = {
      useOptional: false
    };
  }

  componentWillMount() {
    fetch('/some/api/')
      .then((response: any) => response.json())
      .then(({ useOptional }) => this.setState({ useOptional }));
  }

  render({ value, optionalValue }: Props, { useOptional }: State) {
    return (
      <div>{value} {useOptional ? optionalValue : null}</div>
    );
  }

}

Example Usage

<StatefulComponent /> // throws error, property "value" is missing
<StatefulComponent value="foo" /> // ok
<StatefulComponent value="foo" optionalValue={true} /> // throws error, value "true" is not assignable to type string

Functional Components

Functional components are components do not use state and are simple javascript functions accepting props and returns a single JSX/VDOM element.

import { h } from 'preact';

export interface Props {
  value: string,
  optionalValue?: string
}

export default function SomeFunctionalComponent({ value, optionalValue }: Props) {
  return (
    <div>
      {value} {optionalValue}
    </div>
  );
}

Example Usage

<SomeFunctionalComponent /> // throws error, property "value" is missing
<SomeFunctionalComponent value="foo" /> // ok
<SomeFunctionalComponent value="foo" optionalValue={true} /> // throws error, value "true" is not assignable to type string

You can also use named variables with FunctionalComponent<Props>.

import { h, FunctionalComponent } from 'preact';

export interface Props {
  value: string,
  optionalValue?: string
}

export const SomeFunctionalComponent: FunctionalComponent<Props> = ({ value, optionalValue }: Props) => {
  return (
    <div>
      {value} {optionalValue}
    </div>
  );
};

Components with Children

It's likely at some point you will want to nest elements, and with Typescript you can validate that any children props are valid JSX elements.

import { h } from 'preact';

export interface Props {
  children?: JSX.Element[]
}

export function A() {
  return <div></div>;
}

export function B() {
  return 'foo';
}

export default function ComponentWithChildren({ children }: Props) {
  return (
    <div>
      {children}
    </div>
  )
}

Example Usage

<ComponentWithChildren /> // ok

// ok
<ComponentWithChildren>
  <span></span>
</ComponentWithChildren>

// ok
<ComponentWithChildren>
  <A />
</ComponentWithChildren>

// throws error, not a valid JSX Element
<ComponentWithChildren>
  <B />
</ComponentWithChildren>

Higher Order Components (HOC)

Using Higher Order Components (HOC) allows for certain component logic to be reused and is a natural pattern for compositional components. An HOC is simply a function that takes a component and returns that component.

// app/hoc.tsx

import { h, AnyComponent, Component } from 'preact';

export interface Props {
  email: string
}

export interface State {
  firstName: string,
  lastName: string
}

export default function HOC<P extends Props>(SomeComponent: AnyComponent<any, any>) {
  return class extends Component<P, State> {
    componentDidMount() {
      let { email } = this.props;
      fetch(`/user/${email}`)
        .then((response: any) => response.json())
        .then(({ firstName, lastName }) => this.setState({ firstName, lastName }))
    }

    render(props, state) {
      return <SomeComponent {...props} {...state} />;
    }
  }
}

Then, you can wrap your components with your HOC simplifying view logic to potentially only props.

// app/component.tsx

import { h } from 'preact';
import HOC from './hoc';

export interface Props {
  firstName: string,
  lastName: string,
  email: string
}

function FunctionalUserComponent({ firstName, lastName, email }: Props) {
  return <div>{firstName} {lastName}: { email }</div>
}

export default HOC<{ email: string }>(FunctionalUserComponent);

Example Usage

// import FunctionalUserComponent from './app/component'

<FunctionalUserComponent /> // throws error, property "email" is missing
<FunctionalUserComponent email="[email protected]" /> // ok, first and last names are resolved by the HOC

Extending HTML Attributes

If you have a component that passes down unknown HTML attributes using, you can extend JSX.HtmlAttributes to allow any valid HTML attribute for your component.

import { h, Component } from 'preact';

export interface Props {
  value?: string
}

export default class ComponentWithHtmlAttributes extends Component<JSX.HtmlAttributes & Props, any> {
  render({ value, ...otherProps }: Props, {}: any) {
    return (
      <div {...otherProps}>{value}</div>
    );
  }
}

Example Usage

<ComponentWithHtmlAttributes /> // ok
<ComponentWithHtmlAttributes class="foo" /> // ok, valid html attribute
<ComponentWithHtmlAttributes foo="bar" /> // throws error, invalid html attribute

Rendering

Rendering components requires an Element. The second argument will append your component to your node:

import { h, render } from 'preact';
import MyComponent from './MyComponent';

render(<MyComponent />, document.body as Element);

Additionally, you can choose to replace a specific node by specifying a third argument:

import { h, render } from 'preact';
import MyComponent from './MyComponent';

const node = document.getElementById('root') as Element;
render(<MyComponent />, node, node.firstElementChild as Element);

Custom Elements / Web Components

With vanilla JSX in preact, you can create any element with whatever name you want and it'll get transpiled to h('my-element-name', ...). However Typescript is more opinionated and will complain if an imported component or HTML element does not exist with that name. Normally, this is what you would want with Preact components but custom elements may or may not be imported into your JSX files.

For Typescript there is a special interface JSX.IntrinsicElements available where you can define additional elements for Typescript to include. As a bonus, you can define any custom attributes as properties gaining additional type safety for custom elements in JSX!

typings.d.ts

export interface MyCustomElementProps {
  value: string,
  optionalValue?: string
}

declare module JSX {
  interface IntrinsicElements {
    "my-custom-element": MyCustomElementProps
  }
}

Example Usage

<my-custom-element /> // throws error, property "value" is missing
<my-custom-element value="foo" /> // ok
<my-custom-element value="foo" optionalValue={true} /> // throws error, value "true" is not assignable to type string
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].