如何把js语法改造为ts? 简单来说可以根据vsCode的类型提示,先找错误提示(), 然后根据错误提示修改 . 最基本的要求,定义的变量必须有 类型声明 .
注意: 错误提示不代表语法错误,不符合ts语法也会报错误提示,但实际上也可以被解析运行.
我们创建或修改了3个文件
services/login.ts
pages/Login/model.ts
pages/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声明.
```typescript
const 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+await
action为载荷对象(同vuex), 第二个参数解构出来的call和put是固定写法:
call用于调用接口(Promise类型)
put用于调用reducer设置state
*/
*doLogin(action, {call,put}){
// 打印action,查看对象结构
// console.log(action);
const {payload} = action;
// message与引入模块冲突 重命名为: errMsg
const {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解构出来 并获取userInfo
initUserInfo(state,{payload:{userInfo}}){
// 由于单向数据流的设计理念 reducer每次需要重新返回新的完整的state
return {
...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 连接model
import {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.html
const Login: React.FC<PropsType> = props=>{
// 将我们需要的state解构出来
// dispatch是connect之后才有的属性,用于发送请求调用model的effect
// userInfo可以通过 mapStateToPros connect到当前组件
const {userInfo,dispatch} = props;
// 提交表单且数据验证成功后回调事件
const onFinish = (v: any)=>{
// 这里可以获取到表单内容,则直接调用 model的doLogin
dispatch({
type: `${namespace}/doLogin`,
payload: v
})
}
// 提交表单且数据验证失败后回调事件
const onFinishFailed = (v: any)=>{
console.log('ev',v);
}
return (
<Form
className={styles.main}
labelCol = {{span: 8}}
wrapperCol = {{span: 16}}
onFinish= {onFinish}
onFinishFailed = {onFinishFailed}
>
<Form.Item
label="用户名"
name="username"
rules={[
{required: true ,message: '用户名不能为空!'}
]}
>
<Input/>
</Form.Item>
<Form.Item
label="密码"
name="password"
rules={[
{required: true, message: '密码不能为空!'}
]}
>
<Input.Password/>
</Form.Item>
<Form.Item
wrapperCol={
{
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 的model
const 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文档,有些高级特性很有可能我们都用不到,没有使用场景会很难理解. 我们只需要在消除报错的基础上边写边学即可.