如何把js语法改造为ts? 简单来说可以根据vsCode的类型提示,先找错误提示(), 然后根据错误提示修改 . 最基本的要求,定义的变量必须有 类型声明 .
注意: 错误提示不代表语法错误,不符合ts语法也会报错误提示,但实际上也可以被解析运行.
image.png

我们创建或修改了3个文件

  • services/login.ts
  • pages/Login/model.ts
  • pages/Login/index.tsx

    改造接口文件

    services/login.ts 开始,我们看到最直接的错误提示: “参数\”params\”隐式具有\”any\”类型”?
    那么我们思考,参数params应该是什么类型呢?
    很明显,params是登录的参数,应该是 {username: string,password: string} 的对象类型,我们声明一个 LoginUserParams 的类型即可.
    image.png ```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}); }
  1. <a name="CbsLr"></a>
  2. ## 改造model文件
  3. 观察 `pages/Login/model.ts ` 文件的解构, ts能够在代码运行之前,帮我们检测代码错误, 所以每一个对象,每一个声明的变量,必须有类型声明. 这里我们需要对M本身做类型声明,当然可以用 interface , 也可以用 type . 在antd 模板实例中,采用的是 type , 所以这里我们也采用type声明.
  4. ```typescript
  5. const M = {
  6. namespace: 'myLogin',
  7. state: {
  8. userInfo: {}
  9. },
  10. effects: {
  11. *doLogin(action, {call,put}){
  12. }
  13. },
  14. reducers: {
  15. initUserInfo(state,{payload:{userInfo}}){
  16. return {
  17. ...state
  18. }
  19. }
  20. }
  21. }
  22. export default M;

改造后:

  1. import {doLogin as loginApi} from '@/services/login';
  2. import {message} from 'antd';
  3. // Effect和Reducer类型是我们推导的类型,明细是来自Dva的.
  4. import type {Effect, Reducer} from 'umi';
  5. // 定义 MType 类型
  6. // 单独定义UserInfoType 是为了其他地方使用方便
  7. export type UserInfoType = {
  8. username: string;
  9. nickname: string;
  10. icon: string;
  11. }
  12. // 为了方便在其他地方使用到MType类型,这里直接局部导出
  13. export type MType = {
  14. namespace: string ;
  15. // 注意: userInfo:object 会报警告, 因为在后续使用userInfo的时候,有什么参数?必须提前声明.
  16. state: {
  17. userInfo: UserInfoType | {}
  18. };
  19. effects: {
  20. doLogin: Effect
  21. };
  22. reducers: {
  23. initUserInfo: Reducer
  24. }
  25. }
  26. // 给M声明类型
  27. const M: MType = {
  28. namespace: 'myLogin',
  29. state: {
  30. userInfo: {}
  31. },
  32. effects: {
  33. /*
  34. 调用api,因为是异步,这里采用js generator语法把回调扁平化.类似于async+await
  35. action为载荷对象(同vuex), 第二个参数解构出来的call和put是固定写法:
  36. call用于调用接口(Promise类型)
  37. put用于调用reducer设置state
  38. */
  39. *doLogin(action, {call,put}){
  40. // 打印action,查看对象结构
  41. // console.log(action);
  42. const {payload} = action;
  43. // message与引入模块冲突 重命名为: errMsg
  44. const {data,success,message:errMsg} = yield call(loginApi,payload);
  45. if(success){
  46. // 存入state
  47. // put专门用于调用 reducer, 而且需要添加 迭代器 yield修饰符
  48. yield put({
  49. type: 'initUserInfo',
  50. payload: data
  51. })
  52. message.success('登录成功!');
  53. }else{
  54. // 返回错误 提示错误信息
  55. message.error(errMsg);
  56. }
  57. }
  58. },
  59. reducers: {
  60. // 第二个参数为action: {type:string ,payload: any}
  61. // 把payload解构出来 并获取userInfo
  62. initUserInfo(state,{payload:{userInfo}}){
  63. // 由于单向数据流的设计理念 reducer每次需要重新返回新的完整的state
  64. return {
  65. ...state,
  66. userInfo
  67. }
  68. }
  69. }
  70. }
  71. export default M;

改造页面tsx文件

对于文件pages/Login/index.tsx , 我们直接针对报错提示,提供类型声明 .
另外需要注意组件”泛型“声明.

  1. import React from 'react';
  2. import { Form, Input, Button } from 'antd';
  3. import styles from './index.less';
  4. // 引入connect 连接model
  5. import {connect} from 'umi';
  6. import type {Dispatch} from 'umi';
  7. // 我们需要用到MType的类型 所以引入
  8. import type {MType,UserInfoType} from './model';
  9. // 定义明明空间 方便引用
  10. const namespace = 'myLogin';
  11. // 定义props类型
  12. type PropsType = {
  13. userInfo: UserInfoType;
  14. dispatch: Dispatch;
  15. }
  16. // 这种写法是直接声明参数props类型
  17. // const Login = (props: PropsType)=>{
  18. // 也可以通过 "泛型" 来定义组件类型
  19. // # https://www.tslang.cn/docs/handbook/generics.html
  20. const Login: React.FC<PropsType> = props=>{
  21. // 将我们需要的state解构出来
  22. // dispatch是connect之后才有的属性,用于发送请求调用model的effect
  23. // userInfo可以通过 mapStateToPros connect到当前组件
  24. const {userInfo,dispatch} = props;
  25. // 提交表单且数据验证成功后回调事件
  26. const onFinish = (v: any)=>{
  27. // 这里可以获取到表单内容,则直接调用 model的doLogin
  28. dispatch({
  29. type: `${namespace}/doLogin`,
  30. payload: v
  31. })
  32. }
  33. // 提交表单且数据验证失败后回调事件
  34. const onFinishFailed = (v: any)=>{
  35. console.log('ev',v);
  36. }
  37. return (
  38. <Form
  39. className={styles.main}
  40. labelCol = {{span: 8}}
  41. wrapperCol = {{span: 16}}
  42. onFinish= {onFinish}
  43. onFinishFailed = {onFinishFailed}
  44. >
  45. <Form.Item
  46. label="用户名"
  47. name="username"
  48. rules={[
  49. {required: true ,message: '用户名不能为空!'}
  50. ]}
  51. >
  52. <Input/>
  53. </Form.Item>
  54. <Form.Item
  55. label="密码"
  56. name="password"
  57. rules={[
  58. {required: true, message: '密码不能为空!'}
  59. ]}
  60. >
  61. <Input.Password/>
  62. </Form.Item>
  63. <Form.Item
  64. wrapperCol={
  65. {
  66. offset: 8,
  67. span: 16
  68. }
  69. }
  70. >
  71. <Button type="primary" htmlType="submit">登录</Button>
  72. </Form.Item>
  73. {/* 随意添加一个测试div 用于显示userInfo */}
  74. <div>
  75. {JSON.stringify(userInfo)}
  76. </div>
  77. </Form>
  78. )
  79. }
  80. // # https://www.redux.org.cn/docs/react-redux/api.html
  81. // 由于命名空间的划分,我们只需要获取 login 的model
  82. const mapStateToProps = (state: MType['state'])=>{
  83. return{
  84. userInfo: state[namespace].userInfo
  85. }
  86. }
  87. // 通过connect 连接 model与react组件
  88. export default connect(mapStateToProps)(Login);

总结

通过对js代码改造为ts, 我们发现一些规律:

  • ts完全兼容js
  • ts在js的基础上做了强制类型约束,可以是基本类型或复杂类型
  • 约束类型可以自定义复杂类型
  • ts还有一些高级特性,比如泛型等

总之, 新手没有必要完全阅读ts文档,有些高级特性很有可能我们都用不到,没有使用场景会很难理解. 我们只需要在消除报错的基础上边写边学即可.