In Pro, TypeScript is used as the default development language. The benefits of TypeScript are no longer necessary. Both development costs and maintenance costs can be greatly reduced. It is a must for middle and back-end development. Here are several dimensions to talk about the best practices for TypeScript in Pro.
It is recommended to use type at all times. Type is more like a variable. Compared with interface, the characteristics of type are as follows:
-The expression function is more powerful, not limited to object/class/function -To extend an existing type, a new type needs to be created, and the name cannot be duplicated -Support more complex type operations
Basically all types expressed with interface have their equivalent type expressions. In the course of practice, we also found that a type can only be expressed by interface, but cannot be expressed by type, that is, attaching attributes to functions.
interface FuncWithAttachment {(param: string): boolean;someProperty: number;}const testFunc: FuncWithAttachment = {};const result = testFunc('mike'); // Type remindertestFunc.someProperty = 3; // Type reminder
Any project is inseparable from the processing of data and interfaces. Splicing data and interfaces is one of the main tasks of forming business logic and front-end. Defining the TypeScript type of data returned by the interface can reduce a lot of maintenance costs and the time of querying apis.
In Pro, it is recommended to define the type of interface data in src/services/API.d.ts
. Take basic user information as an example:
declare namespace API {// Basic user informationexport type CurrentUser = {avatar?: string;name?: string;title?: string;group?: string;signature?: string;tags?: {key: string;label: string;}[];userid?: string;access?: 'user' | 'guest' | 'admin';unreadCount?: number;};}
There are no type definitions in many projects, and websites such as json2ts are recommended for automatic conversion.
We can use it easily when using it. Files ending with d.ts
will be imported to the global by TypeScript by default, but the import
syntax cannot be used. If you need to quote, you need to use triple slashes.
export async function query() {return request<API.CurrentUser[]>('/api/users');}// propsexport type UserProps = {userInfo: API.CurrentUser;};
We don't recommend writing generics when developing in industry code, but in order to get a better typescript experience, we may need to understand the generic tips of commonly used component libraries. Here is a brief list.
import ProForm from '@ant-design/pro-form';import ProTable, { ActionType } from '@ant-design/pro-table';import React, { useState, useRef } from 'react';type DataType = {};const Page = () => {// The generic type of useState will become the type of stateconst [state, setState] = useState<string>('');// The type of useRef will be set to the type of actionRef.currentconst actionRef = useRef<ActionType>();// click uses React.MouseEvent plus dom type generic// HTMLInputElement represents the input tag Another commonly used one is HTMLDivElementconst onClick = (e: React.MouseEvent<HTMLInputElement>) => {};// onChange uses React.ChangeEvent plus dom type generic// Generally HTMLInputElement, HTMLSelectElement may also be usedconst onChange = (e: React.ChangeEvent<HTMLInputElement>) => {};return (<>{'ProForm sets generics to agree on the parameter types of interfaces such as onFinish'}<ProForm<DataType> />{`DataType sets the type of row in render,Params is the submission type of the parameterValueType represents a custom valueType type, ProTable will automatically merge`}<ProTable<DataType, Params, ValueType> /><input onClick={onClick} onChange={onChange} /></>);};
const WrapComponent: React.FC<ExtendedProps> = (props) => {// return ...};export default WrapComponent;// orexport default function (props: React.PropsWithChildren<SpinProps>) {// return ...}
Umi has helped us to encapsulate in many places. If we know the specific type, we can reduce a lot of any.
IRouteComponentProps
is the props type of the component configured in config.ts
, which brings some react-router related props
export interface IRouteComponentProps<Params extends { [K in keyof Params]?: string } = {},Query extends { [K in keyof Query]?: string } = {}> {children: JSX.Element;location: Location & { query: Query };route: IRoute;routes: IRoute[];history: History;match: match<Params>;}
We can use it like this in the page:
import React from 'antd';import { IRouteComponentProps } from 'umi';const Page: React.FC<IRouteComponentProps> = () => {return <Layout />;};
Front-end development is largely about dealing with Window. Sometimes we have to add parameters to Window, such as various statistical codes. Provide a way to add parameters in TypeScript. Define as follows in /src/typings.d.ts
:
interface Window {ga: (command: 'send',hitType: 'event' | 'pageview',fieldsObject: GAFieldsObject | string,) => void;reloadAuthorized: () => void;}
If you don't want to add it in Window, but want to use it globally, such as parameters injected via define, we inject it in /src/typings.d.ts
via the declare
keyword.
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;
These examples can all be seen in
/src/typings.d.ts
.
Antd is a very convenient set of UI libraries. In order to use it better, we need to understand some of its types.
There are many commonly used types in Form, and most of them can be derived from 'antd/es/form
. Here are some of the most commonly used ones.
The form type generated by using Form.useForm()
in antd@4 is FormInstance
. FormItemProps is also a commonly used type. We can use this type to encapsulate FormItem
and add our own logic.
import { FormInstance, FormItemProps } from 'antd/es/form';const [form] = Form.useForm();// save refconst ref = useRef<FormInstance>();ref.current = form;
Due to the variability of form, the values returned by form.getFieldsValue();
are all of the Store
type, so we can directly as
as we want parameters for ourselves.
const user = form.getFieldsValue() as API.CurrentUser;
ProTable is recommended here, the types are relatively clear, examples of commonly used types.
import { ProColumns, ActionType } from '@ant-design/pro-table';const columns: ProColumns<API.CurrentUser>[] = [{title: 'Name',dataIndex: 'name',hideInSearch: true,},];const actionRef = useRef<ActionType>();export default <ProTable actionRef={actionRef} />;
In addition, TablePaginationConfig
and TableRowSelection
are more commonly used. Special attention should be paid when using both of these generic types.
import { TablePaginationConfig } from 'antd/es/table/Table';import { TableRowSelection } from 'antd/es/table/interface';const pagination: TablePaginationConfig = {pageSize: 20,total: 2000,onChange: (current) => {},};const rowSelection: TableRowSelection = {selectedRowKeys: [],onChange: (keys, rows) => {},};
string[]|number[]
is not the same as (string|number)[]
, in this case, just use React.ReactText[]
directly.
If we use the function component, it may report an error that ref cannot be found. At this time, we need to use React.forwardRef
, but it should be noted that the type must also be modified accordingly.
- React.FC<CategorySelectProps>+ React.ForwardRefRenderFunction<HTMLElement, CategorySelectProps>
Sometimes I need to dynamically update the key of an Object. For convenience, we can set the key to any, so that any key can be used, and JSON.parse is unnecessary.
type Person = {name: string;age?: number;[propName: string]: any;};
It's very simple in 3.8, just obj?.xxx
.
We can directly define it as any.
declare module 'xxx';import xxx from 'xxx';
Sometimes the type error is component, but it looks very uncomfortable. It will always compile and report errors, so you can use @ts-ignore
to ignore it temporarily.
// @ts-ignorexxxx;
After all, TypeScript is a markup language. When you need to use any, you don't need to be hesitant to use any. When encountering dynamic code, you might as well use as unknown as XXX
, which can save a lot of time.