🐣React Ts, Vue Ts vs Angular Ts

536 阅读3分钟

TypeScript 的类型约束,能帮助我们很好的进行代码检查,能够帮我们在编译之前,写代码的时候就能发现错误,TS 已经被很多的库进行使用,TS 的优越性,我们也需要掌握这些他们在前端的用法。

React Ts

  1. 函数式组件添加 TypeScript 应该如何写?
  2. class 组件添加了 TypeScript 应该如何写?

说白了就是应该如何添加类型。当然我们需要在官方所写的声明文件之上写TS。

  • "@types/react"
  • @types/react-dom"
  • 约束参数 props
  • 约束状态 state, 当然函数组件使用的 useState

函数组件

  1. 函数组件参数:
  2. 函数组件返回值;
// 函数组件类型
type FC<P = {}> = FunctionComponent<P>;

// 函数组件接口
interface FunctionComponent<P = {}> {
    // 函数组件
    (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
    // 可选的 props 类型,有了 TS 这就可以不必了
    propTypes?: WeakValidationMap<P>;
    // context api
    contextTypes?: ValidationMap<any>;
    // props 默认值
    defaultProps?: Partial<P>;
    // context api 
    displayName?: string;
}
// 带有 children 的约束类型
type PropsWithChildren<P> = P & { children?: ReactNode };

示例1:

import React from 'react';
import { ReactNode } from 'react';

interface IAc {
  name: string
  children: ReactNode | string | number | ReactNode[]
}

function Ac(props: IAc):JSX.Element {
  return (
    <div>{props.name}{props.children}</div>
  ) 
}

export default Ac;

上面只是一种简单的用法,实际中使用 React.FC<{}> 别名进行限制

const TagList: React.FC<{ tags: CurrentUser['tags'] }> = ({ tags }) => {}

当然我们也可以不写类型约束,让 TS 编译器自己推测。

既然看了组件,那我们不防把转发式的函数组件也一起看了:

// 转发组件,TS 能帮我们很好的理解JS,这就类型带来的好处
interface ForwardRefRenderFunction<T, P = {}> {
    (props: PropsWithChildren<P>, ref: ((instance: T | null) => void) | MutableRefObject<T | null> | null): ReactElement | null;
    displayName?: string;
    defaultProps?: never;
    propTypes?: never;
    }

示例:

interface PhoneViewProps {
  value?: string;
  onChange?: (value: string) => void;
}
const PhoneView: React.FC<PhoneViewProps> = (props) => {}

我们使用 PhoneViewProps 约束函数组件的 props PropsWithChildren<P> P 的类型就是 PhoneViewProps,所以 props 的类型通过泛型参数传入内部,约束了 props.

  • 写函数组件我们不使用统一接口,而是在函数组件中显示的定义
const SalesCard = ({
  rangePickerValue,
  salesData,
  isActive,
  handleRangePickerChange,
  loading,
  selectDate,
}: {
  rangePickerValue: RangePickerValue;
  isActive: (key: 'today' | 'week' | 'month' | 'year') => string;
  salesData: VisitDataType[];
  loading: boolean;
  handleRangePickerChange: (dates: RangePickerValue, dateStrings: [string, string]) => void;
  selectDate: (key: 'today' | 'week' | 'month' | 'year') => void;
}) => ()

这种写法的约束函数变量的类,约束的是函数的参数的类型。其实这种显示约束和FC<p={}> 约束本质是一样的,显示约束更加直接,更加明显。那我们可以知道当 props 特别多的时候,就更加适合用 FC<p={}> 形式约束组件。

  • 函数生命组件使用: FC<p={}>
  • 函数表达式: 使用 props 累心约束

函数组件钩子函数的约束

useState

使用了函数重载,接收过重形式的 State

function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];

function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];

// 示例
const [loading, setLoading] = useState<boolean>(true);
const [stepComponent, setStepComponent] = useState<React.ReactNode>(<Step1 />);
const [currentStep, setCurrentStep] = useState<number>(0);
const [done, setDone] = useState<boolean>(false);
const [visible, setVisible] = useState<boolean>(false);
const [current, setCurrent] = useState<Partial<BasicListItemDataType> | undefined>(undefined);

Vue2x 非class 写法

使用 extend 构造器,创建 Vue 组件。 Vuetify 中所有的组件都是通过 extend 方法进行构建。

import Vue from 'vue'
const Component = Vue.extend({
  // 类型推断已启用
})

const Component = {
  // 这里不会有类型推断,
  // 因为 TypeScript 不能确认这是 Vue 组件的选项
}

Vue2x class 的写法

class 的写法变化就比较大了:

  • Component 组件装饰器,装饰一个 Vue 类。

    • 组件注册: components
  • 数据绑定,使用 data 函数 hook,才会被理解为响应式的,但是不使用 data hook,将值赋值为 null 也可被解释为响应式的。

  • 方法, class 的普通方法

  • 计算属性,使用 getter/setter 方法

  • 生命周期钩子,mounted

  • 渲染函数:render

  • 额外的钩子:

    • 适配VueRouter的钩子,但是 Component.registerHooks 必须在使用前注册他们
  • 自定义装饰器

  • class Vue 组件的继承

  • class Mixins

  • props 类型的处理,使用 extend 来处理

    • vue-property-decorator 这个包很好的解决了这个问题,计算属性还是用 getter/setter
  • vuex 的映射

  • ref 引用

  • vue-class-component/hooks

Vuex

  • vuex
  • vuex-class
  • vuex-module-decorators

Vue3x 中 TS 的写法

Angular

参考