TypeScript + React 进阶的学习文档链接总结一下

448 阅读4分钟

推荐的

  • 官网要全部看一遍 里面有些面试题标准的答案

  • React+TypeScript

    • react + ts 的使用 很详细
  •  TypeScript 类型体操通关秘籍

    • 大佬写的 基本看完感觉日常没有任何问题了
    • 类型体操自己写不出来也有思路或者能在开源的找
      • 例如项目里面 什么驼峰转下划线之类的
  • Type-challenges

    • ts类型体操的刷题的。大概刷刷就行 加强巩固

大概这四个我看了后研究了下 antd 和 Procomponents 的ts类型,目前感觉业务的话这个水平就差不多够用了。
后续话可以自己看看 typescript 实现原理啊, 自己怎么实现一个typescript, tsconfig每个配置的含义啊等等之类的

一些文档

其他文档

ts 类型体操开源的npm包

不会写的可以在里面找找看看有没有相似的,也可以参考参考他们怎么写的。

vue组件如何添加泛型

setup-generic

// child.vue
<script setup lang="ts">
import HelloWorld from './HelloWorld.vue';

interface DataType {
  name: 'zs' | 'l4';
  age: number;
}
type IColumnsType = {
  dataIndex: keyof DataType;
  render?: (text: string, record: DataType) => any;
  [props: string]: any;
}[];
const data: IColumnsType = [
  {
    dataIndex: 'age',
    render(_text, record) {
      return record.age;
    },
  },
  {
    dataIndex: 'name',
    visible: false,
  },
];
const params: { kw: '1' | '2' } = {
  kw: '1',
};
</script>

<template>
  <div>
    <HelloWorld :data="data" :params="params" v-slot="{ data, params }">
      {{ data[0].dataIndex == 'age' ? 1 : 2 }}
      {{ params?.kw == '1' }}
    </HelloWorld>
  </div>
</template>

<style></style>
// HelloWorld.vue
<script lang="ts" setup generic="Record extends any[], Params extends any">
defineProps<{
  data: Record;
  params?: Params;
}>();
</script>

<template>
  <div>
    <table>
      <!-- Todo:  -->
    </table>
    <slot :data="data" :params="params" />
  </div>
</template>

tsx

import { defineComponent, type PropType, type SlotsType } from 'vue'
import type { JSX } from 'vue/jsx-runtime'

type Color = 'red' | 'blue' | 'green'
type IProps<T extends Color> = T extends 'red'
  ? {
      color: T
      name?: string
    }
  : {
      name?: string
    }

export const Demo: <T extends Color>(props: IProps<T>) => JSX.Element = defineComponent({
  name: 'MyDemo',
  props: {
    color: String as PropType<Color>,
    name: String
  },
  slots: Object as SlotsType<{
    default: (color: Color) => any
  }>,
  setup: (props, ctx) => {
    return () => (
      <>
        {props.name}
        {props.color}
        {ctx.slots.default?.(props.color!)}
      </>
    )
  }
}) as any

const a = <Demo />
const b = <Demo<'red'> color="red" v-slots={{ color: 'red' }} />

React+TypeScript 风格的代码

高阶组件的类型

  • 详细地址参考

  • www.yuque.com/eternallycy… 《compose的使用时的类型问题》

  • class类型ref => public readonly MyRef = React.createRef<InstanceType<typeof CustomComponent>>();

  • hooks类型的ref => const MyRef = React.useRef<React.ElementRef<typeof CustomComponent>>(null!)

    • 直接用CustomComponent组件导出的ref类型也行 React.useRef<xxx>(null!)
  • 多层高阶组件使用 compose 在最外层来定义类型

import type { PermessionProps, permissionParams } from './permission';
import { withRoutePage } from './withRoutePage';
import React, { useImperativeHandle } from 'react';
import compose from './compose';
import { withRouter } from './withRouter';
import { connect } from '@umijs/max';
import Permession from './permission';

export interface extraProps<Values = any> {
  myExtraProps?: Values;
}

export interface IndexPageProps<Values = any> extends PermessionProps<Values>, permissionParams<Values>, extraProps<Values> {}

export type IndexPageHandel<Values = any> = {
  setVisible: React.Dispatch<React.SetStateAction<boolean>>;
  myExtraProps?: extraProps<Values>['myExtraProps'];
};

const IndexPage: React.ForwardRefRenderFunction<IndexPageHandel, IndexPageProps> = (props, ref) => {
  const nnnnnnnnn = props.nnnnnnnnn;
  const id = props.match?.params?.id;
  const { myExtraProps } = props;
  const [visible, setVisible] = React.useState<boolean>(false);

  useImperativeHandle(ref, () => ({
    setVisible,
    myExtraProps,
  }));

  return <div>IndexPage</div>;
};

export default compose<typeof IndexPage>(
  withRoutePage,
  withRouter,
  connect(
    ({ global, login }: any) => ({
      accessCollection: login?.accessCollection || [],
    }),
    null,
    null,
    {
      forwardRef: true,
      pure: undefined,
    },
  ),
  Permession,
)(IndexPage, { myExtraProps: { age: 2 } });

antd风格的代码封装demo

  • 组件jsx类型泛型 + xxx.Item 风格 + context + Ref
  • 详细demo代码见
  • antd Form内的 Form.Item绑表单的是用 context 实现的, 但日常业务组件感觉用不上。
  • 一般这种封装就基本够用了
  • 因为有些功能 例如流程什么 可能要封装二三十个组件 这样就比较好
    • 点一下就出来了 ProcessPage.hooks ProcessPage.utils ...
    • 泛型看情况吧 一般表单最好都加下 比如编辑表格等, 其他一些业务组件一般用不上吧。
    • 可以参考参考我的
    • github.com/eternallycy…
import { useFetchProps } from '@ims-view/hooks';
import { Button, Spin } from 'antd';
import { CommonDemo, ICommonDemoHandle } from 'ims-view-pc';
import React, { useState } from 'react';

interface IRecord {
  username: string;
  useId: number;
}

interface IApi {
  code: number;
  msg: string;
  success: boolean;
  data: IRecord;
}

const Demo = () => {
  const [number, setNumber] = useState<number>(0);
  const CommonDemoRef = React.useRef<ICommonDemoHandle<IRecord>>(null!);
  const params: useFetchProps<IRecord> = {
    fetchConfig: {
      apiUrl: '/fetchUserInfo',
      method: 'post',
      dataPath: 'data',
      depts: [number],
    },
    request: async (config) => {
      const data = new Promise((resolve, reject) => {
        fetch(config?.url, config as any)
          .then((data) => data.json())
          .then((data) => resolve(data))
          .catch((error) => reject(error));
      });
      return (await data) as IApi;
    },
    // dataHandler: (data) => data?.data,
  };

  const handleFetch = () => {
    setNumber(number + 1);
  };

  return (
    <>
      <CommonDemo<IRecord> ref={CommonDemoRef} {...params}>
        <Spin spinning={CommonDemoRef?.current?.loading || false}>
          <Button onClick={handleFetch}>发起请求</Button>
          <pre>{JSON.stringify(CommonDemoRef?.current?.data)}</pre>
        </Spin>
      </CommonDemo>
      <CommonDemo.Item />
    </>
  );
};
export default Demo;

最后

想问问 vue 除了官网有没有什么高级的 ts + vue 文档, 类似这种的 React+TypeScript 感觉写泛型比较困难,ts类型支持的不好。

大家还有没有什么比较好的文章啊 可以分享分享 ~