如何把js语法改造为ts? 简单来说可以根据vsCode的类型提示,先找错误提示(), 然后根据错误提示修改 . 最基本的要求,定义的变量必须有 类型声明 .
注意: 错误提示不代表语法错误,不符合ts语法也会报错误提示,但实际上也可以被解析运行.
我们创建或修改了3个文件
services/login.tspages/Login/model.tspages/Login/index.tsx改造接口文件
从services/login.ts开始,我们看到最直接的错误提示: “参数\”params\”隐式具有\”any\”类型”?
那么我们思考,参数params应该是什么类型呢?
很明显,params是登录的参数,应该是 {username: string,password: string} 的对象类型,我们声明一个LoginUserParams的类型即可.
```typescript
import request from ‘@/utils/request’;
export type LoginParamsType = { userName: string; password: string; mobile: string; captcha: string; }; // 类型的声明,可以用type, 也可以使用 interface // 两者功能都差不多,语法有稍微的差别. // 区别: type不可以重名, interface重名会自动合并 export type LoginUserParams = { username: string; password: string; }; // 或者声明接口. 命名为个人习惯,接口的声明以I开头. 非规范. export interface ILoginUserParams { username: string ; password: string; }
export async function fakeAccountLogin(params: LoginParamsType) { return request(‘/api/login/account’, { method: ‘POST’, data: params, }); }
/**
- 登录接口
- @param mobile
*/
// 使用 ILoginUserParams或者LoginUserParams声明params类型都可以,这里使用interface
// export async function doLogin(params: LoginUserParams) {
export async function doLogin(params: ILoginUserParams) {
return request(
/lejuAdmin/index/login,{ method: ‘POST’, data: params }); } export async function getFakeCaptcha(mobile: string) { return request(/api/login/captcha?mobile=${mobile}); }
<a name="CbsLr"></a>## 改造model文件观察 `pages/Login/model.ts ` 文件的解构, ts能够在代码运行之前,帮我们检测代码错误, 所以每一个对象,每一个声明的变量,必须有类型声明. 这里我们需要对M本身做类型声明,当然可以用 interface , 也可以用 type . 在antd 模板实例中,采用的是 type , 所以这里我们也采用type声明.```typescriptconst M = {namespace: 'myLogin',state: {userInfo: {}},effects: {*doLogin(action, {call,put}){}},reducers: {initUserInfo(state,{payload:{userInfo}}){return {...state}}}}export default M;
改造后:
import {doLogin as loginApi} from '@/services/login';import {message} from 'antd';// Effect和Reducer类型是我们推导的类型,明细是来自Dva的.import type {Effect, Reducer} from 'umi';// 定义 MType 类型// 单独定义UserInfoType 是为了其他地方使用方便export type UserInfoType = {username: string;nickname: string;icon: string;}// 为了方便在其他地方使用到MType类型,这里直接局部导出export type MType = {namespace: string ;// 注意: userInfo:object 会报警告, 因为在后续使用userInfo的时候,有什么参数?必须提前声明.state: {userInfo: UserInfoType | {}};effects: {doLogin: Effect};reducers: {initUserInfo: Reducer}}// 给M声明类型const M: MType = {namespace: 'myLogin',state: {userInfo: {}},effects: {/*调用api,因为是异步,这里采用js generator语法把回调扁平化.类似于async+awaitaction为载荷对象(同vuex), 第二个参数解构出来的call和put是固定写法:call用于调用接口(Promise类型)put用于调用reducer设置state*/*doLogin(action, {call,put}){// 打印action,查看对象结构// console.log(action);const {payload} = action;// message与引入模块冲突 重命名为: errMsgconst {data,success,message:errMsg} = yield call(loginApi,payload);if(success){// 存入state// put专门用于调用 reducer, 而且需要添加 迭代器 yield修饰符yield put({type: 'initUserInfo',payload: data})message.success('登录成功!');}else{// 返回错误 提示错误信息message.error(errMsg);}}},reducers: {// 第二个参数为action: {type:string ,payload: any}// 把payload解构出来 并获取userInfoinitUserInfo(state,{payload:{userInfo}}){// 由于单向数据流的设计理念 reducer每次需要重新返回新的完整的statereturn {...state,userInfo}}}}export default M;
改造页面tsx文件
对于文件pages/Login/index.tsx , 我们直接针对报错提示,提供类型声明 .
另外需要注意组件”泛型“声明.
import React from 'react';import { Form, Input, Button } from 'antd';import styles from './index.less';// 引入connect 连接modelimport {connect} from 'umi';import type {Dispatch} from 'umi';// 我们需要用到MType的类型 所以引入import type {MType,UserInfoType} from './model';// 定义明明空间 方便引用const namespace = 'myLogin';// 定义props类型type PropsType = {userInfo: UserInfoType;dispatch: Dispatch;}// 这种写法是直接声明参数props类型// const Login = (props: PropsType)=>{// 也可以通过 "泛型" 来定义组件类型// # https://www.tslang.cn/docs/handbook/generics.htmlconst Login: React.FC<PropsType> = props=>{// 将我们需要的state解构出来// dispatch是connect之后才有的属性,用于发送请求调用model的effect// userInfo可以通过 mapStateToPros connect到当前组件const {userInfo,dispatch} = props;// 提交表单且数据验证成功后回调事件const onFinish = (v: any)=>{// 这里可以获取到表单内容,则直接调用 model的doLogindispatch({type: `${namespace}/doLogin`,payload: v})}// 提交表单且数据验证失败后回调事件const onFinishFailed = (v: any)=>{console.log('ev',v);}return (<FormclassName={styles.main}labelCol = {{span: 8}}wrapperCol = {{span: 16}}onFinish= {onFinish}onFinishFailed = {onFinishFailed}><Form.Itemlabel="用户名"name="username"rules={[{required: true ,message: '用户名不能为空!'}]}><Input/></Form.Item><Form.Itemlabel="密码"name="password"rules={[{required: true, message: '密码不能为空!'}]}><Input.Password/></Form.Item><Form.ItemwrapperCol={{offset: 8,span: 16}}><Button type="primary" htmlType="submit">登录</Button></Form.Item>{/* 随意添加一个测试div 用于显示userInfo */}<div>{JSON.stringify(userInfo)}</div></Form>)}// # https://www.redux.org.cn/docs/react-redux/api.html// 由于命名空间的划分,我们只需要获取 login 的modelconst mapStateToProps = (state: MType['state'])=>{return{userInfo: state[namespace].userInfo}}// 通过connect 连接 model与react组件export default connect(mapStateToProps)(Login);
总结
通过对js代码改造为ts, 我们发现一些规律:
- ts完全兼容js
- ts在js的基础上做了强制类型约束,可以是基本类型或复杂类型
- 约束类型可以自定义复杂类型
- ts还有一些高级特性,比如泛型等
总之, 新手没有必要完全阅读ts文档,有些高级特性很有可能我们都用不到,没有使用场景会很难理解. 我们只需要在消除报错的基础上边写边学即可.
