TypeScript在React中的开发实践

核心提示在项目中添加类型注解是代码结构文档化非常棒的方式,它提供了一种为传递的数据注明结构的方式。添加数据类型,可以让我们复盘正在编写的代码,并思考是否能创建更清晰的代码结构以获得更高的代码质量。添加数据类型,也让我们的代码更利于理解,因为我们不需

在项目中添加类型注解是代码结构文档化非常棒的方式,它提供了一种为传递的数据注明结构的方式。

添加数据类型,可以让我们复盘正在编写的代码,并思考是否能创建更清晰的代码结构以获得更高的代码质量。

添加数据类型,也让我们的代码更利于理解,因为我们不需要深入研究项目就可以了解数据的结构,它消除了很多认知上的复杂性。

配置tsconfig

{ "compilerOptions": { "target": "es5", // 指定ECMAscript目标版本 "module": "commonjs", // 指定模块代码生成 "moduleResolution": "node", // 使用Node.js风格解析模块 "jsx": "react", // 支持在.tsx文件中使用jsx "declaration": true, // 生成相应的.d.ts文件 "rootDir": "src", // 指定源文件中的根文件夹 "sourceMap": false, // 是否生成相应的.map文件 "noUnusedLocals": true, // 是否报告未使用的局部变量的错误 "noUnusedParameters": false, // 是否报告未使用参数的错误 "noImplicitReturns": true, // 是否报告隐式返回的错误 "noImplicitAny": false, // 是否允许隐式any类型 "noFallthroughCasesInSwitch": true, // 是否报告switch语句中不可命中情况的错误 "resolveJsonModule": true, // 支持以.json扩展名导入的模块 "outDir": "lib", // 指定输出文件夹 "baseUrl": "./", // 指定用于解析非相对模块名的基本目录 src/xx/xxx "lib": [ "esNext", "dom", ], // 要包含在编译中的库文件列表 }, "include": [ "src*" ], // 指定与要包含在编译中的文件列表}

eslint

.eslintrc.js

{ "extends": "@ali/eslint-config-yd", "parser": "@typescript-eslint/parser", "rules": { "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".tsx"] }], "no-use-before-define": "off", "no-unused-vars": "off", "key-spacing": [2, { "beforecolon": false, "aftercolon": true }] }}

package.json

{ "name": "@alife/yd-sddp-lib-types", "version": "1.0.6", "main": "index.js", "types": "lib/types/index.d.ts", "files": [ "lib*.d.ts" ], "devDependencies": { "@types/react": "^18.0.9", "@types/react-dom": "^18.0.3", "@typescript-eslint/parser": "^5.22.0", "eslint": "^6.2.2", "typescript": "^4.6.4", }}

组件

组件是React核心概念之一,有类组件和函数组件,建议使用函数组件进行开发

// 函数组件type DomainTreeProps = { onChange : => any; refreshDomainTreeCount : number; openEditModel : boolean;}// 写法1const DomainTree: React.FC<DomainTreeProps> = => { const { onChange, refreshDomainTreeCount, openEditModel } = props; return ;};// 写法2const DomainTree = => { const { onChange, refreshDomainTreeCount, openEditModel } = props; return ;};export default DomainTree;// 类组件type DomainTreeState = { expand : boolean;}class DomainTree extends React.Component<DomainTreeProps, DomainTreeState> { public state = { expand: false }; render { const { onChange } = this.props; return <p>{this.state.expand}</p>; }}export default DomainTree;

在这个「DomainTree」组件中指定了「DomainTreeProps」也就意味着这个组件只接收相应的类型,我们可以给「DomainTreeProps」中的每个属性添加相应的注释(使用「」方式,这样hover对应属性会有相关注释),以便其他同学更快速的了解上下文,我们还可以通过「 」来表示哪些参数是可选项。

Hooks

Typescript具备强大的类型推断能力,这让我们在使用hooks开发时也能得心应手。

// 类型系统会自动推断出 count是number类型// setCount是个允许接受number类型或者函数类型的函数 例如:setCount 或 setCount => count++)const [count, setCount] = React.useState;

Typescript根据useState的参数推断类型,这让我们在实际开发中可以减少很多没必要的类型添加。

但是在有些情况下,我们也可以指定具体的类型。

type SelectStateType = 0 | 1 | 2;const [selectState, setSelectState] = React.useState;const [selectState, setSelectState] = React.useState;

指定「selectState」类型是「SelectStateType」,使「setSelectState」只能输入「0,1,2」,有效保障代码逻辑的严谨性。

第三方库类型问题组件不存在问题

这时可以使用 「@ts-ignore」忽略类型检查。

// @ts-ignore{intl}

onChange参数类型问题

「setArchive」根据类型推断只支持number类型,「UI.Radio.Group」提供的onChange方法返回的val是string | number | boolean类型,针对这种问题可以使用 as number断定val为number类型。

const [archive, setArchive] = React.useState; nChange={val => setArchive} shape="button" size="large" />

定义全局变量

在ts项目中,直接给新增的全局变量赋值会报错,会提示window上没有属性xxx,我们可以通过继承Window的属性新增全局变量。

declare global { interface Window { MICRO_APP: React.FC; }}window.MICRO_APP = =>

;

函数组件实际应用

import { generateTreeData, onDrop } from './helper';import AddOrModifyTree from './AddOrModifyTree';import DeleteTree from './DeleteTree';import './index.scss';import useDescribeDomains from './data';export type DomainInfo = { id: number; name: string; description: string; adminNames: string; authed: boolean | number; aliUid: string; domainLevel: number; domainType: number; instanceIds: string; parentDomainId: number; sort: number; totalCount: number;}type AddTreePanelState = { visible : boolean; record : DomainInfo; type : 1 | 2 | 3; action : 'add' | 'modify'}type DeleteTreePanelState = { visible : boolean; record : DomainInfo;}type SelectedType = 0 | 1 | 2;export interface DomainTreeProps { onChange : => any; refreshDomainTreeCount : number; openEditModel : boolean;}const DomainTree: React.FC = => { const { onChange, refreshDomainTreeCount, openEditModel } = props; const { dataSource, refreshDomainTree, defaultDomainInfo } = useDescribeDomains; const rootDomainInfo: DomainInfo = dataSource[0] || {}; const [currentDomainfo, setCurrentDomainInfo] = React.useState; const { id } = currentDomainfo || {} as DomainInfo; const [selected, setSelected] = React.useState; const [AddTreePanelInfo, setAddTreePanelInfo] = React.useState; const [DeleteTreePanelInfo, setDeleteTreePanelInfo] = React.useState; const [data, setTreeData] = React.useState; const checkDeleteDomain = => { // 如果当前删除的域名Id是当前所选的域,更新所选域为全域数据 if { updateCurrentDomainInfo; } }; const updateCurrentDomainInfo = => { onChange && onChange; setCurrentDomainInfo; setSelected; }; const genTreeData = => generateTreeData; React.useEffect => { // 源数据发生变化,更新树组件 const treeData = genTreeData; setTreeData; }, [JSON.stringify]); React.useEffect => { if { // 获取数据后,默认选中全域数据 onChange && onChange; } }, [rootDomainInfo.id]); React.useEffect => { if { // 依赖值发生变化,重新调用接口,更新树组件 refreshDomainTree; } }, [refreshDomainTreeCount]); // 默认展开全域数据(全域数据节点是被隐藏的) const [expandedKeys, setExpandedKeys] = React.useState; return } onClick={ => updateCurrentDomainInfo}>

全域数据

{rootDomainInfo .totalCount || 0}


自定义数据域

{openEditModel && nClick={ => setAddTreePanelInfo} >添加}

nExpand={keys => setExpandedKeys} selectedKeys={[`${id}`]} onDrop={info => onDrop} dataSource={data} onSelect={ => { const info: DomainInfo | null = selectedNodes[0] .props .data; if {updateCurrentDomainInfo; } }} />


nClick={ => updateCurrentDomainInfo}>

默认数据域

{defaultDomainInfo .totalCount || 0}

{AddTreePanelInfo.visible && setAddTreePanelInfo} refreshDomainTree={refreshDomainTree} />} {DeleteTreePanelInfo.visible && nClose={ => setDeleteTreePanelInfo} refreshDomainTree={refreshDomainTree} checkDeleteDomain={checkDeleteDomain} />}

);};export default DomainTree;

 
友情链接
鄂ICP备19019357号-22