GithubHelp home page GithubHelp logo

Comments (42)

gcanti avatar gcanti commented on August 12, 2024 4

Just pushed a candidate implementation (flow branch).

Despite being black magic, I'm really tempted because it's working extraordinarily well.

For example, adding the following simple file definition:

declare module 'tcomb' {

  declare interface Type<T> {
    (x: T): T;
    is(x: any): boolean;
  }

  declare interface $Refinement<P: (x: any) => boolean> {}

  declare var Integer: Type<number>;

}

I'm now able to do this:

import type { $Refinement } from 'tcomb'
import { Integer } from 'tcomb'

const p = n => n >= 2

type IntegerT = number & $Refinement<typeof Integer.is>;

type IntegerGreaterThan2 = IntegerT & $Refinement<typeof p>;

function foo(n: IntegerGreaterThan2) {
  return n
}

foo(2) // flow ok, tcomb ok
foo(1) // flow ok, tcomb throws [tcomb] Invalid value 1 supplied to n: IntegerGreaterThan2
foo(2.1) // flow ok, tcomb throws [tcomb] Invalid value 2.1 supplied to n: IntegerGreaterThan2
foo('a') // flow throws, tcomb throws

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024 3

Another proposal, validating (at runtime) the IO boundary using typecasts:

type User = { name: string };

export function getUser(userId: string): Promise<User> {
  return axios.get('...').then(p => (p: User)) // <= type cast
}

to

const User = t.interface({
  name: t.String
}, "User");


export function getUser(userId: string): Promise<User> {
  _assert(userId, t.String, "userId");

  const ret = function (userId) {
    return axios.get('...').then(p => {
      return _assert(p, User, "p"); // <= runtime validation
    });
  }.call(this, userId);

  _assert(ret, Promise, "return value");

  return ret;
}

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024 2

why is this the case?

AFAIK because interface can describe almost anything and Flow doesn't assume it describes objects, so:

interface $Refinement<P: (x: any) => boolean> {}

is equivalent to type Refinement<P: (x: any) => boolean> = any; or simply any so

type T1 = number & $Refinement<typeof isPositive>;
// equivalent to
type T2 = number & any;
// equivalent to
type T3 = number;

same here, what's the ,?

an interface can extend n other interfaces

from babel-plugin-tcomb.

zerkalica avatar zerkalica commented on August 12, 2024 1

I mean, how to combine static type checking with runtime checking without boilerplate code:
tcomb repeats part of flow functionality only for runtime and in own syntax: struct, func, etc.

All things, that can be expressed via flow, must be expressed via flow.

/* @flow */
import {
    String as string,
    struct
} from 'tcomb'

const MyObj = struct({
    t: string
})
type MyObj = {
    t: string;
}
export function test(arg: MyObj): MyObj {
    return arg
}

test({
    t: 123 // flow generates error
})

Just transpile

type MyObj = {
    t: string;
}

into

const MyObj = struct({
    t: string
})

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024 1

Hi @zerkalica,

working on this (again...hope I have some good news though).

I'm compiling:

  • string to t.String
  • number to t.Number
  • boolean, boolto t.Boolean
  • any, mixed to t.Any
  • void, null to t.Nil

It's not required to do the trick you suggested:

import {
    String as string // trick
} from 'tcomb'

Example

import t from 'tcomb';

function foo(x: string) {
  return x
}

now compiles to:

import t from 'tcomb';

function foo(x: string) {
  t.assert(t.is(x, t.String), 'Invalid argument x (expected a ' + t.getTypeName(t.String) + ')');

  return x;
}

Note the new t.is call, here's its code in tcomb:

var isType = require('./isType');

// returns true if x is an instance of type
module.exports = function is(x, type) {
  if (isType(type)) {
    return type.is(x);
  }
  return x instanceof type; // type should be a class constructor
};

The x instanceof type part will allow to handle non-tcomb types, generics included:

import t from 'tcomb';

function foo(p: Promise<string>): void {}

foo(1) // throws [tcomb] Invalid argument p (expected a Promise)

at the same time Flow throws:

index.js:9
9: foo(1)
^ number. This type is incompatible with
5: function foo(p: Promise): void {
^^^^^^^^^^^^^^^ Promise

Now, coming to your last example, this works:

import t from 'tcomb'

const MyObj = t.interface({
    t: t.String
}, 'MyObj')

export function test(arg: MyObj): MyObj {
    return arg
}

test({
    t: 123 // throws [tcomb] Invalid argument arg (expected a MyObj)...
})
// ...while Flow says no errors!

but this doesn't:

/* @flow */
type MyObj = {
  t: string
};

export function test(arg: MyObj): MyObj {
    return arg
}

test({
    t: 123 // here Flow correctly throws "number. This type is incompatible with string"
})

because JavaScript throws "ReferenceError: MyObj is not defined".

Now we could check that MyObj is defined, i.e. something like:

function test(arg: MyObj): MyObj {
  MyObj = typeof MyObj !== 'undefined' ? MyObj : t.Any; 
  t.assert(t.is(arg, MyObj), 'Invalid argument arg (expected a ' + t.getTypeName(MyObj) + ')');

  ...
}

or we could compile:

type MyObj = {
  t: string
};

to:

const MyObj = t.interface({
  t: t.String
})

Not sure what's better, what do you think?

/cc @ctrlplusb @chrisui @ivan-kleshnin @majhork

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024 1

Just added (basic) support for interfaces:

interface A {
  a: string;
}

interface B extends A {
  b: string;
}

to

const A = t.interface({
  a: t.String
}, "A");

const B = t.interface.extend([A, {
  b: t.String
}], "B");

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024 1

@gabro it doesn't work with version 0.26.0 :(

This works though (and being an expression seems more versatile):

import type { $Reify } from 'tcomb'

type Person = {
  name: string
};

const X = (({}: any): $Reify<Person>)
X.meta // OK
if (X.meta.kind === 'interface') {
  X.meta.props // OK
}

tested against the following incomplete (WIP) definition file:

declare module 'tcomb' {

  // runtime type introspection
  declare type $Reify<T> = Type<T>;

  declare type MetaIrreducible = {
    kind: 'irreducible'
  };

  declare type MetaList = {
    kind: 'list',
    type: Type
  };

  declare type MetaInterface = {
    kind: 'interface',
    props: {[key: string]: Type}
  };

  // TODO(giu) describe all meta objects
  declare type Meta =
      MetaIrreducible
    | { kind: 'refinement' }
    | { kind: 'maybe' }
    | { kind: 'struct' }
    | MetaInterface
    | { kind: 'func' }
    | { kind: 'tuple' }
    | MetaList
    | { kind: 'dict' }
    | { kind: 'enums' }
    | { kind: 'union' }
    | { kind: 'intersection' };

  declare interface $Refinement<P: (x: any) => boolean> {}

  declare interface Type<T> {
    (x: T): T;
    is(x: any): boolean;
    meta: Meta;
  }

  declare class Tcomb {

    // utils
    assert(guard: boolean, message?: string | () => string): void;
    stringify(x: any): string;

    // irreducibles
    Nil: Type<void | null>;
    String: Type<string>;
    Number: Type<number>;
    Boolean: Type<boolean>;
    Integer: Type<number>;
    Function: Type<Function>;
    Array: Type<Array<any>>;

    // combinators
    list<T>(type: Type<T>, name?: string): Type<Array<T>>;

  }

  declare var exports: Tcomb;
}

from babel-plugin-tcomb.

ctrlplusb avatar ctrlplusb commented on August 12, 2024 1

@gcanti - installing this tomorrow. Shall report back with any issues. Pretty exciting stuff. I'm gonna experiment with the static checking too.

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

Hi @zerkalica,

Since my data structures and domain models are already implemented with tcomb I just needed a quick way to type-check functions using tcomb types without using t.func combinator (which I find kind of annoying).

It's so difficult to create gracefully degraded to flow abstraction over tcomb?

Not sure what you mean, Could you elaborate a little bit? What's your use case?

from babel-plugin-tcomb.

ctrlplusb avatar ctrlplusb commented on August 12, 2024

This is pretty epic @gcanti. Fantastic as always.

My vote is for the transpilation of type MyObj to const MyObject = t.interface. Certainly more work but I feel like it would be fantastic for tcomb if you could support as much of the flow syntax as possible. Firstly, it could lead to greater tcomb adoption, granting a migration path for existing flow users. Additionally this library is already piggy backing off the babel flow syntax plugin, so I believe it will make things feel much more natural if we move to support the "native" flow syntax - perhaps this will save us headache or two in the future, or provide us with a great base from which to adapt against flow syntax changes/extensions.

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

Ok we got type aliases

type Person = {
  name: string,
  surname?: string,
  age: number,
  tags: Array<string>
};

function getFullName(p: Person) {
  return `${p.name} ${p.surname}`
}

getFullName({})

tcomb (at runtime)

throws [tcomb] Invalid argument p (expected a Person)

tcomb

Flow

> flow
index.js:14
 14: getFullName({})
     ^^^^^^^^^^^^^^^ function call
 10: function getFullName(p: Person) {
                             ^^^^^^ property `age`. Property not found in
 14: getFullName({})
                 ^^ object literal

index.js:14
 14: getFullName({})
     ^^^^^^^^^^^^^^^ function call
 10: function getFullName(p: Person) {
                             ^^^^^^ property `name`. Property not found in
 14: getFullName({})
                 ^^ object literal

index.js:14
 14: getFullName({})
     ^^^^^^^^^^^^^^^ function call
 10: function getFullName(p: Person) {
                             ^^^^^^ property `tags`. Property not found in
 14: getFullName({})
                 ^^ object literal


Found 3 errors

The problem now is: I don't know how to define refinements without irritate Flow:

/* @flow */

import t from 'tcomb'

type Person = {
  name: string,
  surname?: string,
  age: number,
  tags: Array<string>
};

const PersonRefinement = t.refinement(Person, p => p.surname === 'aaa')
> flow
index.js:12
 12: const PersonRefinement = t.refinement(Person, p => p.surname === 'aaa')
                                      ^^^^^^ Person. type referenced from value position
  5: type Person = {
          ^^^^^^ type Person


Found 1 error

from babel-plugin-tcomb.

zerkalica avatar zerkalica commented on August 12, 2024

First of all create normal reflection layer for each type, constructor, method and function.

There are many realisations: babel-plugin-type-metadata, babel-plugin-angular2-annotations, or my babel-plugin-transform-metadata

// interfaces.js
// @flow

export type User = {
  name: string;
}

to:

// interfaces.js
// @flow
import t from 'tcomb'
export type User = {
  name: string;
}

export tcomb$User = t.interface({
  name: t.string
})

Interface usage:

// @flow
import type {User} from './interfaces'

function test(user: User): void {}

to

// @flow
import type {User} from './interfaces'
import {tcomb$User} from './interfaces'
import t from 'tcomb'

const metadata$test = [tcomb$User]
function test(user: User): void {
  t.assert(metadata$test)
}
Reflect.defineMetadata('design:paramtypes', metadata$test, test)

PS:
I think, it's too hard to create real flow-compatible runtime checking. Look at https://github.com/codemix/babel-plugin-typecheck, it supports generics, imports/exports, optimizations. Code base about 3000 lines, but stil not flow-compatible.

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

it's too hard to create real flow-compatible runtime checking

Yes I agree, but my goal is different, for instance I don't want to re-implement at runtime what can be done only by Flow, I think it's not even possible.

Static type checking and runtime type checking are complementary:

  • static type checking: typos, generics, refactoring, cross file type errors (i.e. you change a LOC in a file and you break the code of another part of the app)
  • runtime type checking: refinements, runtime type introspection, IO validation, use cases when you can't setup Flow

First of all create normal reflection layer for each type, constructor, method and function

Not sure I'm following, why you'd do that? The goal is to type-check, not to feed the reflection API (which is still just a proposal at this time)

from babel-plugin-tcomb.

zerkalica avatar zerkalica commented on August 12, 2024

Reflect polyfil just an example. Main idea - generate and share tcomb metada for different puproses
and access them in runtime.
For example, for form validation: https://github.com/seanhess/runtime-types

from babel-plugin-tcomb.

ivan-kleshnin avatar ivan-kleshnin commented on August 12, 2024

Main idea - generate and share tcomb metada for different puproses and access them in runtime.

Ending with parallel type definions... How are you going to track and solve unsync issues?

from babel-plugin-tcomb.

gabro avatar gabro commented on August 12, 2024

@gcanti just to understand the refinement problem a bit better, even if you disambiguate the usage of a type as a value, how are you going to use the refinement anyway?

Basically, this:

import t from 'tcomb'

type Person = {
  name: string,
  surname?: string,
  age: number,
  tags: Array<string>
};

const PersonRefinement = t.refinement(Person, p => p.surname === 'aaa')
                                                              //   ^___ suppose this works, somehow

function getSurname(p: PersonRefinement) { // how does flow react to this?
  return p.surname;
}

from babel-plugin-tcomb.

zerkalica avatar zerkalica commented on August 12, 2024

@ivan-kleshnin What you mean, what is unsync issues?

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

how does flow react to this?

@gabro After a few hours of trials I'd say... "badly". You can't use a value as a type annotation (at least I didn't find a way).

There's a divide between the world of types and the world of values so it seems you must choose: either you give up on refinements and RTI:

// @flow

type Person = {
  name: string
};

function foo(p: Person) {
  return p.name
}

foo({}) // flow throws property `name`. Property not found in
        // tcomb throws [tcomb] Invalid argument p (expected a Person)

or you give up on static type checking and go dynamic (you gain refinements and RTI):

import t from 'tcomb'

const Person = t.interface({
  name: t.String
})

function foo(p: Person) {
  return p.name
}

foo(Person({})) // tcomb throws Invalid value undefined supplied to {name: String}/name: String
                // no errors for Flow

There is a slightly verbose middle ground to explore though (static + dynamic):

// @flow

import t from 'tcomb'

type Person = {
  name: string
};

const PersonRefinement = t.refinement(t.Object, p => p.name.length < 3)

function foo(p: Person) {
  t.assert(PersonRefinement.is(p), 'bad!');
  return p.name
}

foo({ name: 'Giulio' }) // no errors for flow but tcomb throws [tcomb] bad!

from babel-plugin-tcomb.

gabro avatar gabro commented on August 12, 2024

Isn't there a way of making a pass before flow does?

I think if we can have some kind of pipeline

pre-flow → flow → babel-plugin

we might do something interesting

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

I'm hesitant to go that route. I don't exclude a pre-processing step but it's potentially cumbersome, the nice thing about Flow is that you throw at it your source files and you are done.

If this thing works well, my preference at the moment is going completely static except for some strategic domain models which I may want to define as refinements.

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

A "real world" example: type-checking redux (state and actions) gcanti/redux-tcomb#9 (comment)

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

Added a _assert helper that gives better error messages:

import t from 'tcomb'

const MyObj = t.interface({
  t: t.String
}, 'MyObj')

export function test(arg: MyObj): MyObj {
  return arg
}

// before: [tcomb] Invalid argument arg (expected a MyObj)...
// now: [tcomb] Invalid value 123 supplied to arg: MyObj/t: String
test({
    t: 123
})

Source:

import t from 'tcomb'

const MyObj = t.interface({
  t: t.String
}, 'MyObj')

export function test(arg: MyObj): MyObj {
  return arg
}

Output:

import t from 'tcomb'

function _assert(x, type, name) {
  ...
}

const MyObj = t.interface({
  t: t.String
}, 'MyObj')

export function test(arg: MyObj): MyObj {
  _assert(arg, MyObj, 'arg');

  var ret = function (arg) {
    return arg;
  }.call(this, arg);

  _assert(ret, MyObj, 'return value');

  return ret;
}

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

I have a proposal in order to support refinements, but it's kinda hacky, I'd love to hear what you think:

Let's define a special interface $Refinement:

interface $Refinement<P: (x: any) => boolean> {}

$Refinement is a "placeholder" and won't be compiled by the babel plugin.

Usage

interface $Refinement<P: (x: any) => boolean> {}

const isPositive = x => x >= 0

// "typeof isInteger" will allow the babel plugin to compile the actual refinement
// picking the "isPositive" identifier while Flow sees only the type: number
type Positive = number & $Refinement<typeof isPositive>;

function foo(x: Positive) {}

foo(-1) // ok for Flow (obviously it can't type-check refinements)
foo('a')
/*
index.js:14
 14: foo('a')
     ^^^^^^^^ function call
 14: foo('a')
         ^^^ string. This type is incompatible with
 10: type Positive = number & $Refinement<typeof isPositive>;
                     ^^^^^^ number
*/

compiles to:

const isPositive = x => x >= 0

// here the babel plugin does its magic
const Positive = t.refinement(t.Number, isPositive, 'Positive');

function foo(x: Positive) {
  _assert(x, Positive, 'x')
  ...
}

// tcomb can type-check refinements
foo(-1) // throws [tcomb] Invalid value -1 supplied to x: Positive
foo('a') // throws [tcomb] Invalid value "a" supplied to x: Positive

Same for interfaces:

interface A {
  name: string;
}

const predicate = (x: A) => x.name.length > 3

interface B extends A, $Refinement<typeof predicate> {}

function foo(b: B) {}

foo({name: 1})

compiles to:

const A = t.interface({
  name: t.String
}, 'A')

const predicate = (x: A) => x.name.length > 3

const B = t.interface.extend([A, t.refinement(t.interface({
}), predicate)], 'B');

/cc @giogonzo

from babel-plugin-tcomb.

giogonzo avatar giogonzo commented on August 12, 2024

what kind of ancient sorcery am I looking at? jk, looks awesome 😀

type Positive = number & Refinement<typeof isPositive>
picking the "isPositive" identifier while Flow sees only the type: number

why is this the case?

interface B extends A, Refinement<typeof predicate> {}

same here, what's the ,?

Apart from some context I'm missing on flow type annotations syntax, yes, this looks kind of hacky / burden on the end user. But yeah! for profit

from babel-plugin-tcomb.

gabro avatar gabro commented on August 12, 2024

This is great! I think the opportunities this solution opens greatly outweigh the "hackish feel". Well done! 👍

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

Last topic: anyone has an idea about getting runtime type introspection?

type Person = {
  name: string
};

// this is ok in the runtime world
console.log(Person.meta) // => {kind: 'interface', props: ...}

but in the static world Flow throws:

index.js:9
  9: console.log(Person.meta)
                 ^^^^^^ Person. type referenced from value position
  5: type Person = {
          ^^^^^^ type Person

/cc @samwgoldman

from babel-plugin-tcomb.

zerkalica avatar zerkalica commented on August 12, 2024
// @flow
import {_, getIntrospection} from 'tcomb'

type Person = {name: string}

console.log(getIntrospection((_: Person)))

_ is a dummy var with any type.

babel converts (_: Person) to Person.meta

from babel-plugin-tcomb.

gabro avatar gabro commented on August 12, 2024

As discussed offline, if we make an assignment mandatory, this should be possible:

import type reify from 'tcomb';

declare interface Person {
  name: string
}

let Person: reify<Person>;

console.log(Person.meta);

The interesting bit is that we use a type for driving the transformation, so there's 0 risk of messing up with user's values.

from babel-plugin-tcomb.

zerkalica avatar zerkalica commented on August 12, 2024

Two errors:
Name is already bound and Property cannot be accessed on possibly undefined value

May be try this?

console.log((_: reify<IPerson>).meta);

from babel-plugin-tcomb.

gabro avatar gabro commented on August 12, 2024

Apparenty:

  • types and values live in the same namespace (my bad, didn't know that)
  • initialization (even to null) is required to silence the possibly undefined value warning

This should work

type reify<T> = any;

declare interface Person {
  name: string
}

let tPerson: reify<Person> = null;
console.log(tPerson.meta);

from babel-plugin-tcomb.

gabro avatar gabro commented on August 12, 2024

Slightly different syntax, but essentially the same thing

import { reify } from 'tcomb';
import type type from 'tcomb;

declare interface Person {
  name: string
}

let tPerson: type<Person> = reify; /// void

console.log(tPerson.meta);

where

export type type<T> = any;
export const reify = null;

No changes introduced, other than explicitly providing a value (reify) to use on the rhs.

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

Closing this as per cea8fc1.

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

@ctrlplusb I'm commiting lib on master so you can install the plugin simply running npm i gcanti/babel-plugin-tcomb#master, could you please give it a whirl? I'd love to hear if there are regressions with respect to v0.2

from babel-plugin-tcomb.

zerkalica avatar zerkalica commented on August 12, 2024

@gcanti How do you plain to track situations with metadata, when flow definition placed in external library?

// node_modules/myLib/flow-typed/interface.js
declare module 'myLib' {
  declare interface User {
    name: string;
  }
}
// app.js
import type {User} from 'myLib'

function (user: User) {
// ...
}

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

@zerkalica currently fallbacks to t.Any:

import type { User } from 'myLib'

function (user: User) {

}

compiles to

var myLib = require('myLib');

function f(user) {
  _assert(user, typeof myLib.User !== "undefined" ? myLib.User : t.Any, 'user');

  console.log(user);
}

What do you suggest? (perhaps it's better to open a new issue though)

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

Hey @ctrlplusb how is going? The first impact with Flow can be overwhelming. I'd love to put up a babel-plugin-tcomb boilerplate showing what you can get in terms of type safety, maybe with redux / react / etc.. Or perhaps even better, pick an existing boilerplate and add type annotations. Any ideas?

from babel-plugin-tcomb.

volkanunsal avatar volkanunsal commented on August 12, 2024

Not him, but I tried yesterday. There were some issues. The native flow
types weren't recognized and I had to create aliases for them. Maybe
because I didn't use the '@flow' comment at top of file? I will try again
today with flow enabled.

On Thursday, June 16, 2016, Giulio Canti [email protected] wrote:

Hey @ctrlplusb https://github.com/ctrlplusb how is going? The first
impact with Flow can be overwhelming. I'd love to put up a
babel-plugin-tcomb boilerplate showing what you can get in term of type
safety, maybe with redux / react / etc.. Or perhaps even better, pick an
existing boilerplate and add type annotations. Any ideas?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#11 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AAJQMHSJq7DbSs0U6hOaNJ_Pd_gFdTRoks5qMPIzgaJpZM4H23ne
.

Volkan Unsal
web and mobile development
volkanunsal.com

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

Just pushed babel-plugin-tcomb-boilerplate, It's just the usual counter for now (I'll add other features step by step) but already shows the great benefits of having both static and runtime type checking. Feeding Flow is a little bit verbose but it's awesome having the whole app type checked.

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

@volkanunsal yesterday I fixed (73a2104) a major problem with types imported from definition files (they were undefined).

The native flow types weren't recognized

Feel free to open an issue with some excerpt of your code, so we can hopefully come up with a solution and/or enhance the test suite.

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

Elm architecture with Flow and babel-plugin-tcomb: https://github.com/gcanti/babel-plugin-tcomb-boilerplate/tree/elm/src

from babel-plugin-tcomb.

minedeljkovic avatar minedeljkovic commented on August 12, 2024

Elm architecture with Flow and babel-plugin-tcomb: https://github.com/gcanti/babel-plugin-tcomb-boilerplate/tree/elm/src

@gcanti This is funny, I was also playing for the last week or so with Elm architecture in Redux, statically type checked with flow. I just pushed it to github today:
https://github.com/minedeljkovic/redux-elmish

I'd love to hear someone's comment or suggestion on this.

I am also very interested to use this repo to produce tcomb types for runtime introspection. For now I think of decoding part of model with fromJSON and tagged actions pretty print in redux devtools (with the help of tcomb union).

from babel-plugin-tcomb.

gcanti avatar gcanti commented on August 12, 2024

@minedeljkovic Really interesting and a great resource, thanks for sharing

from babel-plugin-tcomb.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.