Go to list of users who liked
Share on X(Twitter)
Share on Facebook
More than 5 years have passed since last update.
React JSX with TypeScript(1.6)
はじめに
つい先日,TypeScript 1.5系の安定版がリリースされたばかりだが, 1.6系の話をしようと思う.
6月頃に「TypeScriptにReactのJSXサポートが載るぞ!」とアナウンスがあり, 「へー」と思っていたのだが, 久々にmasterを確認してみたら, 既にmergeされてたので触ってみた.
(Roadmapへの記載とmergeのタイミングにあまり差がなかった模様1)
折角なのでTypeScriptのJSXサポートを試してみましたよ、というお話. なお、作成した内容はQuramy/tsc-react-exampleに上げてます.
追記
このエントリを書いた直後に気づきましたが, 既にTypeScript の JSX/React サポートを試すに同様の内容が投稿されてました... orz
環境の準備
コードの話をする前に, TypeScriptでJSXを記述するための環境準備について.
なにはともあれ, TypeScript本体をレポジトリから持ってこないと話になら無い.
グローバルインストールする場合, 下記でnightlyのTypeScriptを持ってくる
npminstall-g typescript@1.6.0-betatsc --versionでVersion 1.6.0-beta として、1.6系の値が確認できればOK.
ローカルインストールであれば, ↓ですね.
npminstalltypescript@1.6.0-beta--save-dev以下の説明ではローカルインストールした前提で進めることとする.
tsconfig.json or tscのオプション
コマンドラインからtscでコンパイルする場合は, 下記のように--jsxオプションにreact を付与して叩けばよい
./node_modules/TypeScript/bin/tsc--jsx react--module umd main.tsx一点注意すると, tscに渡すファイルは.tsではなく,.tsxである..tsだと,--jsx オプションを与えていてもJSXのパーサーは動作しない.
--jsxがコマンドラインオプションになっているということは, 当然tsconfig.jsonにも記載できるということなので, 以下のようにしてもよい.
{"compilerOptions":{"module":"umd","jsx":"react"}}gulp-typescriptを使う場合
gulp-typescriptを使うと, ローカルインストールしたTypeScriptとtsconfig.jsonを渡すことができるので, 僕は手元で下記のgulpfile.jsを作り,gulp bundleを実行していた.
'use strict';vargulp=require('gulp'),typescript=require('typescript'),ts=require('gulp-typescript'),browserify=require('browserify'),source=require('vinyl-source-stream'),del=require('del');varproject=ts.createProject('src/tsconfig.json',{typescript:typescript});gulp.task('compile',function(){varresult=gulp.src('src/**/*{ts,tsx}').pipe(ts(project));returnresult.js.pipe(gulp.dest('.tmp'));});gulp.task('bundle',['compile'],function(){varb=browserify('.tmp/bootstrap.js');returnb.bundle().pipe(source('bundle.js')).pipe(gulp.dest('dist'));});gulp.task('clean',function(done){del(['.tmp'],done.bind(this));});browserify周りはもうちょっと綺麗に書けそうな気もするが, あまり詳しくないので, これで許してください.
react.d.ts
いつも通りdtsmでgetしておくだけ.
dtsm initdtsminstallreact/react.d.ts--saveエディタ
僕はVim使いなので, Vim用設定の話だけ書きます.AtomとかSublimeは知らん.
JSXでも補完を有効にしたいので, Vimプラギンtsuquyomiを使うこととする.
このプラギンは, ローカルインストールされたTypeScriptを優先的に利用して補完情報を取得するようにできているので, グローバルな環境を汚さずにTypeScriptの新機能を利用したいときにうってつけだ. 作成者本人が言うんだから間違いない.
なお, 前述したようにJSXオプションを利用するためには, 対象コードの拡張子が.tsxであるため, FileTypeの指定を.vimrcに追記しておく必要がある.
NeoBundle'Shougo/vimproc'NeoBundle'Quramy/tsuquyomi'NeoBundle'leafgarland/typescript-vim'autocmdBufNewFile,BufRead *.tssetfiletype=typescriptautocmdBufNewFile,BufRead *.tsxsetfiletype=typescriptご覧の通り, ちゃんとJSX中でも補完が効きました. TSServerすげー.
なお, syntaxプラギンはJSX対応されているわけではないが、特にハイライトの結果に違和感は感じない.
実際に作成したブツ
これでJSXを作成する準備が整った. 今回はサンプルということでtodo的なやつを作ってみた.
Babel + React等で0.13系のJSXを書いたことがあれば, 殆ど違和感なくComponentを作ることができる.
Babelとの差異で言うと, ReactのComponentをextendsする際に, ジェネリクスで2つのtypeが要求される、という点だろう.
1つめがprops用, 2つ目がstate用のtypeである.
- 本体
/// <reference path="../typings/bundle.d.ts" />import*asReactfrom'react';import{TodoItem}from'./todoItem';interfaceITodo{description:string;key:number;}exportinterfaceIMainState{newItem?:{description:string;};todoList?:ITodo[];}exportinterfaceIMainProps{}exportclassMainextendsReact.Component<IMainProps,IMainState>{state:IMainState={newItem:{description:''},todoList:[]}constructor(){super();this.changeName=this.changeName.bind(this);this.addItem=this.addItem.bind(this);this.removeItem=this.removeItem.bind(this);}changeName(e:any){this.setState({newItem:{description:e.target.value}});}addItem(){varlist=this.state.todoList;list.push({description:this.state.newItem.description,key:newDate().getTime()});this.setState({todoList:list,newItem:{description:''}});}removeItem(item:ITodo){varlist=this.state.todoList.filter(i=>i.key!==item.key);this.setState({todoList:list});}render(){vartodoItems=this.state.todoList.map(item=>{return<TodoItemkey={item.key}item={item}onRemove={this.removeItem}></TodoItem>;});return(<div><div><inputtype="text"placeholder="input new item"value={this.state.newItem.description}onChange={this.changeName}/><buttononClick={this.addItem}>add</button></div><ul>{todoItems}</ul></div>);}}- リストの要素
/// <reference path="../typings/bundle.d.ts" />import*asReactfrom'react';interfaceITodo{description:string;}exportinterfaceITodoItemState{}exportinterfaceITodoItemProps{item:ITodo;onRemove?:(todo:ITodo)=>any;key?:number;// I think this prop is unnecessary, but unless it an error occurs in tsc.}exportclassTodoItemextendsReact.Component<ITodoItemProps,ITodoItemState>{constructor(){super();this.removeItem=this.removeItem.bind(this);}removeItem(){this.props.onRemove(this.props.item);}render(){return(<li><span>{this.props.item.description}</span><buttononClick={this.removeItem}>delete</button></li>);}}- 起動するやつ(bundle.jsの起点)
/// <reference path="../typings/bundle.d.ts" />import*asReactfrom'react';import{Main}from'./main';React.render(React.createElement(Main),document.getElementById('main'));- HTML
<!DOCTYPE html><html><head><metacharset="UTF-8"><title>tsc-react-example</title></head><body><divid="main"></div><scriptsrc="bundle.js"></script></body></html>気になったところ
仮想DOMエンジン側に要素とDOMのひもづけを教えてあげた方が描画が早くなる関係上, ReactでリストをRenderするときは以下のようにkey=... を指定する.
(指定しないとReactがwarning吐いてくる)
vartodoItems=this.state.todoList.map(item=>{return<TodoItemkey={item.key}item={item}onRemove={this.removeItem}></TodoItem>;});ただ, 上記のコードを書いた所,TodoItem クラスのprops interfaceにもkeyという名前のプロパティが無いと怒られた.
classTodoItemextendsReact.Component<{key:number;},any>{...}keyってComponentのpropsとして扱うものなのか?という違和感を少し感じたので書き留めておく.
終わりに
環境準備も含めて, かなりあっさりJSX + TypeScriptを用意することができた.
実際にガリガリと.tsxを書くのは1.6がリリースされてからになるだろうけど, React使いの人は今のうちからチェックしておいたらどうだろう。
Roadmapへの記載は6/19,mergeされたが6/30 っぽい↩
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme
