feat: [form] add type definitions to utils to enhance type safety (#3322)

* feat: 为多个文件添加类型定义以增强类型安全性

* fix(schema): 修改默认消息和系统消息的返回值为undefined

* fix(schema): 优化SchemaType接口的类型定义和默认消息处理逻辑
This commit is contained in:
ajaxzheng 2025-04-21 10:03:08 +08:00 committed by GitHub
parent e178dc2928
commit 2b6f894e83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 148 additions and 57 deletions

View File

@ -54,7 +54,7 @@ const maxDateValues = {
const timezone1 = '-12:00,-11:00,-10:00,-09:30,-08:00,-07:00,-06:00,-05:00,-04:30,-04:00,-03:30,-02:00,-01:00'
const timezone2 = '-00:00,+00:00,+01:00,+02:00,+03:00,+03:30,+04:00,+04:30,+05:00,+05:30,+05:45,+06:00'
const timezone3 = '+06:30,+07:00,+08:00,+09:00,+10:00,+10:30,+11:00,+11:30,+12:00,+12:45,+13:00,+14:00'
const timezones = [].concat(timezone1.split(','), timezone2.split(','), timezone3.split(','))
const timezones: string[] = [...timezone1.split(','), ...timezone2.split(','), ...timezone3.split(',')]
const getTimezone = (date) => {
const timezoneoffset = 0 - date.getTimezoneOffset() / 60
@ -185,13 +185,17 @@ const iso8601DateParser = (m) => {
}
}
const dateParsers = [
// 添加适当的类型定义
type DateParser = (m: any) => Date | undefined
type DateParserTuple = [RegExp, DateParser]
const dateParsers: DateParserTuple[] = [
[yyyymmddReg, yyyymmddDateParser],
[mmddyyyyReg, mmddyyyyDateParser],
[iso8601Reg, iso8601DateParser]
]
const parseDate = (str) => {
const parseDate = (str: string) => {
for (let i = 0, len = dateParsers.length; i < len; i++) {
const m = dateParsers[i][0].exec(str)
@ -279,7 +283,7 @@ const getDateArray = (str, dateFormat) => {
return arr
}
const invalideTime = (time, min, max) => isNaN(time) || time < min || time > max
const invalideTime = (time: number, min: number, max: number): boolean => isNaN(time) || time < min || time > max
const invalideValue = ({ year, month, date, hours, minutes, seconds, milliseconds }) =>
invalideTime(year, 0, maxDateValues.YEAR) ||

View File

@ -13,6 +13,31 @@
import { on, off } from '../dom'
import { isServer } from '../globalConfig'
// 添加接口定义
interface FullscreenEvents {
fullscreenElement: string
fullscreenEnabled: string
requestFullscreen: string
exitFullscreen: string
fullscreenchange: string
fullscreenerror: string
[key: string]: string
}
interface Screenfull {
request: (element?: any, options?: any) => Promise<void>
exit: () => Promise<void>
toggle: (element?: any, options?: any) => Promise<void>
onchange: (callback: any) => void
onerror: (callback: any) => void
on: (event: string, callback: any) => void
off: (event: string, callback: any) => void
raw: FullscreenEvents | {}
isFullscreen?: boolean
element?: any
isEnabled?: boolean
}
const fullscreenApi = [
'fullscreenElement',
'fullscreenEnabled',
@ -53,7 +78,7 @@ const fullscreenApiMap = [fullscreenApi, fullscreenApiWebkit, fullscreenApiMoz,
const document = typeof window !== 'undefined' && typeof window.document !== 'undefined' ? window.document : {}
let fullscreenEvents = null
let fullscreenEvents: FullscreenEvents | null = null
const getFullScreenEvents = () => {
if (isServer) return
@ -75,12 +100,12 @@ const getFullScreenEvents = () => {
getFullScreenEvents()
const eventNameMap = {
const eventNameMap: { [key: string]: string | undefined } = {
change: fullscreenEvents && fullscreenEvents.fullscreenchange,
error: fullscreenEvents && fullscreenEvents.fullscreenerror
}
const screenfull = {
const screenfull: Screenfull = {
request(element, options) {
return new Promise((resolve, reject) => {
const onFullscreenEntered = () => {

View File

@ -413,7 +413,14 @@ const getResult = ({ type, res, formatText, string, reg, args }) => {
return res
}
const judgForFunc = (args, formatTypes, type) => {
interface FormatTypesInterface {
text: string
url: string
html: string
[key: string]: string
}
const judgForFunc = (args: any[], formatTypes: FormatTypesInterface, type: string): { args: any[]; type: string } => {
const lastArg = args[args.length - 1]
if (lastArg !== formatTypes.text && lastArg !== formatTypes.url && lastArg !== formatTypes.html) {

View File

@ -14,8 +14,43 @@ import { hasOwn, isNull } from '../type'
import { getNodeKey } from './util'
import { Node } from './node'
interface TreeStoreOptions {
data: any
key?: string
props?: Record<string, any>
load?: (node: Node, callback: (data: any) => void) => void
lazy?: boolean
checkStrictly?: boolean
defaultCheckedKeys?: any[]
defaultExpandedKeys?: any[]
autoExpandParent?: boolean
afterLoad?: (params: { data: any; init: boolean }) => void
filterNodeMethod?: (value: any, data: any, node: Node) => boolean
[key: string]: any
}
// 为Node类型声明安全类型
type NodeType = any
type FilterNodeMethodType = (value: any, data: any, node: NodeType) => boolean
export class TreeStore {
constructor(options) {
currentNode: NodeType | null = null
currentNodeKey: any = null
nodesMap: Record<string, NodeType> = {}
root: NodeType
data: any
lazy = false
load?: (node: NodeType, callback: (data: any) => void) => void
afterLoad?: (params: { data: any; init: boolean }) => void
props: Record<string, any> = {}
filterNodeMethod?: FilterNodeMethodType
key = ''
defaultCheckedKeys: any[] = []
checkStrictly = false
defaultExpandedKeys: any[] = []
autoExpandParent = true
constructor(options: TreeStoreOptions) {
this.currentNode = null
this.currentNodeKey = null
@ -40,7 +75,7 @@ export class TreeStore {
}
}
getMappingData(data) {
getMappingData(data: any) {
const props = this.props || {}
const mapping = {}
@ -53,16 +88,18 @@ export class TreeStore {
return { ...data, ...mapping }
}
filter(value) {
filter(value: any) {
const { lazy, filterNodeMethod, getMappingData } = this
const walkTree = (node) => {
const walkTree = (node: NodeType) => {
const childNodes = node.root ? node.root.childNodes : node.childNodes
childNodes.forEach((child) => {
// 筛选时需要添加mapping字段但是不能修改用户的数据
const mappingData = getMappingData.call(this, child.data)
child.visible = filterNodeMethod.call(child, value, mappingData, child)
if (filterNodeMethod) {
child.visible = filterNodeMethod.call(child, value, mappingData, child)
}
walkTree(child)
})
@ -81,14 +118,14 @@ export class TreeStore {
return
}
if (node.visible && !node.isLeaf && !lazy) {
node.expand()
node.expand(null, this.autoExpandParent)
}
}
walkTree(this)
}
setData(newVal) {
setData(newVal: any) {
if (newVal !== this.root.data) {
this.root.setData(newVal)
this._initDefaultCheckedNodes()
@ -97,7 +134,7 @@ export class TreeStore {
}
}
getNode(data) {
getNode(data: any): NodeType | null {
if (data instanceof Node) {
return data
}
@ -107,17 +144,21 @@ export class TreeStore {
return this.nodesMap[nodeKey] || null
}
insertBefore(data, insertData) {
insertBefore(data: any, insertData: any) {
const refNode = this.getNode(insertData)
refNode.parent.insertBefore({ data }, refNode)
if (refNode && refNode.parent) {
refNode.parent.insertBefore({ data }, refNode)
}
}
insertAfter(data, insertData) {
insertAfter(data: any, insertData: any) {
const refNode = this.getNode(insertData)
refNode.parent.insertAfter({ data }, refNode)
if (refNode && refNode.parent) {
refNode.parent.insertAfter({ data }, refNode)
}
}
remove(data, isSaveChildNode, isNode) {
remove(data: any, isSaveChildNode?: boolean, isNode?: boolean) {
const treeNode = isNode ? data : this.getNode(data)
if (treeNode && treeNode.parent) {
@ -135,7 +176,7 @@ export class TreeStore {
}
}
append(data, parentData, index) {
append(data: any, parentData?: any, index?: number) {
const parentNode = parentData ? this.getNode(parentData) : this.root
if (parentNode) {
@ -144,7 +185,7 @@ export class TreeStore {
}
}
setDefaultCheckedKey(newValue) {
setDefaultCheckedKey(newValue: any[]) {
if (newValue !== this.defaultCheckedKeys) {
this.defaultCheckedKeys = newValue
this._initDefaultCheckedNodes()
@ -162,7 +203,7 @@ export class TreeStore {
})
}
_initDefaultCheckedNode(node) {
_initDefaultCheckedNode(node: NodeType) {
const defaultCheckedKeys = this.defaultCheckedKeys || []
~defaultCheckedKeys.indexOf(node.key) && node.setChecked(true, !this.checkStrictly)
@ -176,7 +217,7 @@ export class TreeStore {
return this.getHalfCheckedNodes().map((node) => (node || {})[this.key])
}
deregisterNode(node) {
deregisterNode(node: NodeType) {
const key = this.key
if (!key || !node || !node.data) {
return
@ -189,7 +230,7 @@ export class TreeStore {
delete this.nodesMap[node.key]
}
registerNode(node) {
registerNode(node: NodeType) {
const key = this.key
if (!key || !node || !node.data) {
return
@ -201,10 +242,10 @@ export class TreeStore {
}
}
getCheckedNodes(leafOnly = false, includeHalfChecked = false, isNode = false) {
const checkedNodes = []
getCheckedNodes(leafOnly = false, includeHalfChecked = false, isNode = false): any[] {
const checkedNodes: any[] = []
const walkTree = (node) => {
const walkTree = (node: NodeType) => {
const childNodes = node.root ? node.root.childNodes : node.childNodes
childNodes.forEach((child) => {
@ -223,10 +264,10 @@ export class TreeStore {
return checkedNodes
}
getHalfCheckedNodes() {
const nodes = []
getHalfCheckedNodes(): any[] {
const nodes: any[] = []
const walkTree = (node) => {
const walkTree = (node: NodeType) => {
const childNodes = node.root ? node.root.childNodes : node.childNodes
childNodes.forEach((child) => {
@ -243,8 +284,8 @@ export class TreeStore {
return nodes
}
_getAllNodes() {
const allNodes = []
_getAllNodes(): NodeType[] {
const allNodes: NodeType[] = []
const nodesMap = this.nodesMap
Object.keys(nodesMap).forEach((nodeKey) => {
@ -254,7 +295,7 @@ export class TreeStore {
return allNodes
}
updateChildren(key, data) {
updateChildren(key: string, data: any[]) {
const node = this.nodesMap[key]
if (!node) {
return
@ -263,7 +304,7 @@ export class TreeStore {
const childNodes = node.childNodes
for (let i = childNodes.length - 1; i >= 0; i--) {
this.remove(childNodes[i].data)
this.remove(childNodes[i].data, false, false)
}
for (let i = 0, len = data.length; i < len; i++) {
@ -272,7 +313,7 @@ export class TreeStore {
}
}
_setCheckedKeys(key, leafOnly = false, checkedKeys = {}) {
_setCheckedKeys(key: string, leafOnly = false, checkedKeys = {}) {
const nodes = this._getAllNodes().sort((prevNode, nextNode) => nextNode.level - prevNode.level)
const cache = Object.create(null)
const keys = Object.keys(checkedKeys)
@ -303,7 +344,7 @@ export class TreeStore {
} else if (leafOnly) {
node.setChecked(false, false)
const walkTree = (node) => {
const walkTree = (node: NodeType) => {
const childNodes = node.childNodes
childNodes.forEach((child) => {
@ -321,7 +362,7 @@ export class TreeStore {
}
}
setDefaultExpandedKeys(keys) {
setDefaultExpandedKeys(keys: any[]) {
keys = keys || []
this.defaultExpandedKeys = keys
@ -331,7 +372,7 @@ export class TreeStore {
})
}
setCheckedKeys(keys, leafOnly = false) {
setCheckedKeys(keys: any[], leafOnly = false) {
this.defaultCheckedKeys = keys
const checkedKeys = {}
@ -342,7 +383,7 @@ export class TreeStore {
this._setCheckedKeys(this.key, leafOnly, checkedKeys)
}
setCheckedNodes(array, leafOnly = false) {
setCheckedNodes(array: any[], leafOnly = false) {
const key = this.key
const checkedKeys = {}
@ -353,13 +394,13 @@ export class TreeStore {
this._setCheckedKeys(key, leafOnly, checkedKeys)
}
setChecked(data, checked, deep) {
setChecked(data: any, checked: boolean, deep: boolean) {
const node = this.getNode(data)
node && node.setChecked(!!checked, deep)
}
setCurrentNode(currentNode) {
setCurrentNode(currentNode: NodeType) {
const prevNode = this.currentNode
if (prevNode) {
@ -377,7 +418,7 @@ export class TreeStore {
return this.currentNode
}
setCurrentNodeKey(key) {
setCurrentNodeKey(key: any) {
if (isNull(key)) {
this.currentNode && (this.currentNode.isCurrent = false)
this.currentNode = null
@ -390,20 +431,20 @@ export class TreeStore {
node && this.setCurrentNode(node)
}
setUserCurrentNode(node) {
setUserCurrentNode(node: any) {
const key = node[this.key]
const currNode = this.nodesMap[key]
this.setCurrentNode(currNode)
}
getData(data) {
getData(data: any) {
return (this.getNode(data) || {}).data
}
getAllData() {
const children = this.props.children
const walkTree = (nodes) => {
const walkTree = (nodes: NodeType[]) => {
return nodes.map((node) => {
return { ...node.data, [children]: walkTree(node.childNodes) }
})

View File

@ -10,7 +10,9 @@
*
*/
const getTypesObj = (translate) => ({
type TranslateFunction = (value: string) => string
const getTypesObj = (translate: TranslateFunction) => ({
string: translate('validation.types.string'),
method: translate('validation.types.method'),
array: translate('validation.types.array'),
@ -38,7 +40,7 @@ const getTypesObj = (translate) => ({
acceptFile: translate('validation.types.acceptFile'),
fileSize: translate('validation.types.fileSize')
})
export default (translate = (value) => value) => ({
export default (translate: TranslateFunction = (value: string) => value) => ({
default: translate('validation.default'),
required: translate('validation.required'),
enum: translate('validation.enum'),

View File

@ -13,14 +13,26 @@
import { format, complementError, asyncMap, warning, deepMerge, convertFieldsError } from './util'
import { hasOwn, isFunction } from '../type'
function Schema(descriptor, translate?) {
// 添加Schema的接口定义
interface SchemaType {
rules: Record<string, any> | null
_messages: Record<string, any>
define: (descriptor: Record<string, any>) => void
messages: (messages?: Record<string, any>) => Record<string, any>
getSeries: (options: any, source: any, source_: any) => Record<string, any>
mergeMessage: (options: any) => void
validate: (source_: any, o?: any, oc?: Function) => Promise<any>
getValidationMethod: (rule: any) => any
getType: (rule: any) => string
}
function Schema(descriptor: Record<string, any>, translate?: any) {
Schema.getSystemMessage = () => Schema.getDefaultMessage(translate)
Schema.messages = Schema.getSystemMessage(translate)
Schema.systemMessages = Schema.messages
this.rules = null
this._messages = Schema.systemMessages
this.define(descriptor)
;(this as SchemaType).rules = null
;(this as SchemaType)._messages = Schema.systemMessages
;(this as SchemaType).define(descriptor)
}
/**
@ -385,13 +397,13 @@ Schema.register = (type, validator) => {
Schema.validators[type] = validator
}
Schema.validators = {} as any
Schema.validators = {} as Record<string, any>
Schema.warning = warning
Schema.messages = {} as any
Schema.messages = {} as Record<string, any>
Schema.systemMessages = {} as any
Schema.systemMessages = {} as Record<string, any>
Schema.getDefaultMessage = () => undefined