TS
TypeScript是Microsoft公司注册商标。https://zhongsp.gitbooks.io/typescript-handbook/content/
TypeScript具有类型系统,且是JavaScript的超集。 它可以编译成普通的JavaScript代码。 TypeScript支持任意浏览器,任意环境,任意系统并且是开源的。
TypeScript 的定位是静态类型语言,在写代码阶段就能检查错误,而非运行阶段
类型系统是最好的文档,增加了代码的可读性和可维护性。
有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)等
ts最后被编译成js
创建TS项目
create-react-app my-app –template typescript
ts基础
1.基本类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var name:string= 'sola' name.substring(0,1)
var age:number = 100 age.toFixed(1)
var show:boolean = true
var my:string|number = 123
var myany:any = true
export default {}
|
2.数组
1 2 3 4 5 6 7 8 9 10 11 12 13
| var list:string[] = ['1','2','3']
var list2:number[] = [1,2,3]
var list3:(number|string)[] = [1,2,'3']
var mylist1:Array<string> = ['a','b']
var myList2:Array<string|number> = [1,2,'qwer']
export default {}
|
3.对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| interface IObj { name:string, age:number, location?:string, [propName:string]:any } var obj1:IObj = { name:'sola', age:11, }
export default {}
|
4.函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| function test1(a:string,b:number,c?:boolean):string { return a.substring(0,1) + b }
test1('a',3,false)
interface IFunc{ (a:string,b:string,c?:number):string }
var myFunc2:IFunc = function (a: string,b:string,c?:number):string { return a+b }
interface IObj { name:string, age:number, getName:(name:string)=>string }
var obj:IObj = { name:'sola', age:18, getName:(name:string) => { return name } } var res = obj.getName('sola') export default {}
|
5.类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Bus { public name = 'sola' private list:any = [] protected age = 100 subscribe(cb: any){ this.list.push(cb) } dispatch(){ this.list.forEach((cb: () => any) => { cb && cb() }) } }
export default {}
|
6.类+接口
类中 定义接口,用来实现类中所要实现的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| interface Ifunc { getName:() => string, } class A implements Ifunc{ getName(){ return "AAA" } }
class B implements Ifunc{ getName(){ return "CCC" } }
class C implements Ifunc{ getName(){ return 'DDD' } }
function init(obj:Ifunc) { obj.getName() } var objA = new A() var objB = new B() var objC = new C()
init(objA) init(objB) init(objC)
export default {}
|
react class ts
1.state
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import React, {Component} from 'react'; interface IState { name:string }
class App extends Component<any,IState> { state:IState = { name:'sola' } render() { return ( <div> app-{this.state.name.substring(0,1).toUpperCase() + this.state.name.substring(1)} <button onClick={() => { this.setState({ name:'jerry' }) }}>click</button> </div> ); } }
export default App;
|
2.prop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import React, {Component} from 'react';
class App extends Component { render() { return ( <div> <Child name="sola"/> </div> ); } }
interface IProps { name:string } class Child extends Component<IProps, any>{ render() { return <div> child - {this.props.name} </div>; } }
export default App;
|
3.ref
当在ts中使用ref的时候,需要通过类型断言的方式,来指定ref 的类型为HTMLInputElement
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| import React, {Component} from 'react';
interface IState { text: string, list:string[] }
class App extends Component<any, IState> { state = { text:'', list:[] } myRef = React.createRef<HTMLInputElement>()
render() { return ( <div> {/*<input value={this.state.text} onChange={(e) => {*/} {/* this.setState({*/} {/* text:e.target.value*/} {/* })*/} {/*}} type="text"/>*/}
<input type="text" ref={this.myRef}/> <button onClick={() => { console.log((this.myRef.current as HTMLInputElement).value);
this.setState({ list:[...this.state.list,(this.myRef.current as HTMLInputElement).value] }) }}>click </button>
{ this.state.list.map((item,index) => <li key={item}>{item} <button onClick={() => { let newList = this.state.list.concat()
var t = newList.splice(index,1) console.log(t); this.setState({ list:newList })
}}>del</button> </li> ) } </div> ); } }
export default App;
|
4.父子通信(子传父)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| import React, {Component} from 'react';
class App extends Component { state = { isShow:true } render() { return ( <div> app <Navbar title="首页" cb={() => { this.setState({ isShow:!this.state.isShow }) }}/>
{this.state.isShow && <Sidebar/>} </div> ); } } interface IProps { title:string, cb:() => void }
class Navbar extends Component<IProps, any>{ render() { return <div> navbar-{this.props.title} <button onClick={() => { this.props.cb() } }>click</button> </div>; } }
class Sidebar extends Component<any, any>{ render() { return <div> sidebar </div>; }
}
export default App;
|
react function ts
1.state
1 2 3 4 5 6 7 8 9 10 11 12 13
| import React, {useState} from 'react';
function App() { const [name,setName] = useState<string>("sola") return ( <div> app - {name.substring(0,1).toUpperCase()+name.substring(1)} <button>click</button> </div> ) }
export default App
|
2.prop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import React from 'react';
export default function App() { return ( <div> <Child name="aaa"/> <Child1 name="bbb"/> </div> ) } interface IProps { name:string } function Child(props:IProps) { return <div>child-{props.name}</div> }
const Child1:React.FC<IProps> = (props) => { return <div>child1</div> }
|
3.ref
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import React, {useRef, useState} from 'react';
export default function App() { const myText = useRef<HTMLInputElement>(null) const [list,setList] = useState<string[]>([]) return ( <div> <input type="text" ref={myText}/> <button onClick={() => { console.log((myText.current as HTMLInputElement).value); setList([...list,(myText.current as HTMLInputElement).value]) }}>click</button> {list.map(item => <li key={item}> {item} </li>)} </div> ) }
|
4.父子通信(子传父)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import React, {useState} from 'react';
export default function App() { const [isShow,setIsShow] = useState<boolean>(true) return ( <div> <Navbar cb={() => { setIsShow(!isShow) }}/> { isShow && <SideBar/>} </div> ) } interface IProps { title?:string cb:() => void } function Navbar(props:IProps) { return <div> navbar- <button onClick={() => { props.cb() } }>click</button> </div> }
function SideBar() { return <div> sidebar </div> }
|
react router ts
安装声明文件:npm i @types/react-router-dom
其他编码模式与之前是一样的,有一个点需要注意:在使用编程式路由导航的时候,需要引入接口配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import React, {Component} from 'react'; import axios from 'axios' import {RouteComponentProps} from 'react-router-dom' interface IItem { filmId:number, name:string } class Film extends Component<RouteComponentProps,any> { state = { list:[] } componentDidMount() { axios({ url: "https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=7605418", headers: { 'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.0","e":"1646462402616989231939585","bc":"110100"}', 'X-Host': 'mall.film-ticket.film.list' } }).then(res => { this.setState({ list:res.data.data.films }) }) }
render() { return ( <div> { this.state.list.map((item:IItem) => <li onClick={() => { this.props.history.push(`/detail/${item.filmId}`) }} key={item.filmId}>{item.name}</li> ) } </div> ); } }
export default Film;
|
动态路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import React, {Component} from 'react'; import {RouteComponentProps} from 'react-router-dom' interface IParam { myid:string } class Detail extends Component<RouteComponentProps<IParam>,any> { render() { return ( <div> detail-{this.props.match.params.myid} </div> ); } }
export default Detail;
|
react redux ts
ts中的redux,与之前也基本无大差异,只是新增了一些规定及约束,下面来看之前的业务场景:在Film组件中点击某个电影,跳转到电影详情Detail组件的时候,底部栏tabbar组件的状态需要改变,在Film组件里显示tabbar组件,在Detail组件中隐藏tabbar组件。
1.新建redux文件夹,该文件夹下创建store.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import {createStore} from 'redux'
interface IAction { type:string, payload?:any }
interface IState { isShow:boolean } const reducer:any = (prevState:IState ={ isShow:true },action:IAction) => { const {type} = action const newState:any = {...prevState} switch (type) { case "show": newState.isShow = true return newState case "hide": newState.isShow = false return newState default:return prevState } }
const store = createStore(reducer) export default store
|
2.App组件中进行监听状态的变化并保存到状态(订阅)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import React, {Component} from 'react'; import IndexRouter from "./router/Index"; import store from "./redux/store"; class App extends Component { state = { isShow:(store.getState() as any).isShow } componentDidMount() { console.log(store.getState()); store.subscribe(() => { this.setState({ isShow:(store.getState() as any).isShow }) }) }
render() { return ( <div> <IndexRouter></IndexRouter> { this.state.isShow && <ul> <li>电影</li> <li>影院</li> <li>我的</li> </ul> }
</div> ); } }
export default App;
|
3.Detail组件中派发action(发布)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import React, {Component} from 'react'; import {RouteComponentProps} from 'react-router-dom' import store from "../redux/store"; interface IParam { myid:string } class Detail extends Component<RouteComponentProps<IParam>,any> { componentDidMount() { store.dispatch({ type:'hide' }) }
componentWillUnmount() { store.dispatch({ type:'show' }) }
render() { return ( <div> detail-{this.props.match.params.myid} </div> ); } }
export default Detail;
|
react antd ts
与正常使用无大差异,个别antd组件中,需要不同的ref,详细查看antd官网