-
-
Save vitalipe/dd442b44a62bb328bc3e948e4eab2a18 to your computer and use it in GitHub Desktop.
| // this is how I write React with JS.. | |
| // | |
| // "View" is a simple wrapper around React's API similar to this one: | |
| // https://github.com/vitalipe/react-hut#createhutview---component-api | |
| // | |
| // "Type" is based on React.PropTypes with a few simple extentions... | |
| const UserComponent = View.define({ | |
| props : { | |
| name : Type.String.defaultsTo("Jhon Doe"), | |
| age : Type.Int.range(0, 99), | |
| isAdmin : Type.Bool.defaultsTo(false) | |
| }, | |
| render({name, age, isAdmin}) { | |
| return <div> | |
| <div>Hello, {name}</div> | |
| <div>Your age: {age}</div> | |
| {isAdmin && <div>welcome admin!</div>} | |
| </div>; | |
| } | |
| }); |
| // this is the TS version of the avobe code, I wrote it to the best of my TypeScript knowledge | |
| // it's based on this: | |
| // https://stackoverflow.com/questions/40209352/how-to-specify-optional-default-props-with-typescript-for-stateless-functiona | |
| // | |
| // hidden in this code is a little bug that makes it possible to pass a decimal age (e,g 32.123123123), I left it just to | |
| // illustrate how limited is the type system of type script, also note that we need to manually validate range at runtime. | |
| // my point here is that TS is not an improvement over JS with React. feedback is welcome! | |
| interface UserProps { | |
| name: string; | |
| age: number; | |
| isAdmin?: boolean | |
| } | |
| const defaultUserProps: UserProps = { | |
| name: "Jhon Doe", | |
| isAdmin: false | |
| } | |
| const Test: React.StatelessComponent<TestProps> = ({name, age, isAdmin}) => { | |
| if (!inRange(age, 0, 99)) | |
| throw new Error("Invalid age range!"); | |
| return <div> | |
| <div>Hello, {name}</div> | |
| <div>Your age: {age}</div> | |
| {isAdmin && <div>welcome admin!</div>} | |
| </div>; | |
| } |
Although, for something so small I'd just write it as:
const Test = ({name = "Jhon Doe", age, isAdmin = false}) => (<div>
<div>Hello, {name}</div>
<div>Your age: {age}</div>
{isAdmin && <div>welcome admin!</div>}
</div>);And put nothing but paint logic in my stateless component - since it's 3 LoC I wouldn't type it explicitly either.
oh wow! that's pretty clever! :)
but about the validation part - if that's the case then types are completely redundant at this level, the range code was not meant to be used as input validation but as a documentation + programmer error validation... consider your code above, to the best of my knowledge TS doesn't have an "int" or an "unsigned int" type, it will gladly accept 4.3234 or -100, and that's where most errors happen.
but still, at least the API is not that horrible when you use this inline interface thing.. :)
--
I've put the throw thing inside render() just to avoid using a class - in practice I'm totally OK with a throwing component as long as I treat it like a "compile time error", render can also throw from a null ref, this has the same weight to me.
I agree about the &&, it's here just to make use of a boolean, I also don't use it in real code (nor do I use JSX :P ).
since it's 3 LoC I wouldn't type it explicitly either.
95% of my code is made of 2-3 arg components, that's my point! you don't need static type validation for your UI tree, it's just data! :P
but about the validation part - if that's the case then types are completely redundant at this level
I humbly disagree, if new programmers (or you in a few months) comes and looks at the code and wants to reuse a component - having types really helps because of the associated tooling and early errors when they make mistakes.
TypeScript is a little like C# in that it's more about being useful than being correct.
the range code was not meant to be used as input validation but as a documentation + programmer error validation... consider your code above, to the best of my knowledge TS doesn't have an "int" or an "unsigned int" type, it will gladly accept 4.3234 or -100, and that's where most errors happen.
TypeScript is just JavaScript. All TypeScript does is statically enforce JavaScript's types (and transpile code down to older versions).
I humbly disagree that a large amount programmer errors happen in number ranges of component input. However, if you want you can force ranges in typescript (with enumerations) and define sound operations on that - I feel like it'd be a waste of your time (although probably cool).
Just like I wouldn't type the above component (unless someone from the outside might use it) I wouldn't type ranges. Statically verifying things is not a free abstraction and the more powerful the types you build are - the harder they are to reason about.
However, for the more annoying cases (string arguments rather than number parameters) TypeScript provides ad-hoc solutions, for example - typed string unions let us statically type things like <Button type='big' /> or <Person role="admin" /> or document.addEventListener("click" pretty cleanly, and you can use keyof to generate these unions on the fly in compile which is really neat.
but still, at least the API is not that horrible when you use this inline interface thing.. :)
The big distinction from languages like Java is that types are determined based on their structure and are extensible at runtime - (a little like clojure protocols).
That is, a {x:3, y:5} is a point in TypeScript - but not in Java (where types are nominal and explicit because they hate us).
First of all, IMO render should never throw. If
renderthrows (like in the above example) the UI can't recover from the render - and more generally - a throwing function is never a pure function :)Validation should happen in the model layer which verifies data isn't dirty (or when it originates in the UI itself - with an
<input min="0" max="99" ...).rendershould always be pure - and these runtime validations are just as declarative with TS.So, like:
(Personally, I try to mininize using code like
isAdmin && <div>componentConditionallyRendered</div>)