TypeScript 的类型约束,能帮助我们很好的进行代码检查,能够帮我们在编译之前,写代码的时候就能发现错误,TS 已经被很多的库进行使用,TS 的优越性,我们也需要掌握这些他们在前端的用法。
React Ts
- 函数式组件添加 TypeScript 应该如何写?
- class 组件添加了 TypeScript 应该如何写?
说白了就是应该如何添加类型。当然我们需要在官方所写的声明文件之上写TS。
- "@types/react"
- @types/react-dom"
- 约束参数 props
- 约束状态 state, 当然函数组件使用的 useState
函数组件
- 函数组件参数:
- 函数组件返回值;
// 函数组件类型
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
参考
- @type/react React 声明文件 TypeScript 支持